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 ec948eaf Add parsing for GRANT ROLE and GRANT DATABASE ROLE in 
Snowflake dialect (#1689)
ec948eaf is described below

commit ec948eaf6e9099451d69c525bb6987ebc721bec7
Author: Yoav Cohen <[email protected]>
AuthorDate: Mon Feb 3 20:17:47 2025 +0100

    Add parsing for GRANT ROLE and GRANT DATABASE ROLE in Snowflake dialect 
(#1689)
---
 src/ast/mod.rs               | 20 ++++++++--
 src/parser/mod.rs            | 89 ++++++++++++++++++++++++--------------------
 tests/sqlparser_common.rs    | 17 +++++----
 tests/sqlparser_mysql.rs     | 10 ++++-
 tests/sqlparser_snowflake.rs | 12 ++++++
 5 files changed, 94 insertions(+), 54 deletions(-)

diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index c0d3ea57..17b5276a 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -3230,7 +3230,7 @@ pub enum Statement {
     /// ```
     Grant {
         privileges: Privileges,
-        objects: GrantObjects,
+        objects: Option<GrantObjects>,
         grantees: Vec<Grantee>,
         with_grant_option: bool,
         granted_by: Option<Ident>,
@@ -3240,7 +3240,7 @@ pub enum Statement {
     /// ```
     Revoke {
         privileges: Privileges,
-        objects: GrantObjects,
+        objects: Option<GrantObjects>,
         grantees: Vec<Grantee>,
         granted_by: Option<Ident>,
         cascade: Option<CascadeOption>,
@@ -4785,7 +4785,9 @@ impl fmt::Display for Statement {
                 granted_by,
             } => {
                 write!(f, "GRANT {privileges} ")?;
-                write!(f, "ON {objects} ")?;
+                if let Some(objects) = objects {
+                    write!(f, "ON {objects} ")?;
+                }
                 write!(f, "TO {}", display_comma_separated(grantees))?;
                 if *with_grant_option {
                     write!(f, " WITH GRANT OPTION")?;
@@ -4803,7 +4805,9 @@ impl fmt::Display for Statement {
                 cascade,
             } => {
                 write!(f, "REVOKE {privileges} ")?;
-                write!(f, "ON {objects} ")?;
+                if let Some(objects) = objects {
+                    write!(f, "ON {objects} ")?;
+                }
                 write!(f, "FROM {}", display_comma_separated(grantees))?;
                 if let Some(grantor) = granted_by {
                     write!(f, " GRANTED BY {grantor}")?;
@@ -5503,6 +5507,9 @@ pub enum Action {
     Create {
         obj_type: Option<ActionCreateObjectType>,
     },
+    DatabaseRole {
+        role: ObjectName,
+    },
     Delete,
     EvolveSchema,
     Execute {
@@ -5536,6 +5543,9 @@ pub enum Action {
     },
     Replicate,
     ResolveAll,
+    Role {
+        role: Ident,
+    },
     Select {
         columns: Option<Vec<Ident>>,
     },
@@ -5565,6 +5575,7 @@ impl fmt::Display for Action {
                     write!(f, " {obj_type}")?
                 }
             }
+            Action::DatabaseRole { role } => write!(f, "DATABASE ROLE 
{role}")?,
             Action::Delete => f.write_str("DELETE")?,
             Action::EvolveSchema => f.write_str("EVOLVE SCHEMA")?,
             Action::Execute { obj_type } => {
@@ -5591,6 +5602,7 @@ impl fmt::Display for Action {
             Action::References { .. } => f.write_str("REFERENCES")?,
             Action::Replicate => f.write_str("REPLICATE")?,
             Action::ResolveAll => f.write_str("RESOLVE ALL")?,
+            Action::Role { role } => write!(f, "ROLE {role}")?,
             Action::Select { .. } => f.write_str("SELECT")?,
             Action::Temporary => f.write_str("TEMPORARY")?,
             Action::Trigger => f.write_str("TRIGGER")?,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 931d9700..28cba039 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -12130,7 +12130,7 @@ impl<'a> Parser<'a> {
 
     pub fn parse_grant_revoke_privileges_objects(
         &mut self,
-    ) -> Result<(Privileges, GrantObjects), ParserError> {
+    ) -> Result<(Privileges, Option<GrantObjects>), ParserError> {
         let privileges = if self.parse_keyword(Keyword::ALL) {
             Privileges::All {
                 with_privileges_keyword: 
self.parse_keyword(Keyword::PRIVILEGES),
@@ -12140,48 +12140,49 @@ impl<'a> Parser<'a> {
             Privileges::Actions(actions)
         };
 
-        self.expect_keyword_is(Keyword::ON)?;
-
-        let objects = if self.parse_keywords(&[
-            Keyword::ALL,
-            Keyword::TABLES,
-            Keyword::IN,
-            Keyword::SCHEMA,
-        ]) {
-            GrantObjects::AllTablesInSchema {
-                schemas: self.parse_comma_separated(|p| 
p.parse_object_name(false))?,
-            }
-        } else if self.parse_keywords(&[
-            Keyword::ALL,
-            Keyword::SEQUENCES,
-            Keyword::IN,
-            Keyword::SCHEMA,
-        ]) {
-            GrantObjects::AllSequencesInSchema {
-                schemas: self.parse_comma_separated(|p| 
p.parse_object_name(false))?,
-            }
-        } else {
-            let object_type = self.parse_one_of_keywords(&[
-                Keyword::SEQUENCE,
-                Keyword::DATABASE,
+        let objects = if self.parse_keyword(Keyword::ON) {
+            if self.parse_keywords(&[Keyword::ALL, Keyword::TABLES, 
Keyword::IN, Keyword::SCHEMA]) {
+                Some(GrantObjects::AllTablesInSchema {
+                    schemas: self.parse_comma_separated(|p| 
p.parse_object_name(false))?,
+                })
+            } else if self.parse_keywords(&[
+                Keyword::ALL,
+                Keyword::SEQUENCES,
+                Keyword::IN,
                 Keyword::SCHEMA,
-                Keyword::TABLE,
-                Keyword::VIEW,
-                Keyword::WAREHOUSE,
-                Keyword::INTEGRATION,
-            ]);
-            let objects =
-                self.parse_comma_separated(|p| 
p.parse_object_name_with_wildcards(false, true));
-            match object_type {
-                Some(Keyword::DATABASE) => GrantObjects::Databases(objects?),
-                Some(Keyword::SCHEMA) => GrantObjects::Schemas(objects?),
-                Some(Keyword::SEQUENCE) => GrantObjects::Sequences(objects?),
-                Some(Keyword::WAREHOUSE) => GrantObjects::Warehouses(objects?),
-                Some(Keyword::INTEGRATION) => 
GrantObjects::Integrations(objects?),
-                Some(Keyword::VIEW) => GrantObjects::Views(objects?),
-                Some(Keyword::TABLE) | None => GrantObjects::Tables(objects?),
-                _ => unreachable!(),
+            ]) {
+                Some(GrantObjects::AllSequencesInSchema {
+                    schemas: self.parse_comma_separated(|p| 
p.parse_object_name(false))?,
+                })
+            } else {
+                let object_type = self.parse_one_of_keywords(&[
+                    Keyword::SEQUENCE,
+                    Keyword::DATABASE,
+                    Keyword::DATABASE,
+                    Keyword::SCHEMA,
+                    Keyword::TABLE,
+                    Keyword::VIEW,
+                    Keyword::WAREHOUSE,
+                    Keyword::INTEGRATION,
+                    Keyword::VIEW,
+                    Keyword::WAREHOUSE,
+                    Keyword::INTEGRATION,
+                ]);
+                let objects =
+                    self.parse_comma_separated(|p| 
p.parse_object_name_with_wildcards(false, true));
+                match object_type {
+                    Some(Keyword::DATABASE) => 
Some(GrantObjects::Databases(objects?)),
+                    Some(Keyword::SCHEMA) => 
Some(GrantObjects::Schemas(objects?)),
+                    Some(Keyword::SEQUENCE) => 
Some(GrantObjects::Sequences(objects?)),
+                    Some(Keyword::WAREHOUSE) => 
Some(GrantObjects::Warehouses(objects?)),
+                    Some(Keyword::INTEGRATION) => 
Some(GrantObjects::Integrations(objects?)),
+                    Some(Keyword::VIEW) => Some(GrantObjects::Views(objects?)),
+                    Some(Keyword::TABLE) | None => 
Some(GrantObjects::Tables(objects?)),
+                    _ => unreachable!(),
+                }
             }
+        } else {
+            None
         };
 
         Ok((privileges, objects))
@@ -12208,6 +12209,9 @@ impl<'a> Parser<'a> {
             Ok(Action::AttachPolicy)
         } else if self.parse_keywords(&[Keyword::BIND, Keyword::SERVICE, 
Keyword::ENDPOINT]) {
             Ok(Action::BindServiceEndpoint)
+        } else if self.parse_keywords(&[Keyword::DATABASE, Keyword::ROLE]) {
+            let role = self.parse_object_name(false)?;
+            Ok(Action::DatabaseRole { role })
         } else if self.parse_keywords(&[Keyword::EVOLVE, Keyword::SCHEMA]) {
             Ok(Action::EvolveSchema)
         } else if self.parse_keywords(&[Keyword::IMPORT, Keyword::SHARE]) {
@@ -12273,6 +12277,9 @@ impl<'a> Parser<'a> {
             Ok(Action::Read)
         } else if self.parse_keyword(Keyword::REPLICATE) {
             Ok(Action::Replicate)
+        } else if self.parse_keyword(Keyword::ROLE) {
+            let role = self.parse_identifier()?;
+            Ok(Action::Role { role })
         } else if self.parse_keyword(Keyword::SELECT) {
             Ok(Action::Select {
                 columns: parse_columns(self)?,
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 643ac357..4fcb37b6 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -8637,7 +8637,7 @@ fn parse_grant() {
             granted_by,
             ..
         } => match (privileges, objects) {
-            (Privileges::Actions(actions), GrantObjects::Tables(objects)) => {
+            (Privileges::Actions(actions), 
Some(GrantObjects::Tables(objects))) => {
                 assert_eq!(
                     vec![
                         Action::Select { columns: None },
@@ -8687,7 +8687,7 @@ fn parse_grant() {
             with_grant_option,
             ..
         } => match (privileges, objects) {
-            (Privileges::Actions(actions), GrantObjects::AllTablesInSchema { 
schemas }) => {
+            (Privileges::Actions(actions), 
Some(GrantObjects::AllTablesInSchema { schemas })) => {
                 assert_eq!(vec![Action::Insert { columns: None }], actions);
                 assert_eq_vec(&["public"], &schemas);
                 assert_eq_vec(&["browser"], &grantees);
@@ -8707,7 +8707,7 @@ fn parse_grant() {
             granted_by,
             ..
         } => match (privileges, objects, granted_by) {
-            (Privileges::Actions(actions), GrantObjects::Sequences(objects), 
None) => {
+            (Privileges::Actions(actions), 
Some(GrantObjects::Sequences(objects)), None) => {
                 assert_eq!(
                     vec![Action::Usage, Action::Select { columns: None }],
                     actions
@@ -8744,7 +8744,7 @@ fn parse_grant() {
                 Privileges::All {
                     with_privileges_keyword,
                 },
-                GrantObjects::Schemas(schemas),
+                Some(GrantObjects::Schemas(schemas)),
             ) => {
                 assert!(!with_privileges_keyword);
                 assert_eq_vec(&["aa", "b"], &schemas);
@@ -8761,7 +8761,10 @@ fn parse_grant() {
             objects,
             ..
         } => match (privileges, objects) {
-            (Privileges::Actions(actions), GrantObjects::AllSequencesInSchema 
{ schemas }) => {
+            (
+                Privileges::Actions(actions),
+                Some(GrantObjects::AllSequencesInSchema { schemas }),
+            ) => {
                 assert_eq!(vec![Action::Usage], actions);
                 assert_eq_vec(&["bus"], &schemas);
             }
@@ -8791,7 +8794,7 @@ fn test_revoke() {
     match verified_stmt(sql) {
         Statement::Revoke {
             privileges,
-            objects: GrantObjects::Tables(tables),
+            objects: Some(GrantObjects::Tables(tables)),
             grantees,
             granted_by,
             cascade,
@@ -8817,7 +8820,7 @@ fn test_revoke_with_cascade() {
     match all_dialects_except(|d| d.is::<MySqlDialect>()).verified_stmt(sql) {
         Statement::Revoke {
             privileges,
-            objects: GrantObjects::Tables(tables),
+            objects: Some(GrantObjects::Tables(tables)),
             grantees,
             granted_by,
             cascade,
diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs
index 2e6dfc72..9f00a921 100644
--- a/tests/sqlparser_mysql.rs
+++ b/tests/sqlparser_mysql.rs
@@ -3046,7 +3046,10 @@ fn parse_grant() {
         );
         assert_eq!(
             objects,
-            GrantObjects::Tables(vec![ObjectName::from(vec!["*".into(), 
"*".into()])])
+            Some(GrantObjects::Tables(vec![ObjectName::from(vec![
+                "*".into(),
+                "*".into()
+            ])]))
         );
         assert!(!with_grant_option);
         assert!(granted_by.is_none());
@@ -3087,7 +3090,10 @@ fn parse_revoke() {
         );
         assert_eq!(
             objects,
-            GrantObjects::Tables(vec![ObjectName::from(vec!["db1".into(), 
"*".into()])])
+            Some(GrantObjects::Tables(vec![ObjectName::from(vec![
+                "db1".into(),
+                "*".into()
+            ])]))
         );
         if let [Grantee {
             grantee_type: GranteesType::None,
diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs
index 1036ce1f..c68ada3c 100644
--- a/tests/sqlparser_snowflake.rs
+++ b/tests/sqlparser_snowflake.rs
@@ -3263,3 +3263,15 @@ fn test_grant_account_privileges() {
         }
     }
 }
+
+#[test]
+fn test_grant_role_to() {
+    snowflake_and_generic().verified_stmt("GRANT ROLE r1 TO ROLE r2");
+    snowflake_and_generic().verified_stmt("GRANT ROLE r1 TO USER u1");
+}
+
+#[test]
+fn test_grant_database_role_to() {
+    snowflake_and_generic().verified_stmt("GRANT DATABASE ROLE r1 TO ROLE r2");
+    snowflake_and_generic().verified_stmt("GRANT DATABASE ROLE db1.sc1.r1 TO 
ROLE db1.sc1.r2");
+}


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

Reply via email to