Skip to content

Commit c1c43dc

Browse files
committed
Snowflake: ALTER USER and KeyValueOptions Refactoring
1 parent b8539a5 commit c1c43dc

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
@@ -4421,6 +4421,11 @@ pub enum Statement {
44214421
/// ```
44224422
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-user)
44234423
CreateUser(CreateUser),
4424+
/// ```sql
4425+
/// ALTER USER \[ IF EXISTS \] \[ <name> \]
4426+
/// ```
4427+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/alter-user)
4428+
AlterUser(AlterUser),
44244429
/// Re-sorts rows and reclaims space in either a specified table or all tables in the current database
44254430
///
44264431
/// ```sql
@@ -6383,6 +6388,7 @@ impl fmt::Display for Statement {
63836388
Statement::CreateUser(s) => write!(f, "{s}"),
63846389
Statement::AlterSchema(s) => write!(f, "{s}"),
63856390
Statement::Vacuum(s) => write!(f, "{s}"),
6391+
Statement::AlterUser(s) => write!(f, "{s}"),
63866392
}
63876393
}
63886394
}
@@ -10758,6 +10764,199 @@ impl fmt::Display for CreateUser {
1075810764
}
1075910765
}
1076010766

10767+
/// Modifies the properties of a user
10768+
///
10769+
/// Syntax:
10770+
/// ```sql
10771+
/// ALTER USER [ IF EXISTS ] [ <name> ] [ OPTIONS ]
10772+
/// ```
10773+
///
10774+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/alter-user)
10775+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10776+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10777+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10778+
pub struct AlterUser {
10779+
pub if_exists: bool,
10780+
pub name: Ident,
10781+
pub rename_to: Option<Ident>,
10782+
pub reset_password: bool,
10783+
pub abort_all_queries: bool,
10784+
pub add_role_delegation: Option<AlterUserAddRoleDelegation>,
10785+
pub remove_role_delegation: Option<AlterUserRemoveRoleDelegation>,
10786+
pub enroll_mfa: bool,
10787+
pub set_default_mfa_method: Option<MfaMethodKind>,
10788+
pub remove_mfa_method: Option<MfaMethodKind>,
10789+
pub modify_mfa_method: Option<AlterUserModifyMfaMethod>,
10790+
pub set_policy: Option<AlterUserSetPolicy>,
10791+
pub unset_policy: Option<UserPolicyKind>,
10792+
pub set_tag: KeyValueOptions,
10793+
pub unset_tag: Vec<String>,
10794+
pub set_props: KeyValueOptions,
10795+
pub unset_props: Vec<String>,
10796+
}
10797+
10798+
/// ```sql
10799+
/// ALTER USER [ IF EXISTS ] [ <name> ] ADD DELEGATED AUTHORIZATION OF ROLE <role_name> TO SECURITY INTEGRATION <integration_name>
10800+
/// ```
10801+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10802+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10803+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10804+
pub struct AlterUserAddRoleDelegation {
10805+
pub role: Ident,
10806+
pub integration: Ident,
10807+
}
10808+
10809+
/// ```sql
10810+
/// ALTER USER [ IF EXISTS ] [ <name> ] REMOVE DELEGATED { AUTHORIZATION OF ROLE <role_name> | AUTHORIZATIONS } FROM SECURITY INTEGRATION <integration_name>
10811+
/// ```
10812+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10813+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10814+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10815+
pub struct AlterUserRemoveRoleDelegation {
10816+
pub role: Option<Ident>,
10817+
pub integration: Ident,
10818+
}
10819+
10820+
/// ```sql
10821+
/// ALTER USER [ IF EXISTS ] [ <name> ] MODIFY MFA METHOD <mfa_method> SET COMMENT = '<string>'
10822+
/// ```
10823+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10824+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10825+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10826+
pub struct AlterUserModifyMfaMethod {
10827+
pub method: MfaMethodKind,
10828+
pub comment: String,
10829+
}
10830+
10831+
/// Types of MFA methods
10832+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10833+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10834+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10835+
pub enum MfaMethodKind {
10836+
PassKey,
10837+
Totp,
10838+
Duo,
10839+
}
10840+
10841+
impl fmt::Display for MfaMethodKind {
10842+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10843+
match self {
10844+
MfaMethodKind::PassKey => write!(f, "PASSKEY"),
10845+
MfaMethodKind::Totp => write!(f, "TOTP"),
10846+
MfaMethodKind::Duo => write!(f, "DUO"),
10847+
}
10848+
}
10849+
}
10850+
10851+
/// ```sql
10852+
/// ALTER USER [ IF EXISTS ] [ <name> ] SET { AUTHENTICATION | PASSWORD | SESSION } POLICY <policy_name>
10853+
/// ```
10854+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10855+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10856+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10857+
pub struct AlterUserSetPolicy {
10858+
pub policy_kind: UserPolicyKind,
10859+
pub policy: Ident,
10860+
}
10861+
10862+
/// Types of user-based policies
10863+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10864+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10865+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10866+
pub enum UserPolicyKind {
10867+
Authentication,
10868+
Password,
10869+
Session,
10870+
}
10871+
10872+
impl fmt::Display for UserPolicyKind {
10873+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10874+
match self {
10875+
UserPolicyKind::Authentication => write!(f, "AUTHENTICATION"),
10876+
UserPolicyKind::Password => write!(f, "PASSWORD"),
10877+
UserPolicyKind::Session => write!(f, "SESSION"),
10878+
}
10879+
}
10880+
}
10881+
10882+
impl fmt::Display for AlterUser {
10883+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10884+
write!(f, "ALTER")?;
10885+
write!(f, " USER")?;
10886+
if self.if_exists {
10887+
write!(f, " IF EXISTS")?;
10888+
}
10889+
write!(f, " {}", self.name)?;
10890+
if let Some(new_name) = &self.rename_to {
10891+
write!(f, " RENAME TO {new_name}")?;
10892+
}
10893+
if self.reset_password {
10894+
write!(f, " RESET PASSWORD")?;
10895+
}
10896+
if self.abort_all_queries {
10897+
write!(f, " ABORT ALL QUERIES")?;
10898+
}
10899+
if let Some(role_delegation) = &self.add_role_delegation {
10900+
let role = &role_delegation.role;
10901+
let integration = &role_delegation.integration;
10902+
write!(
10903+
f,
10904+
" ADD DELEGATED AUTHORIZATION OF ROLE {role} TO SECURITY INTEGRATION {integration}"
10905+
)?;
10906+
}
10907+
if let Some(role_delegation) = &self.remove_role_delegation {
10908+
write!(f, " REMOVE DELEGATED")?;
10909+
match &role_delegation.role {
10910+
Some(role) => write!(f, " AUTHORIZATION OF ROLE {role}")?,
10911+
None => write!(f, " AUTHORIZATIONS")?,
10912+
}
10913+
let integration = &role_delegation.integration;
10914+
write!(f, " FROM SECURITY INTEGRATION {integration}")?;
10915+
}
10916+
if self.enroll_mfa {
10917+
write!(f, " ENROLL MFA")?;
10918+
}
10919+
if let Some(method) = &self.set_default_mfa_method {
10920+
write!(f, " SET DEFAULT_MFA_METHOD {method}")?
10921+
}
10922+
if let Some(method) = &self.remove_mfa_method {
10923+
write!(f, " REMOVE MFA METHOD {method}")?;
10924+
}
10925+
if let Some(modify) = &self.modify_mfa_method {
10926+
let method = &modify.method;
10927+
let comment = &modify.comment;
10928+
write!(
10929+
f,
10930+
" MODIFY MFA METHOD {method} SET COMMENT '{}'",
10931+
value::escape_single_quote_string(comment)
10932+
)?;
10933+
}
10934+
if let Some(policy) = &self.set_policy {
10935+
let policy_kind = &policy.policy_kind;
10936+
let name = &policy.policy;
10937+
write!(f, " SET {policy_kind} POLICY {name}")?;
10938+
}
10939+
if let Some(policy_kind) = &self.unset_policy {
10940+
write!(f, " UNSET {policy_kind} POLICY")?;
10941+
}
10942+
if !self.set_tag.is_empty() {
10943+
write!(f, " SET TAG {}", self.set_tag)?;
10944+
}
10945+
if !self.unset_tag.is_empty() {
10946+
write!(f, " UNSET TAG {}", display_comma_separated(&self.unset_tag))?;
10947+
}
10948+
let has_props = !self.set_props.options.is_empty();
10949+
if has_props {
10950+
write!(f, " SET")?;
10951+
write!(f, " {}", &self.set_props)?;
10952+
}
10953+
if !self.unset_props.is_empty() {
10954+
write!(f, " UNSET {}", display_comma_separated(&self.unset_props))?;
10955+
}
10956+
Ok(())
10957+
}
10958+
}
10959+
1076110960
/// Specifies how to create a new table based on an existing table's schema.
1076210961
/// '''sql
1076310962
/// 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)