Add support for Snowflake account privileges (#1666)

This commit is contained in:
Yoav Cohen 2025-01-20 20:20:41 +01:00 committed by GitHub
parent e5bc3dfad8
commit 183274e274
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 651 additions and 71 deletions

View file

@ -5453,29 +5453,107 @@ impl fmt::Display for FetchDirection {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum Action { pub enum Action {
AddSearchOptimization,
Apply {
apply_type: ActionApplyType,
},
ApplyBudget,
AttachListing,
AttachPolicy,
Audit,
BindServiceEndpoint,
Connect, Connect,
Create, Create {
obj_type: Option<ActionCreateObjectType>,
},
Delete, Delete,
Execute, EvolveSchema,
Insert { columns: Option<Vec<Ident>> }, Execute {
References { columns: Option<Vec<Ident>> }, obj_type: Option<ActionExecuteObjectType>,
Select { columns: Option<Vec<Ident>> }, },
Failover,
ImportedPrivileges,
ImportShare,
Insert {
columns: Option<Vec<Ident>>,
},
Manage {
manage_type: ActionManageType,
},
ManageReleases,
ManageVersions,
Modify {
modify_type: ActionModifyType,
},
Monitor {
monitor_type: ActionMonitorType,
},
Operate,
OverrideShareRestrictions,
Ownership,
PurchaseDataExchangeListing,
Read,
ReadSession,
References {
columns: Option<Vec<Ident>>,
},
Replicate,
ResolveAll,
Select {
columns: Option<Vec<Ident>>,
},
Temporary, Temporary,
Trigger, Trigger,
Truncate, Truncate,
Update { columns: Option<Vec<Ident>> }, Update {
columns: Option<Vec<Ident>>,
},
Usage, Usage,
} }
impl fmt::Display for Action { impl fmt::Display for Action {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { 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::AttachListing => f.write_str("ATTACH LISTING")?,
Action::AttachPolicy => f.write_str("ATTACH POLICY")?,
Action::Audit => f.write_str("AUDIT")?,
Action::BindServiceEndpoint => f.write_str("BIND SERVICE ENDPOINT")?,
Action::Connect => f.write_str("CONNECT")?, Action::Connect => f.write_str("CONNECT")?,
Action::Create => f.write_str("CREATE")?, Action::Create { obj_type } => {
f.write_str("CREATE")?;
if let Some(obj_type) = obj_type {
write!(f, " {obj_type}")?
}
}
Action::Delete => f.write_str("DELETE")?, Action::Delete => f.write_str("DELETE")?,
Action::Execute => f.write_str("EXECUTE")?, Action::EvolveSchema => f.write_str("EVOLVE SCHEMA")?,
Action::Execute { obj_type } => {
f.write_str("EXECUTE")?;
if let Some(obj_type) = obj_type {
write!(f, " {obj_type}")?
}
}
Action::Failover => f.write_str("FAILOVER")?,
Action::ImportedPrivileges => f.write_str("IMPORTED PRIVILEGES")?,
Action::ImportShare => f.write_str("IMPORT SHARE")?,
Action::Insert { .. } => f.write_str("INSERT")?, Action::Insert { .. } => f.write_str("INSERT")?,
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::Operate => f.write_str("OPERATE")?,
Action::OverrideShareRestrictions => f.write_str("OVERRIDE SHARE RESTRICTIONS")?,
Action::Ownership => f.write_str("OWNERSHIP")?,
Action::PurchaseDataExchangeListing => f.write_str("PURCHASE DATA EXCHANGE LISTING")?,
Action::Read => f.write_str("READ")?,
Action::ReadSession => f.write_str("READ SESSION")?,
Action::References { .. } => f.write_str("REFERENCES")?, Action::References { .. } => f.write_str("REFERENCES")?,
Action::Replicate => f.write_str("REPLICATE")?,
Action::ResolveAll => f.write_str("RESOLVE ALL")?,
Action::Select { .. } => f.write_str("SELECT")?, Action::Select { .. } => f.write_str("SELECT")?,
Action::Temporary => f.write_str("TEMPORARY")?, Action::Temporary => f.write_str("TEMPORARY")?,
Action::Trigger => f.write_str("TRIGGER")?, Action::Trigger => f.write_str("TRIGGER")?,
@ -5498,6 +5576,186 @@ impl fmt::Display for Action {
} }
} }
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `CREATE` privilege.
pub enum ActionCreateObjectType {
Account,
Application,
ApplicationPackage,
ComputePool,
DataExchangeListing,
Database,
ExternalVolume,
FailoverGroup,
Integration,
NetworkPolicy,
OrganiationListing,
ReplicationGroup,
Role,
Share,
User,
Warehouse,
}
impl fmt::Display for ActionCreateObjectType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ActionCreateObjectType::Account => write!(f, "ACCOUNT"),
ActionCreateObjectType::Application => write!(f, "APPLICATION"),
ActionCreateObjectType::ApplicationPackage => write!(f, "APPLICATION PACKAGE"),
ActionCreateObjectType::ComputePool => write!(f, "COMPUTE POOL"),
ActionCreateObjectType::DataExchangeListing => write!(f, "DATA EXCHANGE LISTING"),
ActionCreateObjectType::Database => write!(f, "DATABASE"),
ActionCreateObjectType::ExternalVolume => write!(f, "EXTERNAL VOLUME"),
ActionCreateObjectType::FailoverGroup => write!(f, "FAILOVER GROUP"),
ActionCreateObjectType::Integration => write!(f, "INTEGRATION"),
ActionCreateObjectType::NetworkPolicy => write!(f, "NETWORK POLICY"),
ActionCreateObjectType::OrganiationListing => write!(f, "ORGANIZATION LISTING"),
ActionCreateObjectType::ReplicationGroup => write!(f, "REPLICATION GROUP"),
ActionCreateObjectType::Role => write!(f, "ROLE"),
ActionCreateObjectType::Share => write!(f, "SHARE"),
ActionCreateObjectType::User => write!(f, "USER"),
ActionCreateObjectType::Warehouse => write!(f, "WAREHOUSE"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `APPLY` privilege.
pub enum ActionApplyType {
AggregationPolicy,
AuthenticationPolicy,
JoinPolicy,
MaskingPolicy,
PackagesPolicy,
PasswordPolicy,
ProjectionPolicy,
RowAccessPolicy,
SessionPolicy,
Tag,
}
impl fmt::Display for ActionApplyType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ActionApplyType::AggregationPolicy => write!(f, "AGGREGATION POLICY"),
ActionApplyType::AuthenticationPolicy => write!(f, "AUTHENTICATION POLICY"),
ActionApplyType::JoinPolicy => write!(f, "JOIN POLICY"),
ActionApplyType::MaskingPolicy => write!(f, "MASKING POLICY"),
ActionApplyType::PackagesPolicy => write!(f, "PACKAGES POLICY"),
ActionApplyType::PasswordPolicy => write!(f, "PASSWORD POLICY"),
ActionApplyType::ProjectionPolicy => write!(f, "PROJECTION POLICY"),
ActionApplyType::RowAccessPolicy => write!(f, "ROW ACCESS POLICY"),
ActionApplyType::SessionPolicy => write!(f, "SESSION POLICY"),
ActionApplyType::Tag => write!(f, "TAG"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `EXECUTE` privilege.
pub enum ActionExecuteObjectType {
Alert,
DataMetricFunction,
ManagedAlert,
ManagedTask,
Task,
}
impl fmt::Display for ActionExecuteObjectType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ActionExecuteObjectType::Alert => write!(f, "ALERT"),
ActionExecuteObjectType::DataMetricFunction => write!(f, "DATA METRIC FUNCTION"),
ActionExecuteObjectType::ManagedAlert => write!(f, "MANAGED ALERT"),
ActionExecuteObjectType::ManagedTask => write!(f, "MANAGED TASK"),
ActionExecuteObjectType::Task => write!(f, "TASK"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `MANAGE` privilege.
pub enum ActionManageType {
AccountSupportCases,
EventSharing,
Grants,
ListingAutoFulfillment,
OrganizationSupportCases,
UserSupportCases,
Warehouses,
}
impl fmt::Display for ActionManageType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ActionManageType::AccountSupportCases => write!(f, "ACCOUNT SUPPORT CASES"),
ActionManageType::EventSharing => write!(f, "EVENT SHARING"),
ActionManageType::Grants => write!(f, "GRANTS"),
ActionManageType::ListingAutoFulfillment => write!(f, "LISTING AUTO FULFILLMENT"),
ActionManageType::OrganizationSupportCases => write!(f, "ORGANIZATION SUPPORT CASES"),
ActionManageType::UserSupportCases => write!(f, "USER SUPPORT CASES"),
ActionManageType::Warehouses => write!(f, "WAREHOUSES"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `MODIFY` privilege.
pub enum ActionModifyType {
LogLevel,
TraceLevel,
SessionLogLevel,
SessionTraceLevel,
}
impl fmt::Display for ActionModifyType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ActionModifyType::LogLevel => write!(f, "LOG LEVEL"),
ActionModifyType::TraceLevel => write!(f, "TRACE LEVEL"),
ActionModifyType::SessionLogLevel => write!(f, "SESSION LOG LEVEL"),
ActionModifyType::SessionTraceLevel => write!(f, "SESSION TRACE LEVEL"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `MONITOR` privilege.
pub enum ActionMonitorType {
Execution,
Security,
Usage,
}
impl fmt::Display for ActionMonitorType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ActionMonitorType::Execution => write!(f, "EXECUTION"),
ActionMonitorType::Security => write!(f, "SECURITY"),
ActionMonitorType::Usage => write!(f, "USAGE"),
}
}
}
/// The principal that receives the privileges /// The principal that receives the privileges
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]

View file

@ -84,6 +84,7 @@ define_keywords!(
AFTER, AFTER,
AGAINST, AGAINST,
AGGREGATION, AGGREGATION,
ALERT,
ALGORITHM, ALGORITHM,
ALIAS, ALIAS,
ALL, ALL,
@ -96,6 +97,7 @@ define_keywords!(
ANY, ANY,
APPLICATION, APPLICATION,
APPLY, APPLY,
APPLYBUDGET,
ARCHIVE, ARCHIVE,
ARE, ARE,
ARRAY, ARRAY,
@ -109,6 +111,8 @@ define_keywords!(
AT, AT,
ATOMIC, ATOMIC,
ATTACH, ATTACH,
AUDIT,
AUTHENTICATION,
AUTHORIZATION, AUTHORIZATION,
AUTO, AUTO,
AUTOINCREMENT, AUTOINCREMENT,
@ -127,6 +131,7 @@ define_keywords!(
BIGINT, BIGINT,
BIGNUMERIC, BIGNUMERIC,
BINARY, BINARY,
BIND,
BINDING, BINDING,
BIT, BIT,
BLOB, BLOB,
@ -150,6 +155,7 @@ define_keywords!(
CASCADE, CASCADE,
CASCADED, CASCADED,
CASE, CASE,
CASES,
CAST, CAST,
CATALOG, CATALOG,
CATCH, CATCH,
@ -305,12 +311,15 @@ define_keywords!(
ESTIMATE, ESTIMATE,
EVENT, EVENT,
EVERY, EVERY,
EVOLVE,
EXCEPT, EXCEPT,
EXCEPTION, EXCEPTION,
EXCHANGE,
EXCLUDE, EXCLUDE,
EXCLUSIVE, EXCLUSIVE,
EXEC, EXEC,
EXECUTE, EXECUTE,
EXECUTION,
EXISTS, EXISTS,
EXP, EXP,
EXPANSION, EXPANSION,
@ -322,6 +331,7 @@ define_keywords!(
EXTERNAL, EXTERNAL,
EXTRACT, EXTRACT,
FAIL, FAIL,
FAILOVER,
FALSE, FALSE,
FETCH, FETCH,
FIELDS, FIELDS,
@ -357,6 +367,7 @@ define_keywords!(
FREEZE, FREEZE,
FROM, FROM,
FSCK, FSCK,
FULFILLMENT,
FULL, FULL,
FULLTEXT, FULLTEXT,
FUNCTION, FUNCTION,
@ -394,6 +405,8 @@ define_keywords!(
ILIKE, ILIKE,
IMMEDIATE, IMMEDIATE,
IMMUTABLE, IMMUTABLE,
IMPORT,
IMPORTED,
IN, IN,
INCLUDE, INCLUDE,
INCLUDE_NULL_VALUES, INCLUDE_NULL_VALUES,
@ -421,6 +434,7 @@ define_keywords!(
INT64, INT64,
INT8, INT8,
INTEGER, INTEGER,
INTEGRATION,
INTERPOLATE, INTERPOLATE,
INTERSECT, INTERSECT,
INTERSECTION, INTERSECTION,
@ -460,6 +474,7 @@ define_keywords!(
LINES, LINES,
LIST, LIST,
LISTEN, LISTEN,
LISTING,
LN, LN,
LOAD, LOAD,
LOCAL, LOCAL,
@ -478,6 +493,8 @@ define_keywords!(
LOW_PRIORITY, LOW_PRIORITY,
LS, LS,
MACRO, MACRO,
MANAGE,
MANAGED,
MANAGEDLOCATION, MANAGEDLOCATION,
MAP, MAP,
MASKING, MASKING,
@ -499,6 +516,7 @@ define_keywords!(
MERGE, MERGE,
METADATA, METADATA,
METHOD, METHOD,
METRIC,
MICROSECOND, MICROSECOND,
MICROSECONDS, MICROSECONDS,
MILLENIUM, MILLENIUM,
@ -515,6 +533,7 @@ define_keywords!(
MODIFIES, MODIFIES,
MODIFY, MODIFY,
MODULE, MODULE,
MONITOR,
MONTH, MONTH,
MONTHS, MONTHS,
MSCK, MSCK,
@ -528,6 +547,7 @@ define_keywords!(
NCHAR, NCHAR,
NCLOB, NCLOB,
NESTED, NESTED,
NETWORK,
NEW, NEW,
NEXT, NEXT,
NFC, NFC,
@ -575,7 +595,9 @@ define_keywords!(
ONLY, ONLY,
OPEN, OPEN,
OPENJSON, OPENJSON,
OPERATE,
OPERATOR, OPERATOR,
OPTIMIZATION,
OPTIMIZE, OPTIMIZE,
OPTIMIZER_COSTS, OPTIMIZER_COSTS,
OPTION, OPTION,
@ -584,6 +606,7 @@ define_keywords!(
ORC, ORC,
ORDER, ORDER,
ORDINALITY, ORDINALITY,
ORGANIZATION,
OUT, OUT,
OUTER, OUTER,
OUTPUTFORMAT, OUTPUTFORMAT,
@ -591,9 +614,13 @@ define_keywords!(
OVERFLOW, OVERFLOW,
OVERLAPS, OVERLAPS,
OVERLAY, OVERLAY,
OVERRIDE,
OVERWRITE, OVERWRITE,
OWNED, OWNED,
OWNER, OWNER,
OWNERSHIP,
PACKAGE,
PACKAGES,
PARALLEL, PARALLEL,
PARAMETER, PARAMETER,
PARQUET, PARQUET,
@ -618,6 +645,7 @@ define_keywords!(
PLAN, PLAN,
PLANS, PLANS,
POLICY, POLICY,
POOL,
PORTION, PORTION,
POSITION, POSITION,
POSITION_REGEX, POSITION_REGEX,
@ -637,6 +665,7 @@ define_keywords!(
PROGRAM, PROGRAM,
PROJECTION, PROJECTION,
PUBLIC, PUBLIC,
PURCHASE,
PURGE, PURGE,
QUALIFY, QUALIFY,
QUARTER, QUARTER,
@ -670,6 +699,7 @@ define_keywords!(
RELATIVE, RELATIVE,
RELAY, RELAY,
RELEASE, RELEASE,
RELEASES,
REMOTE, REMOTE,
REMOVE, REMOVE,
RENAME, RENAME,
@ -678,12 +708,15 @@ define_keywords!(
REPEATABLE, REPEATABLE,
REPLACE, REPLACE,
REPLICA, REPLICA,
REPLICATE,
REPLICATION, REPLICATION,
RESET, RESET,
RESOLVE,
RESPECT, RESPECT,
RESTART, RESTART,
RESTRICT, RESTRICT,
RESTRICTED, RESTRICTED,
RESTRICTIONS,
RESTRICTIVE, RESTRICTIVE,
RESULT, RESULT,
RESULTSET, RESULTSET,
@ -732,6 +765,7 @@ define_keywords!(
SERDE, SERDE,
SERDEPROPERTIES, SERDEPROPERTIES,
SERIALIZABLE, SERIALIZABLE,
SERVICE,
SESSION, SESSION,
SESSION_USER, SESSION_USER,
SET, SET,
@ -739,6 +773,7 @@ define_keywords!(
SETS, SETS,
SETTINGS, SETTINGS,
SHARE, SHARE,
SHARING,
SHOW, SHOW,
SIMILAR, SIMILAR,
SKIP, SKIP,
@ -782,6 +817,7 @@ define_keywords!(
SUM, SUM,
SUPER, SUPER,
SUPERUSER, SUPERUSER,
SUPPORT,
SUSPEND, SUSPEND,
SWAP, SWAP,
SYMMETRIC, SYMMETRIC,
@ -794,6 +830,7 @@ define_keywords!(
TABLESAMPLE, TABLESAMPLE,
TAG, TAG,
TARGET, TARGET,
TASK,
TBLPROPERTIES, TBLPROPERTIES,
TEMP, TEMP,
TEMPORARY, TEMPORARY,
@ -819,6 +856,7 @@ define_keywords!(
TO, TO,
TOP, TOP,
TOTALS, TOTALS,
TRACE,
TRAILING, TRAILING,
TRANSACTION, TRANSACTION,
TRANSIENT, TRANSIENT,
@ -885,11 +923,14 @@ define_keywords!(
VERBOSE, VERBOSE,
VERSION, VERSION,
VERSIONING, VERSIONING,
VERSIONS,
VIEW, VIEW,
VIEWS, VIEWS,
VIRTUAL, VIRTUAL,
VOLATILE, VOLATILE,
VOLUME,
WAREHOUSE, WAREHOUSE,
WAREHOUSES,
WEEK, WEEK,
WEEKS, WEEKS,
WHEN, WHEN,

View file

@ -48,9 +48,6 @@ pub enum ParserError {
RecursionLimitExceeded, RecursionLimitExceeded,
} }
// avoid clippy type_complexity warnings
type ParsedAction = (Keyword, Option<Vec<Ident>>);
// Use `Parser::expected` instead, if possible // Use `Parser::expected` instead, if possible
macro_rules! parser_err { macro_rules! parser_err {
($MSG:expr, $loc:expr) => { ($MSG:expr, $loc:expr) => {
@ -3950,7 +3947,7 @@ impl<'a> Parser<'a> {
) )
} }
pub fn parse_actions_list(&mut self) -> Result<Vec<ParsedAction>, ParserError> { pub fn parse_actions_list(&mut self) -> Result<Vec<Action>, ParserError> {
let mut values = vec![]; let mut values = vec![];
loop { loop {
values.push(self.parse_grant_permission()?); values.push(self.parse_grant_permission()?);
@ -11980,37 +11977,8 @@ impl<'a> Parser<'a> {
with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES), with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES),
} }
} else { } else {
let (actions, err): (Vec<_>, Vec<_>) = self let actions = self.parse_actions_list()?;
.parse_actions_list()? Privileges::Actions(actions)
.into_iter()
.map(|(kw, columns)| match kw {
Keyword::DELETE => Ok(Action::Delete),
Keyword::INSERT => Ok(Action::Insert { columns }),
Keyword::REFERENCES => Ok(Action::References { columns }),
Keyword::SELECT => Ok(Action::Select { columns }),
Keyword::TRIGGER => Ok(Action::Trigger),
Keyword::TRUNCATE => Ok(Action::Truncate),
Keyword::UPDATE => Ok(Action::Update { columns }),
Keyword::USAGE => Ok(Action::Usage),
Keyword::CONNECT => Ok(Action::Connect),
Keyword::CREATE => Ok(Action::Create),
Keyword::EXECUTE => Ok(Action::Execute),
Keyword::TEMPORARY => Ok(Action::Temporary),
// This will cover all future added keywords to
// parse_grant_permission and unhandled in this
// match
_ => Err(kw),
})
.partition(Result::is_ok);
if !err.is_empty() {
let errors: Vec<Keyword> = err.into_iter().filter_map(|x| x.err()).collect();
return Err(ParserError::ParserError(format!(
"INTERNAL ERROR: GRANT/REVOKE unexpected keyword(s) - {errors:?}"
)));
}
let act = actions.into_iter().filter_map(|x| x.ok()).collect();
Privileges::Actions(act)
}; };
self.expect_keyword_is(Keyword::ON)?; self.expect_keyword_is(Keyword::ON)?;
@ -12049,38 +12017,244 @@ impl<'a> Parser<'a> {
Ok((privileges, objects)) Ok((privileges, objects))
} }
pub fn parse_grant_permission(&mut self) -> Result<ParsedAction, ParserError> { pub fn parse_grant_permission(&mut self) -> Result<Action, ParserError> {
if let Some(kw) = self.parse_one_of_keywords(&[ fn parse_columns(parser: &mut Parser) -> Result<Option<Vec<Ident>>, ParserError> {
Keyword::CONNECT, let columns = parser.parse_parenthesized_column_list(Optional, false)?;
Keyword::CREATE, if columns.is_empty() {
Keyword::DELETE, Ok(None)
Keyword::EXECUTE, } else {
Keyword::INSERT, Ok(Some(columns))
Keyword::REFERENCES, }
Keyword::SELECT, }
Keyword::TEMPORARY,
Keyword::TRIGGER, // Multi-word privileges
Keyword::TRUNCATE, if self.parse_keywords(&[Keyword::IMPORTED, Keyword::PRIVILEGES]) {
Keyword::UPDATE, Ok(Action::ImportedPrivileges)
Keyword::USAGE, } else if self.parse_keywords(&[Keyword::ADD, Keyword::SEARCH, Keyword::OPTIMIZATION]) {
Ok(Action::AddSearchOptimization)
} else if self.parse_keywords(&[Keyword::ATTACH, Keyword::LISTING]) {
Ok(Action::AttachListing)
} else if self.parse_keywords(&[Keyword::ATTACH, Keyword::POLICY]) {
Ok(Action::AttachPolicy)
} else if self.parse_keywords(&[Keyword::BIND, Keyword::SERVICE, Keyword::ENDPOINT]) {
Ok(Action::BindServiceEndpoint)
} else if self.parse_keywords(&[Keyword::EVOLVE, Keyword::SCHEMA]) {
Ok(Action::EvolveSchema)
} else if self.parse_keywords(&[Keyword::IMPORT, Keyword::SHARE]) {
Ok(Action::ImportShare)
} else if self.parse_keywords(&[Keyword::MANAGE, Keyword::VERSIONS]) {
Ok(Action::ManageVersions)
} else if self.parse_keywords(&[Keyword::MANAGE, Keyword::RELEASES]) {
Ok(Action::ManageReleases)
} else if self.parse_keywords(&[Keyword::OVERRIDE, Keyword::SHARE, Keyword::RESTRICTIONS]) {
Ok(Action::OverrideShareRestrictions)
} else if self.parse_keywords(&[
Keyword::PURCHASE,
Keyword::DATA,
Keyword::EXCHANGE,
Keyword::LISTING,
]) { ]) {
let columns = match kw { Ok(Action::PurchaseDataExchangeListing)
Keyword::INSERT | Keyword::REFERENCES | Keyword::SELECT | Keyword::UPDATE => { } else if self.parse_keywords(&[Keyword::RESOLVE, Keyword::ALL]) {
let columns = self.parse_parenthesized_column_list(Optional, false)?; Ok(Action::ResolveAll)
if columns.is_empty() { } else if self.parse_keywords(&[Keyword::READ, Keyword::SESSION]) {
None Ok(Action::ReadSession)
} else {
Some(columns) // Single-word privileges
} } else if self.parse_keyword(Keyword::APPLY) {
} let apply_type = self.parse_action_apply_type()?;
_ => None, Ok(Action::Apply { apply_type })
}; } else if self.parse_keyword(Keyword::APPLYBUDGET) {
Ok((kw, columns)) Ok(Action::ApplyBudget)
} else if self.parse_keyword(Keyword::AUDIT) {
Ok(Action::Audit)
} else if self.parse_keyword(Keyword::CONNECT) {
Ok(Action::Connect)
} else if self.parse_keyword(Keyword::CREATE) {
let obj_type = self.maybe_parse_action_create_object_type();
Ok(Action::Create { obj_type })
} else if self.parse_keyword(Keyword::DELETE) {
Ok(Action::Delete)
} else if self.parse_keyword(Keyword::EXECUTE) {
let obj_type = self.maybe_parse_action_execute_obj_type();
Ok(Action::Execute { obj_type })
} else if self.parse_keyword(Keyword::FAILOVER) {
Ok(Action::Failover)
} else if self.parse_keyword(Keyword::INSERT) {
Ok(Action::Insert {
columns: parse_columns(self)?,
})
} else if self.parse_keyword(Keyword::MANAGE) {
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()?;
Ok(Action::Modify { modify_type })
} else if self.parse_keyword(Keyword::MONITOR) {
let monitor_type = self.parse_action_monitor_type()?;
Ok(Action::Monitor { monitor_type })
} else if self.parse_keyword(Keyword::OPERATE) {
Ok(Action::Operate)
} else if self.parse_keyword(Keyword::REFERENCES) {
Ok(Action::References {
columns: parse_columns(self)?,
})
} else if self.parse_keyword(Keyword::READ) {
Ok(Action::Read)
} else if self.parse_keyword(Keyword::REPLICATE) {
Ok(Action::Replicate)
} else if self.parse_keyword(Keyword::SELECT) {
Ok(Action::Select {
columns: parse_columns(self)?,
})
} else if self.parse_keyword(Keyword::TEMPORARY) {
Ok(Action::Temporary)
} else if self.parse_keyword(Keyword::TRIGGER) {
Ok(Action::Trigger)
} else if self.parse_keyword(Keyword::TRUNCATE) {
Ok(Action::Truncate)
} else if self.parse_keyword(Keyword::UPDATE) {
Ok(Action::Update {
columns: parse_columns(self)?,
})
} else if self.parse_keyword(Keyword::USAGE) {
Ok(Action::Usage)
} else if self.parse_keyword(Keyword::OWNERSHIP) {
Ok(Action::Ownership)
} else { } else {
self.expected("a privilege keyword", self.peek_token())? self.expected("a privilege keyword", self.peek_token())?
} }
} }
fn maybe_parse_action_create_object_type(&mut self) -> Option<ActionCreateObjectType> {
// Multi-word object types
if self.parse_keywords(&[Keyword::APPLICATION, Keyword::PACKAGE]) {
Some(ActionCreateObjectType::ApplicationPackage)
} else if self.parse_keywords(&[Keyword::COMPUTE, Keyword::POOL]) {
Some(ActionCreateObjectType::ComputePool)
} else if self.parse_keywords(&[Keyword::DATA, Keyword::EXCHANGE, Keyword::LISTING]) {
Some(ActionCreateObjectType::DataExchangeListing)
} else if self.parse_keywords(&[Keyword::EXTERNAL, Keyword::VOLUME]) {
Some(ActionCreateObjectType::ExternalVolume)
} else if self.parse_keywords(&[Keyword::FAILOVER, Keyword::GROUP]) {
Some(ActionCreateObjectType::FailoverGroup)
} else if self.parse_keywords(&[Keyword::NETWORK, Keyword::POLICY]) {
Some(ActionCreateObjectType::NetworkPolicy)
} else if self.parse_keywords(&[Keyword::ORGANIZATION, Keyword::LISTING]) {
Some(ActionCreateObjectType::OrganiationListing)
} else if self.parse_keywords(&[Keyword::REPLICATION, Keyword::GROUP]) {
Some(ActionCreateObjectType::ReplicationGroup)
}
// Single-word object types
else if self.parse_keyword(Keyword::ACCOUNT) {
Some(ActionCreateObjectType::Account)
} else if self.parse_keyword(Keyword::APPLICATION) {
Some(ActionCreateObjectType::Application)
} else if self.parse_keyword(Keyword::DATABASE) {
Some(ActionCreateObjectType::Database)
} else if self.parse_keyword(Keyword::INTEGRATION) {
Some(ActionCreateObjectType::Integration)
} else if self.parse_keyword(Keyword::ROLE) {
Some(ActionCreateObjectType::Role)
} else if self.parse_keyword(Keyword::SHARE) {
Some(ActionCreateObjectType::Share)
} else if self.parse_keyword(Keyword::USER) {
Some(ActionCreateObjectType::User)
} else if self.parse_keyword(Keyword::WAREHOUSE) {
Some(ActionCreateObjectType::Warehouse)
} else {
None
}
}
fn parse_action_apply_type(&mut self) -> Result<ActionApplyType, ParserError> {
if self.parse_keywords(&[Keyword::AGGREGATION, Keyword::POLICY]) {
Ok(ActionApplyType::AggregationPolicy)
} else if self.parse_keywords(&[Keyword::AUTHENTICATION, Keyword::POLICY]) {
Ok(ActionApplyType::AuthenticationPolicy)
} else if self.parse_keywords(&[Keyword::JOIN, Keyword::POLICY]) {
Ok(ActionApplyType::JoinPolicy)
} else if self.parse_keywords(&[Keyword::MASKING, Keyword::POLICY]) {
Ok(ActionApplyType::MaskingPolicy)
} else if self.parse_keywords(&[Keyword::PACKAGES, Keyword::POLICY]) {
Ok(ActionApplyType::PackagesPolicy)
} else if self.parse_keywords(&[Keyword::PASSWORD, Keyword::POLICY]) {
Ok(ActionApplyType::PasswordPolicy)
} else if self.parse_keywords(&[Keyword::PROJECTION, Keyword::POLICY]) {
Ok(ActionApplyType::ProjectionPolicy)
} else if self.parse_keywords(&[Keyword::ROW, Keyword::ACCESS, Keyword::POLICY]) {
Ok(ActionApplyType::RowAccessPolicy)
} else if self.parse_keywords(&[Keyword::SESSION, Keyword::POLICY]) {
Ok(ActionApplyType::SessionPolicy)
} else if self.parse_keyword(Keyword::TAG) {
Ok(ActionApplyType::Tag)
} else {
self.expected("GRANT APPLY type", self.peek_token())
}
}
fn maybe_parse_action_execute_obj_type(&mut self) -> Option<ActionExecuteObjectType> {
if self.parse_keywords(&[Keyword::DATA, Keyword::METRIC, Keyword::FUNCTION]) {
Some(ActionExecuteObjectType::DataMetricFunction)
} else if self.parse_keywords(&[Keyword::MANAGED, Keyword::ALERT]) {
Some(ActionExecuteObjectType::ManagedAlert)
} else if self.parse_keywords(&[Keyword::MANAGED, Keyword::TASK]) {
Some(ActionExecuteObjectType::ManagedTask)
} else if self.parse_keyword(Keyword::ALERT) {
Some(ActionExecuteObjectType::Alert)
} else if self.parse_keyword(Keyword::TASK) {
Some(ActionExecuteObjectType::Task)
} else {
None
}
}
fn parse_action_manage_type(&mut self) -> Result<ActionManageType, ParserError> {
if self.parse_keywords(&[Keyword::ACCOUNT, Keyword::SUPPORT, Keyword::CASES]) {
Ok(ActionManageType::AccountSupportCases)
} else if self.parse_keywords(&[Keyword::EVENT, Keyword::SHARING]) {
Ok(ActionManageType::EventSharing)
} else if self.parse_keywords(&[Keyword::LISTING, Keyword::AUTO, Keyword::FULFILLMENT]) {
Ok(ActionManageType::ListingAutoFulfillment)
} else if self.parse_keywords(&[Keyword::ORGANIZATION, Keyword::SUPPORT, Keyword::CASES]) {
Ok(ActionManageType::OrganizationSupportCases)
} else if self.parse_keywords(&[Keyword::USER, Keyword::SUPPORT, Keyword::CASES]) {
Ok(ActionManageType::UserSupportCases)
} else if self.parse_keyword(Keyword::GRANTS) {
Ok(ActionManageType::Grants)
} else if self.parse_keyword(Keyword::WAREHOUSES) {
Ok(ActionManageType::Warehouses)
} else {
self.expected("GRANT MANAGE type", self.peek_token())
}
}
fn parse_action_modify_type(&mut self) -> Result<ActionModifyType, ParserError> {
if self.parse_keywords(&[Keyword::LOG, Keyword::LEVEL]) {
Ok(ActionModifyType::LogLevel)
} else if self.parse_keywords(&[Keyword::TRACE, Keyword::LEVEL]) {
Ok(ActionModifyType::TraceLevel)
} else if self.parse_keywords(&[Keyword::SESSION, Keyword::LOG, Keyword::LEVEL]) {
Ok(ActionModifyType::SessionLogLevel)
} else if self.parse_keywords(&[Keyword::SESSION, Keyword::TRACE, Keyword::LEVEL]) {
Ok(ActionModifyType::SessionTraceLevel)
} else {
self.expected("GRANT MODIFY type", self.peek_token())
}
}
fn parse_action_monitor_type(&mut self) -> Result<ActionMonitorType, ParserError> {
if self.parse_keyword(Keyword::EXECUTION) {
Ok(ActionMonitorType::Execution)
} else if self.parse_keyword(Keyword::SECURITY) {
Ok(ActionMonitorType::Security)
} else if self.parse_keyword(Keyword::USAGE) {
Ok(ActionMonitorType::Usage)
} else {
self.expected("GRANT MONITOR type", self.peek_token())
}
}
pub fn parse_grantee_name(&mut self) -> Result<GranteeName, ParserError> { pub fn parse_grantee_name(&mut self) -> Result<GranteeName, ParserError> {
let mut name = self.parse_object_name(false)?; let mut name = self.parse_object_name(false)?;
if self.dialect.supports_user_host_grantee() if self.dialect.supports_user_host_grantee()

View file

@ -8500,8 +8500,8 @@ fn parse_grant() {
Action::References { columns: None }, Action::References { columns: None },
Action::Trigger, Action::Trigger,
Action::Connect, Action::Connect,
Action::Create, Action::Create { obj_type: None },
Action::Execute, Action::Execute { obj_type: None },
Action::Temporary, Action::Temporary,
], ],
actions actions
@ -8616,6 +8616,7 @@ fn parse_grant() {
verified_stmt("GRANT SELECT ON ALL TABLES IN SCHEMA db1.sc1 TO SHARE share1"); 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 a:b");
verified_stmt("GRANT USAGE ON SCHEMA sc1 TO GROUP group1"); verified_stmt("GRANT USAGE ON SCHEMA sc1 TO GROUP group1");
verified_stmt("GRANT OWNERSHIP ON ALL TABLES IN SCHEMA DEV_STAS_ROGOZHIN TO ROLE ANALYST");
} }
#[test] #[test]

View file

@ -3058,3 +3058,109 @@ fn test_timetravel_at_before() {
snowflake() snowflake()
.verified_only_select("SELECT * FROM tbl BEFORE(TIMESTAMP => '2024-12-15 00:00:00')"); .verified_only_select("SELECT * FROM tbl BEFORE(TIMESTAMP => '2024-12-15 00:00:00')");
} }
#[test]
fn test_grant_account_privileges() {
let privileges = vec![
"ALL",
"ALL PRIVILEGES",
"ATTACH POLICY",
"AUDIT",
"BIND SERVICE ENDPOINT",
"IMPORT SHARE",
"OVERRIDE SHARE RESTRICTIONS",
"PURCHASE DATA EXCHANGE LISTING",
"RESOLVE ALL",
"READ SESSION",
];
let with_grant_options = vec!["", " WITH GRANT OPTION"];
for p in &privileges {
for wgo in &with_grant_options {
let sql = format!("GRANT {p} ON ACCOUNT TO ROLE role1{wgo}");
snowflake_and_generic().verified_stmt(&sql);
}
}
let create_object_types = vec![
"ACCOUNT",
"APPLICATION",
"APPLICATION PACKAGE",
"COMPUTE POOL",
"DATA EXCHANGE LISTING",
"DATABASE",
"EXTERNAL VOLUME",
"FAILOVER GROUP",
"INTEGRATION",
"NETWORK POLICY",
"ORGANIZATION LISTING",
"REPLICATION GROUP",
"ROLE",
"SHARE",
"USER",
"WAREHOUSE",
];
for t in &create_object_types {
for wgo in &with_grant_options {
let sql = format!("GRANT CREATE {t} ON ACCOUNT TO ROLE role1{wgo}");
snowflake_and_generic().verified_stmt(&sql);
}
}
let apply_types = vec![
"AGGREGATION POLICY",
"AUTHENTICATION POLICY",
"JOIN POLICY",
"MASKING POLICY",
"PACKAGES POLICY",
"PASSWORD POLICY",
"PROJECTION POLICY",
"ROW ACCESS POLICY",
"SESSION POLICY",
"TAG",
];
for t in &apply_types {
for wgo in &with_grant_options {
let sql = format!("GRANT APPLY {t} ON ACCOUNT TO ROLE role1{wgo}");
snowflake_and_generic().verified_stmt(&sql);
}
}
let execute_types = vec![
"ALERT",
"DATA METRIC FUNCTION",
"MANAGED ALERT",
"MANAGED TASK",
"TASK",
];
for t in &execute_types {
for wgo in &with_grant_options {
let sql = format!("GRANT EXECUTE {t} ON ACCOUNT TO ROLE role1{wgo}");
snowflake_and_generic().verified_stmt(&sql);
}
}
let manage_types = vec![
"ACCOUNT SUPPORT CASES",
"EVENT SHARING",
"GRANTS",
"LISTING AUTO FULFILLMENT",
"ORGANIZATION SUPPORT CASES",
"USER SUPPORT CASES",
"WAREHOUSES",
];
for t in &manage_types {
for wgo in &with_grant_options {
let sql = format!("GRANT MANAGE {t} ON ACCOUNT TO ROLE role1{wgo}");
snowflake_and_generic().verified_stmt(&sql);
}
}
let monitor_types = vec!["EXECUTION", "SECURITY", "USAGE"];
for t in &monitor_types {
for wgo in &with_grant_options {
let sql = format!("GRANT MONITOR {t} ON ACCOUNT TO ROLE role1{wgo}");
snowflake_and_generic().verified_stmt(&sql);
}
}
}