This is an automated email from the ASF dual-hosted git repository.

github-bot 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 b6003eb9 Fixed COPY GRANTS clause parsing for snowflake (#2267)
b6003eb9 is described below

commit b6003eb98e721943c711672571d7cf252c42ae2b
Author: Andriy Romanov <[email protected]>
AuthorDate: Mon Mar 9 02:27:03 2026 -0700

    Fixed COPY GRANTS clause parsing for snowflake (#2267)
---
 src/ast/ddl.rs               |  6 ++++++
 src/parser/mod.rs            |  2 ++
 tests/sqlparser_common.rs    |  6 ++++++
 tests/sqlparser_snowflake.rs | 11 +++++++++++
 4 files changed, 25 insertions(+)

diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs
index 6bea28cb..d6da8368 100644
--- a/src/ast/ddl.rs
+++ b/src/ast/ddl.rs
@@ -4293,6 +4293,9 @@ pub struct CreateView {
     pub if_not_exists: bool,
     /// if true, has SQLite `TEMP` or `TEMPORARY` clause 
<https://www.sqlite.org/lang_createview.html>
     pub temporary: bool,
+    /// Snowflake: `COPY GRANTS` clause
+    /// <https://docs.snowflake.com/en/sql-reference/sql/create-view>
+    pub copy_grants: bool,
     /// if not None, has Clickhouse `TO` clause, specify the table into which 
to insert results
     /// 
<https://clickhouse.com/docs/en/sql-reference/statements/create/view#materialized-view>
     pub to: Option<ObjectName>,
@@ -4336,6 +4339,9 @@ impl fmt::Display for CreateView {
                 .map(|to| format!(" TO {to}"))
                 .unwrap_or_default()
         )?;
+        if self.copy_grants {
+            write!(f, " COPY GRANTS")?;
+        }
         if !self.columns.is_empty() {
             write!(f, " ({})", display_comma_separated(&self.columns))?;
         }
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index eaaa95ec..f9432f86 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -6375,6 +6375,7 @@ impl<'a> Parser<'a> {
         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;
+        let copy_grants = self.parse_keywords(&[Keyword::COPY, 
Keyword::GRANTS]);
         // 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 columns = self.parse_view_columns()?;
@@ -6442,6 +6443,7 @@ impl<'a> Parser<'a> {
             with_no_schema_binding,
             if_not_exists,
             temporary,
+            copy_grants,
             to,
             params: create_view_params,
             name_before_not_exists,
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index a59e3b96..08fb6107 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -8317,6 +8317,7 @@ fn parse_create_view() {
             params,
             name_before_not_exists: _,
             secure: _,
+            copy_grants: _,
         }) => {
             assert_eq!(or_alter, false);
             assert_eq!("myschema.myview", name.to_string());
@@ -8435,6 +8436,7 @@ fn parse_create_view_temporary() {
             params,
             name_before_not_exists: _,
             secure: _,
+            copy_grants: _,
         }) => {
             assert_eq!(or_alter, false);
             assert_eq!("myschema.myview", name.to_string());
@@ -8476,6 +8478,7 @@ fn parse_create_or_replace_view() {
             params,
             name_before_not_exists: _,
             secure: _,
+            copy_grants: _,
         }) => {
             assert_eq!(or_alter, false);
             assert_eq!("v", name.to_string());
@@ -8521,6 +8524,7 @@ fn parse_create_or_replace_materialized_view() {
             params,
             name_before_not_exists: _,
             secure: _,
+            copy_grants: _,
         }) => {
             assert_eq!(or_alter, false);
             assert_eq!("v", name.to_string());
@@ -8562,6 +8566,7 @@ fn parse_create_materialized_view() {
             params,
             name_before_not_exists: _,
             secure: _,
+            copy_grants: _,
         }) => {
             assert_eq!(or_alter, false);
             assert_eq!("myschema.myview", name.to_string());
@@ -8603,6 +8608,7 @@ fn parse_create_materialized_view_with_cluster_by() {
             params,
             name_before_not_exists: _,
             secure: _,
+            copy_grants: _,
         }) => {
             assert_eq!(or_alter, false);
             assert_eq!("myschema.myview", name.to_string());
diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs
index 022c644a..265f8a9a 100644
--- a/tests/sqlparser_snowflake.rs
+++ b/tests/sqlparser_snowflake.rs
@@ -4671,6 +4671,17 @@ fn 
test_snowflake_create_view_with_composite_policy_name() {
     snowflake().verified_stmt(create_view_with_tag);
 }
 
+#[test]
+fn test_snowflake_create_view_copy_grants() {
+    snowflake().verified_stmt("CREATE OR REPLACE VIEW bla COPY GRANTS AS 
(SELECT * FROM source)");
+    snowflake()
+        .verified_stmt("CREATE OR REPLACE SECURE VIEW bla COPY GRANTS AS 
(SELECT * FROM source)");
+    // COPY GRANTS with column list
+    snowflake().verified_stmt(
+        "CREATE OR REPLACE VIEW bla COPY GRANTS (a, b) AS (SELECT a, b FROM 
source)",
+    );
+}
+
 #[test]
 fn test_snowflake_identifier_function() {
     // Using IDENTIFIER to reference a column


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to