Skip to content

Commit efa142c

Browse files
committed
Snowflake: ALTER USER and KeyValueOptions Refactoring
1 parent 3c61db5 commit efa142c

File tree

9 files changed

+777
-124
lines changed

9 files changed

+777
-124
lines changed

src/ast/helpers/key_value_options.rs

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use serde::{Deserialize, Serialize};
3131
#[cfg(feature = "visitor")]
3232
use sqlparser_derive::{Visit, VisitMut};
3333

34-
use crate::ast::display_separated;
34+
use crate::ast::{display_comma_separated, display_separated, Value};
3535

3636
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3737
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -41,6 +41,13 @@ pub struct KeyValueOptions {
4141
pub delimiter: KeyValueOptionsDelimiter,
4242
}
4343

44+
impl KeyValueOptions {
45+
/// Returns true iff the options list is empty
46+
pub fn is_empty(&self) -> bool {
47+
self.options.is_empty()
48+
}
49+
}
50+
4451
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
4552
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
4653
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
@@ -52,20 +59,23 @@ pub enum KeyValueOptionsDelimiter {
5259
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5360
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
5461
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
55-
pub enum KeyValueOptionType {
56-
STRING,
57-
BOOLEAN,
58-
ENUM,
59-
NUMBER,
62+
pub struct KeyValueOption {
63+
pub option_name: String,
64+
pub option_value: KeyValueOptionKind,
6065
}
6166

67+
/// An option can have a single value, multiple values or a nested list of values.
68+
///
69+
/// A value can be numeric, boolean, etc. Enum-style values are represented
70+
/// as Value::Placeholder. For example: MFA_METHOD=SMS will be represented as
71+
/// `Value::Placeholder("SMS".to_string)`.
6272
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
6373
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6474
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
65-
pub struct KeyValueOption {
66-
pub option_name: String,
67-
pub option_type: KeyValueOptionType,
68-
pub value: String,
75+
pub enum KeyValueOptionKind {
76+
Single(Value),
77+
Multi(Vec<Value>),
78+
KeyValueOptions(Box<KeyValueOptions>),
6979
}
7080

7181
impl fmt::Display for KeyValueOptions {
@@ -80,12 +90,20 @@ impl fmt::Display for KeyValueOptions {
8090

8191
impl fmt::Display for KeyValueOption {
8292
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83-
match self.option_type {
84-
KeyValueOptionType::STRING => {
85-
write!(f, "{}='{}'", self.option_name, self.value)?;
93+
match &self.option_value {
94+
KeyValueOptionKind::Single(value) => {
95+
write!(f, "{}={value}", self.option_name)?;
96+
}
97+
KeyValueOptionKind::Multi(values) => {
98+
write!(
99+
f,
100+
"{}=({})",
101+
self.option_name,
102+
display_comma_separated(values)
103+
)?;
86104
}
87-
KeyValueOptionType::ENUM | KeyValueOptionType::BOOLEAN | KeyValueOptionType::NUMBER => {
88-
write!(f, "{}={}", self.option_name, self.value)?;
105+
KeyValueOptionKind::KeyValueOptions(options) => {
106+
write!(f, "{}=({options})", self.option_name)?;
89107
}
90108
}
91109
Ok(())

src/ast/mod.rs

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4310,6 +4310,11 @@ pub enum Statement {
43104310
/// ```
43114311
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-user)
43124312
CreateUser(CreateUser),
4313+
/// ```sql
4314+
/// ALTER USER \[ IF EXISTS \] \[ <name> \]
4315+
/// ```
4316+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/alter-user)
4317+
AlterUser(AlterUser),
43134318
/// Re-sorts rows and reclaims space in either a specified table or all tables in the current database
43144319
///
43154320
/// ```sql
@@ -6183,6 +6188,7 @@ impl fmt::Display for Statement {
61836188
Statement::CreateUser(s) => write!(f, "{s}"),
61846189
Statement::AlterSchema(s) => write!(f, "{s}"),
61856190
Statement::Vacuum(s) => write!(f, "{s}"),
6191+
Statement::AlterUser(s) => write!(f, "{s}"),
61866192
}
61876193
}
61886194
}
@@ -10558,6 +10564,199 @@ impl fmt::Display for CreateUser {
1055810564
}
1055910565
}
1056010566

10567+
/// Modifies the properties of a user
10568+
///
10569+
/// Syntax:
10570+
/// ```sql
10571+
/// ALTER USER [ IF EXISTS ] [ <name> ] [ OPTIONS ]
10572+
/// ```
10573+
///
10574+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/alter-user)
10575+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10576+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10577+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10578+
pub struct AlterUser {
10579+
pub if_exists: bool,
10580+
pub name: Ident,
10581+
pub rename_to: Option<Ident>,
10582+
pub reset_password: bool,
10583+
pub abort_all_queries: bool,
10584+
pub add_role_delegation: Option<AlterUserAddRoleDelegation>,
10585+
pub remove_role_delegation: Option<AlterUserRemoveRoleDelegation>,
10586+
pub enroll_mfa: bool,
10587+
pub set_default_mfa_method: Option<MfaMethodKind>,
10588+
pub remove_mfa_method: Option<MfaMethodKind>,
10589+
pub modify_mfa_method: Option<AlterUserModifyMfaMethod>,
10590+
pub set_policy: Option<AlterUserSetPolicy>,
10591+
pub unset_policy: Option<UserPolicyKind>,
10592+
pub set_tag: KeyValueOptions,
10593+
pub unset_tag: Vec<String>,
10594+
pub set_props: KeyValueOptions,
10595+
pub unset_props: Vec<String>,
10596+
}
10597+
10598+
/// ```sql
10599+
/// ALTER USER [ IF EXISTS ] [ <name> ] ADD DELEGATED AUTHORIZATION OF ROLE <role_name> TO SECURITY INTEGRATION <integration_name>
10600+
/// ```
10601+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10602+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10603+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10604+
pub struct AlterUserAddRoleDelegation {
10605+
pub role: Ident,
10606+
pub integration: Ident,
10607+
}
10608+
10609+
/// ```sql
10610+
/// ALTER USER [ IF EXISTS ] [ <name> ] REMOVE DELEGATED { AUTHORIZATION OF ROLE <role_name> | AUTHORIZATIONS } FROM SECURITY INTEGRATION <integration_name>
10611+
/// ```
10612+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10613+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10614+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10615+
pub struct AlterUserRemoveRoleDelegation {
10616+
pub role: Option<Ident>,
10617+
pub integration: Ident,
10618+
}
10619+
10620+
/// ```sql
10621+
/// ALTER USER [ IF EXISTS ] [ <name> ] MODIFY MFA METHOD <mfa_method> SET COMMENT = '<string>'
10622+
/// ```
10623+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10624+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10625+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10626+
pub struct AlterUserModifyMfaMethod {
10627+
pub method: MfaMethodKind,
10628+
pub comment: String,
10629+
}
10630+
10631+
/// Types of MFA methods
10632+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10633+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10634+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10635+
pub enum MfaMethodKind {
10636+
PassKey,
10637+
Totp,
10638+
Duo,
10639+
}
10640+
10641+
impl fmt::Display for MfaMethodKind {
10642+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10643+
match self {
10644+
MfaMethodKind::PassKey => write!(f, "PASSKEY"),
10645+
MfaMethodKind::Totp => write!(f, "TOTP"),
10646+
MfaMethodKind::Duo => write!(f, "DUO"),
10647+
}
10648+
}
10649+
}
10650+
10651+
/// ```sql
10652+
/// ALTER USER [ IF EXISTS ] [ <name> ] SET { AUTHENTICATION | PASSWORD | SESSION } POLICY <policy_name>
10653+
/// ```
10654+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10655+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10656+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10657+
pub struct AlterUserSetPolicy {
10658+
pub policy_kind: UserPolicyKind,
10659+
pub policy: Ident,
10660+
}
10661+
10662+
/// Types of user-based policies
10663+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10664+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10665+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10666+
pub enum UserPolicyKind {
10667+
Authentication,
10668+
Password,
10669+
Session,
10670+
}
10671+
10672+
impl fmt::Display for UserPolicyKind {
10673+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10674+
match self {
10675+
UserPolicyKind::Authentication => write!(f, "AUTHENTICATION"),
10676+
UserPolicyKind::Password => write!(f, "PASSWORD"),
10677+
UserPolicyKind::Session => write!(f, "SESSION"),
10678+
}
10679+
}
10680+
}
10681+
10682+
impl fmt::Display for AlterUser {
10683+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10684+
write!(f, "ALTER")?;
10685+
write!(f, " USER")?;
10686+
if self.if_exists {
10687+
write!(f, " IF EXISTS")?;
10688+
}
10689+
write!(f, " {}", self.name)?;
10690+
if let Some(new_name) = &self.rename_to {
10691+
write!(f, " RENAME TO {new_name}")?;
10692+
}
10693+
if self.reset_password {
10694+
write!(f, " RESET PASSWORD")?;
10695+
}
10696+
if self.abort_all_queries {
10697+
write!(f, " ABORT ALL QUERIES")?;
10698+
}
10699+
if let Some(role_delegation) = &self.add_role_delegation {
10700+
let role = &role_delegation.role;
10701+
let integration = &role_delegation.integration;
10702+
write!(
10703+
f,
10704+
" ADD DELEGATED AUTHORIZATION OF ROLE {role} TO SECURITY INTEGRATION {integration}"
10705+
)?;
10706+
}
10707+
if let Some(role_delegation) = &self.remove_role_delegation {
10708+
write!(f, " REMOVE DELEGATED")?;
10709+
match &role_delegation.role {
10710+
Some(role) => write!(f, " AUTHORIZATION OF ROLE {role}")?,
10711+
None => write!(f, " AUTHORIZATIONS")?,
10712+
}
10713+
let integration = &role_delegation.integration;
10714+
write!(f, " FROM SECURITY INTEGRATION {integration}")?;
10715+
}
10716+
if self.enroll_mfa {
10717+
write!(f, " ENROLL MFA")?;
10718+
}
10719+
if let Some(method) = &self.set_default_mfa_method {
10720+
write!(f, " SET DEFAULT_MFA_METHOD {method}")?
10721+
}
10722+
if let Some(method) = &self.remove_mfa_method {
10723+
write!(f, " REMOVE MFA METHOD {method}")?;
10724+
}
10725+
if let Some(modify) = &self.modify_mfa_method {
10726+
let method = &modify.method;
10727+
let comment = &modify.comment;
10728+
write!(
10729+
f,
10730+
" MODIFY MFA METHOD {method} SET COMMENT '{}'",
10731+
value::escape_single_quote_string(comment)
10732+
)?;
10733+
}
10734+
if let Some(policy) = &self.set_policy {
10735+
let policy_kind = &policy.policy_kind;
10736+
let name = &policy.policy;
10737+
write!(f, " SET {policy_kind} POLICY {name}")?;
10738+
}
10739+
if let Some(policy_kind) = &self.unset_policy {
10740+
write!(f, " UNSET {policy_kind} POLICY")?;
10741+
}
10742+
if !self.set_tag.is_empty() {
10743+
write!(f, " SET TAG {}", self.set_tag)?;
10744+
}
10745+
if !self.unset_tag.is_empty() {
10746+
write!(f, " UNSET TAG {}", display_comma_separated(&self.unset_tag))?;
10747+
}
10748+
let has_props = !self.set_props.options.is_empty();
10749+
if has_props {
10750+
write!(f, " SET")?;
10751+
write!(f, " {}", &self.set_props)?;
10752+
}
10753+
if !self.unset_props.is_empty() {
10754+
write!(f, " UNSET {}", display_comma_separated(&self.unset_props))?;
10755+
}
10756+
Ok(())
10757+
}
10758+
}
10759+
1056110760
/// Specifies how to create a new table based on an existing table's schema.
1056210761
/// '''sql
1056310762
/// CREATE TABLE new LIKE old ...

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ impl Spanned for Statement {
555555
Statement::CreateUser(..) => Span::empty(),
556556
Statement::AlterSchema(s) => s.span(),
557557
Statement::Vacuum(..) => Span::empty(),
558+
Statement::AlterUser(..) => Span::empty(),
558559
}
559560
}
560561
}

0 commit comments

Comments
 (0)