This is an automated email from the ASF dual-hosted git repository. iffyio pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
The following commit(s) were added to refs/heads/main by this push: new 91273703 Snowflake: Support `CREATE VIEW myview IF NOT EXISTS` (#1961) 91273703 is described below commit 91273703d44802fe5ca92e850357687777cd50c0 Author: etgarperets <etgar.per...@satoricyber.com> AuthorDate: Wed Jul 30 13:24:16 2025 +0300 Snowflake: Support `CREATE VIEW myview IF NOT EXISTS` (#1961) --- src/ast/mod.rs | 25 ++++++++++++++++++++++--- src/ast/spans.rs | 1 + src/parser/mod.rs | 12 +++++++++--- tests/sqlparser_common.rs | 24 ++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 751da66b..805e5f42 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -3255,6 +3255,17 @@ pub enum Statement { materialized: bool, /// View name name: ObjectName, + /// If `if_not_exists` is true, this flag is set to true if the view name comes before the `IF NOT EXISTS` clause. + /// Example: + /// ```sql + /// CREATE VIEW myview IF NOT EXISTS AS SELECT 1` + /// ``` + /// Otherwise, the flag is set to false if the view name comes after the clause + /// Example: + /// ```sql + /// CREATE VIEW IF NOT EXISTS myview AS SELECT 1` + /// ``` + name_before_not_exists: bool, columns: Vec<ViewColumnDef>, query: Box<Query>, options: CreateTableOptions, @@ -4994,6 +5005,7 @@ impl fmt::Display for Statement { temporary, to, params, + name_before_not_exists, } => { write!( f, @@ -5006,11 +5018,18 @@ impl fmt::Display for Statement { } write!( f, - "{materialized}{temporary}VIEW {if_not_exists}{name}{to}", + "{materialized}{temporary}VIEW {if_not_and_name}{to}", + if_not_and_name = if *if_not_exists { + if *name_before_not_exists { + format!("{name} IF NOT EXISTS") + } else { + format!("IF NOT EXISTS {name}") + } + } else { + format!("{name}") + }, materialized = if *materialized { "MATERIALIZED " } else { "" }, - name = name, temporary = if *temporary { "TEMPORARY " } else { "" }, - if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, to = to .as_ref() .map(|to| format!(" TO {to}")) diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 8e7011f5..00313c0e 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -400,6 +400,7 @@ impl Spanned for Statement { if_not_exists: _, temporary: _, to, + name_before_not_exists: _, params: _, } => union_spans( core::iter::once(name.span()) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 0ab0ccf4..4d2aa202 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -5818,12 +5818,17 @@ impl<'a> Parser<'a> { ) -> Result<Statement, ParserError> { let materialized = self.parse_keyword(Keyword::MATERIALIZED); self.expect_keyword_is(Keyword::VIEW)?; - let if_not_exists = dialect_of!(self is BigQueryDialect|SQLiteDialect|GenericDialect) + let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect); + // Tries to parse IF NOT EXISTS either before name or after name + // Name before IF NOT EXISTS is supported by snowflake but undocumented + let if_not_exists_first = + self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); + let name = self.parse_object_name(allow_unquoted_hyphen)?; + let name_before_not_exists = !if_not_exists_first && self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); + let if_not_exists = if_not_exists_first || name_before_not_exists; // Many dialects support `OR ALTER` right after `CREATE`, but we don't (yet). // ANSI SQL and Postgres support RECURSIVE here, but we don't support it either. - let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect); - let name = self.parse_object_name(allow_unquoted_hyphen)?; let columns = self.parse_view_columns()?; let mut options = CreateTableOptions::None; let with_options = self.parse_options(Keyword::WITH)?; @@ -5890,6 +5895,7 @@ impl<'a> Parser<'a> { temporary, to, params: create_view_params, + name_before_not_exists, }) } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 7dc6fa81..ec7eec7d 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -8058,6 +8058,7 @@ fn parse_create_view() { temporary, to, params, + name_before_not_exists: _, } => { assert_eq!(or_alter, false); assert_eq!("myschema.myview", name.to_string()); @@ -8126,6 +8127,7 @@ fn parse_create_view_with_columns() { temporary, to, params, + name_before_not_exists: _, } => { assert_eq!(or_alter, false); assert_eq!("v", name.to_string()); @@ -8175,6 +8177,7 @@ fn parse_create_view_temporary() { temporary, to, params, + name_before_not_exists: _, } => { assert_eq!(or_alter, false); assert_eq!("myschema.myview", name.to_string()); @@ -8214,6 +8217,7 @@ fn parse_create_or_replace_view() { temporary, to, params, + name_before_not_exists: _, } => { assert_eq!(or_alter, false); assert_eq!("v", name.to_string()); @@ -8257,6 +8261,7 @@ fn parse_create_or_replace_materialized_view() { temporary, to, params, + name_before_not_exists: _, } => { assert_eq!(or_alter, false); assert_eq!("v", name.to_string()); @@ -8296,6 +8301,7 @@ fn parse_create_materialized_view() { temporary, to, params, + name_before_not_exists: _, } => { assert_eq!(or_alter, false); assert_eq!("myschema.myview", name.to_string()); @@ -8335,6 +8341,7 @@ fn parse_create_materialized_view_with_cluster_by() { temporary, to, params, + name_before_not_exists: _, } => { assert_eq!(or_alter, false); assert_eq!("myschema.myview", name.to_string()); @@ -16427,3 +16434,20 @@ fn parse_drop_stream() { } verified_stmt("DROP STREAM IF EXISTS s1"); } + +#[test] +fn parse_create_view_if_not_exists() { + // Name after IF NOT EXISTS + let sql: &'static str = "CREATE VIEW IF NOT EXISTS v AS SELECT 1"; + let _ = all_dialects().verified_stmt(sql); + // Name before IF NOT EXISTS + let sql = "CREATE VIEW v IF NOT EXISTS AS SELECT 1"; + let _ = all_dialects().verified_stmt(sql); + // Name missing from query + let sql = "CREATE VIEW IF NOT EXISTS AS SELECT 1"; + let res = all_dialects().parse_sql_statements(sql); + assert_eq!( + ParserError::ParserError("Expected: AS, found: SELECT".to_string()), + res.unwrap_err() + ); +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@datafusion.apache.org For additional commands, e-mail: commits-h...@datafusion.apache.org