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]