diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 0810a41f2..159249c17 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1149,6 +1149,7 @@ pub enum Statement { columns: Vec, query: Box, with_options: Vec, + cluster_by: Vec, }, /// CREATE TABLE CreateTable { @@ -1887,6 +1888,7 @@ impl fmt::Display for Statement { query, materialized, with_options, + cluster_by, } => { write!( f, @@ -1901,6 +1903,9 @@ impl fmt::Display for Statement { if !columns.is_empty() { write!(f, " ({})", display_comma_separated(columns))?; } + if !cluster_by.is_empty() { + write!(f, " CLUSTER BY ({})", display_comma_separated(cluster_by))?; + } write!(f, " AS {}", query) } Statement::CreateTable { diff --git a/src/parser.rs b/src/parser.rs index ff822b6a3..cb78cf5a3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2339,6 +2339,14 @@ impl<'a> Parser<'a> { let name = self.parse_object_name()?; let columns = self.parse_parenthesized_column_list(Optional)?; let with_options = self.parse_options(Keyword::WITH)?; + + let cluster_by = if self.parse_keyword(Keyword::CLUSTER) { + self.expect_keyword(Keyword::BY)?; + self.parse_parenthesized_column_list(Optional)? + } else { + vec![] + }; + self.expect_keyword(Keyword::AS)?; let query = Box::new(self.parse_query()?); // Optional `WITH [ CASCADED | LOCAL ] CHECK OPTION` is widely supported here. @@ -2349,6 +2357,7 @@ impl<'a> Parser<'a> { materialized, or_replace, with_options, + cluster_by, }) } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 1bf261041..e9b73b93d 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4496,6 +4496,7 @@ fn parse_create_view() { or_replace, materialized, with_options, + cluster_by, } => { assert_eq!("myschema.myview", name.to_string()); assert_eq!(Vec::::new(), columns); @@ -4503,6 +4504,7 @@ fn parse_create_view() { assert!(!materialized); assert!(!or_replace); assert_eq!(with_options, vec![]); + assert_eq!(cluster_by, vec![]); } _ => unreachable!(), } @@ -4542,13 +4544,15 @@ fn parse_create_view_with_columns() { with_options, query, materialized, + cluster_by, } => { assert_eq!("v", name.to_string()); assert_eq!(columns, vec![Ident::new("has"), Ident::new("cols")]); assert_eq!(with_options, vec![]); assert_eq!("SELECT 1, 2", query.to_string()); assert!(!materialized); - assert!(!or_replace) + assert!(!or_replace); + assert_eq!(cluster_by, vec![]); } _ => unreachable!(), } @@ -4565,13 +4569,15 @@ fn parse_create_or_replace_view() { with_options, query, materialized, + cluster_by, } => { assert_eq!("v", name.to_string()); assert_eq!(columns, vec![]); assert_eq!(with_options, vec![]); assert_eq!("SELECT 1", query.to_string()); assert!(!materialized); - assert!(or_replace) + assert!(or_replace); + assert_eq!(cluster_by, vec![]); } _ => unreachable!(), } @@ -4592,13 +4598,15 @@ fn parse_create_or_replace_materialized_view() { with_options, query, materialized, + cluster_by, } => { assert_eq!("v", name.to_string()); assert_eq!(columns, vec![]); assert_eq!(with_options, vec![]); assert_eq!("SELECT 1", query.to_string()); assert!(materialized); - assert!(or_replace) + assert!(or_replace); + assert_eq!(cluster_by, vec![]); } _ => unreachable!(), } @@ -4615,6 +4623,32 @@ fn parse_create_materialized_view() { query, materialized, with_options, + cluster_by, + } => { + assert_eq!("myschema.myview", name.to_string()); + assert_eq!(Vec::::new(), columns); + assert_eq!("SELECT foo FROM bar", query.to_string()); + assert!(materialized); + assert_eq!(with_options, vec![]); + assert!(!or_replace); + assert_eq!(cluster_by, vec![]); + } + _ => unreachable!(), + } +} + +#[test] +fn parse_create_materialized_view_with_cluster_by() { + let sql = "CREATE MATERIALIZED VIEW myschema.myview CLUSTER BY (foo) AS SELECT foo FROM bar"; + match verified_stmt(sql) { + Statement::CreateView { + name, + or_replace, + columns, + query, + materialized, + with_options, + cluster_by, } => { assert_eq!("myschema.myview", name.to_string()); assert_eq!(Vec::::new(), columns); @@ -4622,6 +4656,7 @@ fn parse_create_materialized_view() { assert!(materialized); assert_eq!(with_options, vec![]); assert!(!or_replace); + assert_eq!(cluster_by, vec![Ident::new("foo")]); } _ => unreachable!(), }