This is an automated email from the ASF dual-hosted git repository.
alamb 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 e23877cb Add support for various Snowflake grantees (#1640)
e23877cb is described below
commit e23877cb2d558019621eb772c27a3cb090124d8c
Author: Yoav Cohen <[email protected]>
AuthorDate: Sun Jan 5 19:31:51 2025 +0100
Add support for various Snowflake grantees (#1640)
---
src/ast/mod.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++-
src/keywords.rs | 1 +
src/parser/mod.rs | 58 +++++++++++++++++++++++++++++++++++++++++++-
tests/sqlparser_common.rs | 9 +++++++
4 files changed, 128 insertions(+), 2 deletions(-)
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index 75677435..867ae25b 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -3159,7 +3159,7 @@ pub enum Statement {
Grant {
privileges: Privileges,
objects: GrantObjects,
- grantees: Vec<Ident>,
+ grantees: Vec<Grantee>,
with_grant_option: bool,
granted_by: Option<Ident>,
},
@@ -5366,6 +5366,66 @@ impl fmt::Display for Action {
}
}
+/// The principal that receives the privileges
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct Grantee {
+ pub grantee_type: GranteesType,
+ pub name: Option<ObjectName>,
+}
+
+impl fmt::Display for Grantee {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.grantee_type {
+ GranteesType::Role => {
+ write!(f, "ROLE ")?;
+ }
+ GranteesType::Share => {
+ write!(f, "SHARE ")?;
+ }
+ GranteesType::User => {
+ write!(f, "USER ")?;
+ }
+ GranteesType::Group => {
+ write!(f, "GROUP ")?;
+ }
+ GranteesType::Public => {
+ write!(f, "PUBLIC ")?;
+ }
+ GranteesType::DatabaseRole => {
+ write!(f, "DATABASE ROLE ")?;
+ }
+ GranteesType::Application => {
+ write!(f, "APPLICATION ")?;
+ }
+ GranteesType::ApplicationRole => {
+ write!(f, "APPLICATION ROLE ")?;
+ }
+ GranteesType::None => (),
+ }
+ if let Some(ref name) = self.name {
+ write!(f, "{}", name)?;
+ }
+ Ok(())
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum GranteesType {
+ Role,
+ Share,
+ User,
+ Group,
+ Public,
+ DatabaseRole,
+ Application,
+ ApplicationRole,
+ None,
+}
+
/// Objects on which privileges are granted in a GRANT statement.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
diff --git a/src/keywords.rs b/src/keywords.rs
index 43abc2b0..3fed882c 100644
--- a/src/keywords.rs
+++ b/src/keywords.rs
@@ -615,6 +615,7 @@ define_keywords!(
PROCEDURE,
PROGRAM,
PROJECTION,
+ PUBLIC,
PURGE,
QUALIFY,
QUARTER,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 0a245d8d..170f3439 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -11611,7 +11611,7 @@ impl<'a> Parser<'a> {
let (privileges, objects) =
self.parse_grant_revoke_privileges_objects()?;
self.expect_keyword_is(Keyword::TO)?;
- let grantees = self.parse_comma_separated(|p| p.parse_identifier())?;
+ let grantees = self.parse_grantees()?;
let with_grant_option =
self.parse_keywords(&[Keyword::WITH, Keyword::GRANT,
Keyword::OPTION]);
@@ -11629,6 +11629,62 @@ impl<'a> Parser<'a> {
})
}
+ fn parse_grantees(&mut self) -> Result<Vec<Grantee>, ParserError> {
+ let mut values = vec![];
+ let mut grantee_type = GranteesType::None;
+ loop {
+ grantee_type = if self.parse_keyword(Keyword::ROLE) {
+ GranteesType::Role
+ } else if self.parse_keyword(Keyword::USER) {
+ GranteesType::User
+ } else if self.parse_keyword(Keyword::SHARE) {
+ GranteesType::Share
+ } else if self.parse_keyword(Keyword::GROUP) {
+ GranteesType::Group
+ } else if self.parse_keyword(Keyword::PUBLIC) {
+ GranteesType::Public
+ } else if self.parse_keywords(&[Keyword::DATABASE, Keyword::ROLE])
{
+ GranteesType::DatabaseRole
+ } else if self.parse_keywords(&[Keyword::APPLICATION,
Keyword::ROLE]) {
+ GranteesType::ApplicationRole
+ } else if self.parse_keyword(Keyword::APPLICATION) {
+ GranteesType::Application
+ } else {
+ grantee_type // keep from previous iteraton, if not specified
+ };
+
+ let grantee = if grantee_type == GranteesType::Public {
+ Grantee {
+ grantee_type: grantee_type.clone(),
+ name: None,
+ }
+ } else {
+ let mut name = self.parse_object_name(false)?;
+ if self.consume_token(&Token::Colon) {
+ // Redshift supports namespace prefix for extenrnal users
and groups:
+ // <Namespace>:<GroupName> or <Namespace>:<UserName>
+ //
https://docs.aws.amazon.com/redshift/latest/mgmt/redshift-iam-access-control-native-idp.html
+ let ident = self.parse_identifier()?;
+ if let Some(n) = name.0.first() {
+ name = ObjectName(vec![Ident::new(format!("{}:{}",
n.value, ident.value))]);
+ };
+ }
+ Grantee {
+ grantee_type: grantee_type.clone(),
+ name: Some(name),
+ }
+ };
+
+ values.push(grantee);
+
+ if !self.consume_token(&Token::Comma) {
+ break;
+ }
+ }
+
+ Ok(values)
+ }
+
pub fn parse_grant_revoke_privileges_objects(
&mut self,
) -> Result<(Privileges, GrantObjects), ParserError> {
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 3c2e0899..4b307e8d 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -8497,6 +8497,15 @@ fn parse_grant() {
},
_ => unreachable!(),
}
+
+ verified_stmt("GRANT SELECT ON ALL TABLES IN SCHEMA db1.sc1 TO ROLE
role1");
+ verified_stmt("GRANT SELECT ON ALL TABLES IN SCHEMA db1.sc1 TO ROLE role1
WITH GRANT OPTION");
+ verified_stmt("GRANT SELECT ON ALL TABLES IN SCHEMA db1.sc1 TO DATABASE
ROLE role1");
+ verified_stmt("GRANT SELECT ON ALL TABLES IN SCHEMA db1.sc1 TO APPLICATION
role1");
+ verified_stmt("GRANT SELECT ON ALL TABLES IN SCHEMA db1.sc1 TO APPLICATION
ROLE role1");
+ verified_stmt("GRANT SELECT ON ALL TABLES IN SCHEMA db1.sc1 TO SHARE
share1");
+ verified_stmt("GRANT USAGE ON SCHEMA sc1 TO a:b");
+ verified_stmt("GRANT USAGE ON SCHEMA sc1 TO GROUP group1");
}
#[test]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]