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

Reply via email to