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 = "visitor", derive(Visit, VisitMut))]
pub enum Action {
AddSearchOptimization,
Apply {
apply_type: ActionApplyType,
},
ApplyBudget,
AttachListing,
AttachPolicy,
Audit,
BindServiceEndpoint,
Connect,
Create,
Create {
obj_type: Option<ActionCreateObjectType>,
},
Delete,
Execute,
Insert { columns: Option<Vec<Ident>> },
References { columns: Option<Vec<Ident>> },
Select { columns: Option<Vec<Ident>> },
EvolveSchema,
Execute {
obj_type: Option<ActionExecuteObjectType>,
},
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,
Trigger,
Truncate,
Update { columns: Option<Vec<Ident>> },
Update {
columns: Option<Vec<Ident>>,
},
Usage,
}
impl fmt::Display for Action {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
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::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::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::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::Replicate => f.write_str("REPLICATE")?,
Action::ResolveAll => f.write_str("RESOLVE ALL")?,
Action::Select { .. } => f.write_str("SELECT")?,
Action::Temporary => f.write_str("TEMPORARY")?,
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
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]

View file

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

View file

@ -48,9 +48,6 @@ pub enum ParserError {
RecursionLimitExceeded,
}
// avoid clippy type_complexity warnings
type ParsedAction = (Keyword, Option<Vec<Ident>>);
// Use `Parser::expected` instead, if possible
macro_rules! parser_err {
($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![];
loop {
values.push(self.parse_grant_permission()?);
@ -11980,37 +11977,8 @@ impl<'a> Parser<'a> {
with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES),
}
} else {
let (actions, err): (Vec<_>, Vec<_>) = self
.parse_actions_list()?
.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)
let actions = self.parse_actions_list()?;
Privileges::Actions(actions)
};
self.expect_keyword_is(Keyword::ON)?;
@ -12049,38 +12017,244 @@ impl<'a> Parser<'a> {
Ok((privileges, objects))
}
pub fn parse_grant_permission(&mut self) -> Result<ParsedAction, ParserError> {
if let Some(kw) = self.parse_one_of_keywords(&[
Keyword::CONNECT,
Keyword::CREATE,
Keyword::DELETE,
Keyword::EXECUTE,
Keyword::INSERT,
Keyword::REFERENCES,
Keyword::SELECT,
Keyword::TEMPORARY,
Keyword::TRIGGER,
Keyword::TRUNCATE,
Keyword::UPDATE,
Keyword::USAGE,
pub fn parse_grant_permission(&mut self) -> Result<Action, ParserError> {
fn parse_columns(parser: &mut Parser) -> Result<Option<Vec<Ident>>, ParserError> {
let columns = parser.parse_parenthesized_column_list(Optional, false)?;
if columns.is_empty() {
Ok(None)
} else {
Ok(Some(columns))
}
}
// Multi-word privileges
if self.parse_keywords(&[Keyword::IMPORTED, Keyword::PRIVILEGES]) {
Ok(Action::ImportedPrivileges)
} 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 {
Keyword::INSERT | Keyword::REFERENCES | Keyword::SELECT | Keyword::UPDATE => {
let columns = self.parse_parenthesized_column_list(Optional, false)?;
if columns.is_empty() {
None
} else {
Some(columns)
}
}
_ => None,
};
Ok((kw, columns))
Ok(Action::PurchaseDataExchangeListing)
} else if self.parse_keywords(&[Keyword::RESOLVE, Keyword::ALL]) {
Ok(Action::ResolveAll)
} else if self.parse_keywords(&[Keyword::READ, Keyword::SESSION]) {
Ok(Action::ReadSession)
// Single-word privileges
} else if self.parse_keyword(Keyword::APPLY) {
let apply_type = self.parse_action_apply_type()?;
Ok(Action::Apply { apply_type })
} else if self.parse_keyword(Keyword::APPLYBUDGET) {
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 {
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> {
let mut name = self.parse_object_name(false)?;
if self.dialect.supports_user_host_grantee()

View file

@ -8500,8 +8500,8 @@ fn parse_grant() {
Action::References { columns: None },
Action::Trigger,
Action::Connect,
Action::Create,
Action::Execute,
Action::Create { obj_type: None },
Action::Execute { obj_type: None },
Action::Temporary,
],
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 USAGE ON SCHEMA sc1 TO a:b");
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]

View file

@ -3058,3 +3058,109 @@ fn test_timetravel_at_before() {
snowflake()
.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);
}
}
}