Extend snowflake grant options support (#1794)

This commit is contained in:
Yoav Cohen 2025-04-03 19:52:23 +02:00 committed by GitHub
parent 776b10afe6
commit 7efa686d78
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 126 additions and 19 deletions

View file

@ -6079,10 +6079,10 @@ pub enum Action {
ManageReleases,
ManageVersions,
Modify {
modify_type: ActionModifyType,
modify_type: Option<ActionModifyType>,
},
Monitor {
monitor_type: ActionMonitorType,
monitor_type: Option<ActionMonitorType>,
},
Operate,
OverrideShareRestrictions,
@ -6115,7 +6115,7 @@ impl fmt::Display for Action {
match self {
Action::AddSearchOptimization => f.write_str("ADD SEARCH OPTIMIZATION")?,
Action::Apply { apply_type } => write!(f, "APPLY {apply_type}")?,
Action::ApplyBudget => f.write_str("APPLY BUDGET")?,
Action::ApplyBudget => f.write_str("APPLYBUDGET")?,
Action::AttachListing => f.write_str("ATTACH LISTING")?,
Action::AttachPolicy => f.write_str("ATTACH POLICY")?,
Action::Audit => f.write_str("AUDIT")?,
@ -6143,8 +6143,18 @@ impl fmt::Display for Action {
Action::Manage { manage_type } => write!(f, "MANAGE {manage_type}")?,
Action::ManageReleases => f.write_str("MANAGE RELEASES")?,
Action::ManageVersions => f.write_str("MANAGE VERSIONS")?,
Action::Modify { modify_type } => write!(f, "MODIFY {modify_type}")?,
Action::Monitor { monitor_type } => write!(f, "MONITOR {monitor_type}")?,
Action::Modify { modify_type } => {
write!(f, "MODIFY")?;
if let Some(modify_type) = modify_type {
write!(f, " {modify_type}")?;
}
}
Action::Monitor { monitor_type } => {
write!(f, "MONITOR")?;
if let Some(monitor_type) = monitor_type {
write!(f, " {monitor_type}")?
}
}
Action::Operate => f.write_str("OPERATE")?,
Action::OverrideShareRestrictions => f.write_str("OVERRIDE SHARE RESTRICTIONS")?,
Action::Ownership => f.write_str("OWNERSHIP")?,
@ -6462,6 +6472,20 @@ pub enum GrantObjects {
Warehouses(Vec<ObjectName>),
/// Grant privileges on specific integrations
Integrations(Vec<ObjectName>),
/// Grant privileges on resource monitors
ResourceMonitors(Vec<ObjectName>),
/// Grant privileges on users
Users(Vec<ObjectName>),
/// Grant privileges on compute pools
ComputePools(Vec<ObjectName>),
/// Grant privileges on connections
Connections(Vec<ObjectName>),
/// Grant privileges on failover groups
FailoverGroup(Vec<ObjectName>),
/// Grant privileges on replication group
ReplicationGroup(Vec<ObjectName>),
/// Grant privileges on external volumes
ExternalVolumes(Vec<ObjectName>),
}
impl fmt::Display for GrantObjects {
@ -6502,6 +6526,27 @@ impl fmt::Display for GrantObjects {
display_comma_separated(schemas)
)
}
GrantObjects::ResourceMonitors(objects) => {
write!(f, "RESOURCE MONITOR {}", display_comma_separated(objects))
}
GrantObjects::Users(objects) => {
write!(f, "USER {}", display_comma_separated(objects))
}
GrantObjects::ComputePools(objects) => {
write!(f, "COMPUTE POOL {}", display_comma_separated(objects))
}
GrantObjects::Connections(objects) => {
write!(f, "CONNECTION {}", display_comma_separated(objects))
}
GrantObjects::FailoverGroup(objects) => {
write!(f, "FAILOVER GROUP {}", display_comma_separated(objects))
}
GrantObjects::ReplicationGroup(objects) => {
write!(f, "REPLICATION GROUP {}", display_comma_separated(objects))
}
GrantObjects::ExternalVolumes(objects) => {
write!(f, "EXTERNAL VOLUME {}", display_comma_separated(objects))
}
}
}
}

View file

@ -738,6 +738,7 @@ define_keywords!(
REPLICATION,
RESET,
RESOLVE,
RESOURCE,
RESPECT,
RESTART,
RESTRICT,

View file

@ -12875,6 +12875,26 @@ impl<'a> Parser<'a> {
Some(GrantObjects::AllSequencesInSchema {
schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?,
})
} else if self.parse_keywords(&[Keyword::RESOURCE, Keyword::MONITOR]) {
Some(GrantObjects::ResourceMonitors(self.parse_comma_separated(
|p| p.parse_object_name_with_wildcards(false, true),
)?))
} else if self.parse_keywords(&[Keyword::COMPUTE, Keyword::POOL]) {
Some(GrantObjects::ComputePools(self.parse_comma_separated(
|p| p.parse_object_name_with_wildcards(false, true),
)?))
} else if self.parse_keywords(&[Keyword::FAILOVER, Keyword::GROUP]) {
Some(GrantObjects::FailoverGroup(self.parse_comma_separated(
|p| p.parse_object_name_with_wildcards(false, true),
)?))
} else if self.parse_keywords(&[Keyword::REPLICATION, Keyword::GROUP]) {
Some(GrantObjects::ReplicationGroup(self.parse_comma_separated(
|p| p.parse_object_name_with_wildcards(false, true),
)?))
} else if self.parse_keywords(&[Keyword::EXTERNAL, Keyword::VOLUME]) {
Some(GrantObjects::ExternalVolumes(self.parse_comma_separated(
|p| p.parse_object_name_with_wildcards(false, true),
)?))
} else {
let object_type = self.parse_one_of_keywords(&[
Keyword::SEQUENCE,
@ -12888,6 +12908,8 @@ impl<'a> Parser<'a> {
Keyword::VIEW,
Keyword::WAREHOUSE,
Keyword::INTEGRATION,
Keyword::USER,
Keyword::CONNECTION,
]);
let objects =
self.parse_comma_separated(|p| p.parse_object_name_with_wildcards(false, true));
@ -12898,6 +12920,8 @@ impl<'a> Parser<'a> {
Some(Keyword::WAREHOUSE) => Some(GrantObjects::Warehouses(objects?)),
Some(Keyword::INTEGRATION) => Some(GrantObjects::Integrations(objects?)),
Some(Keyword::VIEW) => Some(GrantObjects::Views(objects?)),
Some(Keyword::USER) => Some(GrantObjects::Users(objects?)),
Some(Keyword::CONNECTION) => Some(GrantObjects::Connections(objects?)),
Some(Keyword::TABLE) | None => Some(GrantObjects::Tables(objects?)),
_ => unreachable!(),
}
@ -12983,10 +13007,10 @@ impl<'a> Parser<'a> {
let manage_type = self.parse_action_manage_type()?;
Ok(Action::Manage { manage_type })
} else if self.parse_keyword(Keyword::MODIFY) {
let modify_type = self.parse_action_modify_type()?;
let modify_type = self.parse_action_modify_type();
Ok(Action::Modify { modify_type })
} else if self.parse_keyword(Keyword::MONITOR) {
let monitor_type = self.parse_action_monitor_type()?;
let monitor_type = self.parse_action_monitor_type();
Ok(Action::Monitor { monitor_type })
} else if self.parse_keyword(Keyword::OPERATE) {
Ok(Action::Operate)
@ -13127,29 +13151,29 @@ impl<'a> Parser<'a> {
}
}
fn parse_action_modify_type(&mut self) -> Result<ActionModifyType, ParserError> {
fn parse_action_modify_type(&mut self) -> Option<ActionModifyType> {
if self.parse_keywords(&[Keyword::LOG, Keyword::LEVEL]) {
Ok(ActionModifyType::LogLevel)
Some(ActionModifyType::LogLevel)
} else if self.parse_keywords(&[Keyword::TRACE, Keyword::LEVEL]) {
Ok(ActionModifyType::TraceLevel)
Some(ActionModifyType::TraceLevel)
} else if self.parse_keywords(&[Keyword::SESSION, Keyword::LOG, Keyword::LEVEL]) {
Ok(ActionModifyType::SessionLogLevel)
Some(ActionModifyType::SessionLogLevel)
} else if self.parse_keywords(&[Keyword::SESSION, Keyword::TRACE, Keyword::LEVEL]) {
Ok(ActionModifyType::SessionTraceLevel)
Some(ActionModifyType::SessionTraceLevel)
} else {
self.expected("GRANT MODIFY type", self.peek_token())
None
}
}
fn parse_action_monitor_type(&mut self) -> Result<ActionMonitorType, ParserError> {
fn parse_action_monitor_type(&mut self) -> Option<ActionMonitorType> {
if self.parse_keyword(Keyword::EXECUTION) {
Ok(ActionMonitorType::Execution)
Some(ActionMonitorType::Execution)
} else if self.parse_keyword(Keyword::SECURITY) {
Ok(ActionMonitorType::Security)
Some(ActionMonitorType::Security)
} else if self.parse_keyword(Keyword::USAGE) {
Ok(ActionMonitorType::Usage)
Some(ActionMonitorType::Usage)
} else {
self.expected("GRANT MONITOR type", self.peek_token())
None
}
}

View file

@ -3357,7 +3357,7 @@ fn test_timetravel_at_before() {
}
#[test]
fn test_grant_account_privileges() {
fn test_grant_account_global_privileges() {
let privileges = vec![
"ALL",
"ALL PRIVILEGES",
@ -3462,6 +3462,43 @@ fn test_grant_account_privileges() {
}
}
#[test]
fn test_grant_account_object_privileges() {
let privileges = vec![
"ALL",
"ALL PRIVILEGES",
"APPLYBUDGET",
"MODIFY",
"MONITOR",
"USAGE",
"OPERATE",
];
let objects_types = vec![
"USER",
"RESOURCE MONITOR",
"WAREHOUSE",
"COMPUTE POOL",
"DATABASE",
"INTEGRATION",
"CONNECTION",
"FAILOVER GROUP",
"REPLICATION GROUP",
"EXTERNAL VOLUME",
];
let with_grant_options = vec!["", " WITH GRANT OPTION"];
for t in &objects_types {
for p in &privileges {
for wgo in &with_grant_options {
let sql = format!("GRANT {p} ON {t} obj1 TO ROLE role1{wgo}");
snowflake_and_generic().verified_stmt(&sql);
}
}
}
}
#[test]
fn test_grant_role_to() {
snowflake_and_generic().verified_stmt("GRANT ROLE r1 TO ROLE r2");