From c811e2260505e2651b04e85e886ec09d237a0602 Mon Sep 17 00:00:00 2001 From: Lukasz Stefaniak Date: Mon, 2 Oct 2023 19:42:01 +0200 Subject: [PATCH] =?UTF-8?q?redshift:=20add=20support=20for=20CREATE=20VIEW?= =?UTF-8?q?=20=E2=80=A6=20WITH=20NO=20SCHEMA=20BINDING=20(#979)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ast/mod.rs | 11 +++++++++-- src/keywords.rs | 1 + src/parser/mod.rs | 10 ++++++++++ tests/sqlparser_common.rs | 12 ++++++++++++ tests/sqlparser_redshift.rs | 14 ++++++++++++++ 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index f2dbb8899..6f9d32c8d 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -577,7 +577,7 @@ pub enum Expr { /// /// Syntax: /// ```sql - /// MARCH (, , ...) AGAINST ( []) + /// MATCH (, , ...) AGAINST ( []) /// /// = CompoundIdentifier /// = String literal @@ -1316,6 +1316,8 @@ pub enum Statement { query: Box, with_options: Vec, cluster_by: Vec, + /// if true, has RedShift [`WITH NO SCHEMA BINDING`] clause + with_no_schema_binding: bool, }, /// CREATE TABLE CreateTable { @@ -2271,6 +2273,7 @@ impl fmt::Display for Statement { materialized, with_options, cluster_by, + with_no_schema_binding, } => { write!( f, @@ -2288,7 +2291,11 @@ impl fmt::Display for Statement { if !cluster_by.is_empty() { write!(f, " CLUSTER BY ({})", display_comma_separated(cluster_by))?; } - write!(f, " AS {query}") + write!(f, " AS {query}")?; + if *with_no_schema_binding { + write!(f, " WITH NO SCHEMA BINDING")?; + } + Ok(()) } Statement::CreateTable { name, diff --git a/src/keywords.rs b/src/keywords.rs index d85708032..e1bbf44ae 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -110,6 +110,7 @@ define_keywords!( BIGINT, BIGNUMERIC, BINARY, + BINDING, BLOB, BLOOMFILTER, BOOL, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 45600f42d..5f6788696 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2974,6 +2974,15 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::AS)?; let query = Box::new(self.parse_query()?); // Optional `WITH [ CASCADED | LOCAL ] CHECK OPTION` is widely supported here. + + let with_no_schema_binding = dialect_of!(self is RedshiftSqlDialect | GenericDialect) + && self.parse_keywords(&[ + Keyword::WITH, + Keyword::NO, + Keyword::SCHEMA, + Keyword::BINDING, + ]); + Ok(Statement::CreateView { name, columns, @@ -2982,6 +2991,7 @@ impl<'a> Parser<'a> { or_replace, with_options, cluster_by, + with_no_schema_binding, }) } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 3c4a2d9ea..027dc312f 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -5320,6 +5320,7 @@ fn parse_create_view() { materialized, with_options, cluster_by, + with_no_schema_binding: late_binding, } => { assert_eq!("myschema.myview", name.to_string()); assert_eq!(Vec::::new(), columns); @@ -5328,6 +5329,7 @@ fn parse_create_view() { assert!(!or_replace); assert_eq!(with_options, vec![]); assert_eq!(cluster_by, vec![]); + assert!(!late_binding); } _ => unreachable!(), } @@ -5368,6 +5370,7 @@ fn parse_create_view_with_columns() { query, materialized, cluster_by, + with_no_schema_binding: late_binding, } => { assert_eq!("v", name.to_string()); assert_eq!(columns, vec![Ident::new("has"), Ident::new("cols")]); @@ -5376,6 +5379,7 @@ fn parse_create_view_with_columns() { assert!(!materialized); assert!(!or_replace); assert_eq!(cluster_by, vec![]); + assert!(!late_binding); } _ => unreachable!(), } @@ -5393,6 +5397,7 @@ fn parse_create_or_replace_view() { query, materialized, cluster_by, + with_no_schema_binding: late_binding, } => { assert_eq!("v", name.to_string()); assert_eq!(columns, vec![]); @@ -5401,6 +5406,7 @@ fn parse_create_or_replace_view() { assert!(!materialized); assert!(or_replace); assert_eq!(cluster_by, vec![]); + assert!(!late_binding); } _ => unreachable!(), } @@ -5422,6 +5428,7 @@ fn parse_create_or_replace_materialized_view() { query, materialized, cluster_by, + with_no_schema_binding: late_binding, } => { assert_eq!("v", name.to_string()); assert_eq!(columns, vec![]); @@ -5430,6 +5437,7 @@ fn parse_create_or_replace_materialized_view() { assert!(materialized); assert!(or_replace); assert_eq!(cluster_by, vec![]); + assert!(!late_binding); } _ => unreachable!(), } @@ -5447,6 +5455,7 @@ fn parse_create_materialized_view() { materialized, with_options, cluster_by, + with_no_schema_binding: late_binding, } => { assert_eq!("myschema.myview", name.to_string()); assert_eq!(Vec::::new(), columns); @@ -5455,6 +5464,7 @@ fn parse_create_materialized_view() { assert_eq!(with_options, vec![]); assert!(!or_replace); assert_eq!(cluster_by, vec![]); + assert!(!late_binding); } _ => unreachable!(), } @@ -5472,6 +5482,7 @@ fn parse_create_materialized_view_with_cluster_by() { materialized, with_options, cluster_by, + with_no_schema_binding: late_binding, } => { assert_eq!("myschema.myview", name.to_string()); assert_eq!(Vec::::new(), columns); @@ -5480,6 +5491,7 @@ fn parse_create_materialized_view_with_cluster_by() { assert_eq!(with_options, vec![]); assert!(!or_replace); assert_eq!(cluster_by, vec![Ident::new("foo")]); + assert!(!late_binding); } _ => unreachable!(), } diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index f17ca5841..5ae539b3c 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -16,6 +16,7 @@ mod test_utils; use test_utils::*; use sqlparser::ast::*; +use sqlparser::dialect::GenericDialect; use sqlparser::dialect::RedshiftSqlDialect; #[test] @@ -272,6 +273,13 @@ fn redshift() -> TestedDialects { } } +fn redshift_and_generic() -> TestedDialects { + TestedDialects { + dialects: vec![Box::new(RedshiftSqlDialect {}), Box::new(GenericDialect {})], + options: None, + } +} + #[test] fn test_sharp() { let sql = "SELECT #_of_values"; @@ -281,3 +289,9 @@ fn test_sharp() { select.projection[0] ); } + +#[test] +fn test_create_view_with_no_schema_binding() { + redshift_and_generic() + .verified_stmt("CREATE VIEW myevent AS SELECT eventname FROM event WITH NO SCHEMA BINDING"); +}