Skip to content

Commit e6c0539

Browse files
yoavcloudayman-sigma
authored andcommitted
Snowflake: ALTER USER and KeyValueOptions Refactoring (apache#2035)
1 parent 1f8b57d commit e6c0539

File tree

11 files changed

+809
-135
lines changed

11 files changed

+809
-135
lines changed

src/ast/dml.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,7 @@
1616
// under the License.
1717

1818
#[cfg(not(feature = "std"))]
19-
use alloc::{
20-
boxed::Box,
21-
format,
22-
string::{String, ToString},
23-
vec::Vec,
24-
};
19+
use alloc::{boxed::Box, format, string::ToString, vec::Vec};
2520

2621
use core::fmt::{self, Display};
2722
#[cfg(feature = "serde")]

src/ast/helpers/key_value_options.rs

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919
//! See [this page](https://docs.snowflake.com/en/sql-reference/commands-data-loading) for more details.
2020
2121
#[cfg(not(feature = "std"))]
22-
use alloc::string::String;
23-
#[cfg(not(feature = "std"))]
24-
use alloc::vec::Vec;
22+
use alloc::{boxed::Box, string::String, vec::Vec};
2523
use core::fmt;
2624
use core::fmt::Formatter;
2725

@@ -31,7 +29,7 @@ use serde::{Deserialize, Serialize};
3129
#[cfg(feature = "visitor")]
3230
use sqlparser_derive::{Visit, VisitMut};
3331

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

3634
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3735
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -52,20 +50,23 @@ pub enum KeyValueOptionsDelimiter {
5250
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5351
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
5452
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
55-
pub enum KeyValueOptionType {
56-
STRING,
57-
BOOLEAN,
58-
ENUM,
59-
NUMBER,
53+
pub struct KeyValueOption {
54+
pub option_name: String,
55+
pub option_value: KeyValueOptionKind,
6056
}
6157

58+
/// An option can have a single value, multiple values or a nested list of values.
59+
///
60+
/// A value can be numeric, boolean, etc. Enum-style values are represented
61+
/// as Value::Placeholder. For example: MFA_METHOD=SMS will be represented as
62+
/// `Value::Placeholder("SMS".to_string)`.
6263
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
6364
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6465
#[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,
66+
pub enum KeyValueOptionKind {
67+
Single(Value),
68+
Multi(Vec<Value>),
69+
KeyValueOptions(Box<KeyValueOptions>),
6970
}
7071

7172
impl fmt::Display for KeyValueOptions {
@@ -80,12 +81,20 @@ impl fmt::Display for KeyValueOptions {
8081

8182
impl fmt::Display for KeyValueOption {
8283
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)?;
84+
match &self.option_value {
85+
KeyValueOptionKind::Single(value) => {
86+
write!(f, "{}={value}", self.option_name)?;
87+
}
88+
KeyValueOptionKind::Multi(values) => {
89+
write!(
90+
f,
91+
"{}=({})",
92+
self.option_name,
93+
display_comma_separated(values)
94+
)?;
8695
}
87-
KeyValueOptionType::ENUM | KeyValueOptionType::BOOLEAN | KeyValueOptionType::NUMBER => {
88-
write!(f, "{}={}", self.option_name, self.value)?;
96+
KeyValueOptionKind::KeyValueOptions(options) => {
97+
write!(f, "{}=({options})", self.option_name)?;
8998
}
9099
}
91100
Ok(())

src/ast/helpers/stmt_create_database.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
// under the License.
1717

1818
#[cfg(not(feature = "std"))]
19-
use alloc::{boxed::Box, format, string::String, vec, vec::Vec};
19+
use alloc::{format, string::String, vec::Vec};
2020

2121
#[cfg(feature = "serde")]
2222
use serde::{Deserialize, Serialize};

src/ast/mod.rs

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4339,6 +4339,11 @@ pub enum Statement {
43394339
/// ```
43404340
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-user)
43414341
CreateUser(CreateUser),
4342+
/// ```sql
4343+
/// ALTER USER \[ IF EXISTS \] \[ <name> \]
4344+
/// ```
4345+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/alter-user)
4346+
AlterUser(AlterUser),
43424347
/// Re-sorts rows and reclaims space in either a specified table or all tables in the current database
43434348
///
43444349
/// ```sql
@@ -6212,6 +6217,7 @@ impl fmt::Display for Statement {
62126217
Statement::CreateUser(s) => write!(f, "{s}"),
62136218
Statement::AlterSchema(s) => write!(f, "{s}"),
62146219
Statement::Vacuum(s) => write!(f, "{s}"),
6220+
Statement::AlterUser(s) => write!(f, "{s}"),
62156221
}
62166222
}
62176223
}
@@ -10587,6 +10593,217 @@ impl fmt::Display for CreateUser {
1058710593
}
1058810594
}
1058910595

10596+
/// Modifies the properties of a user
10597+
///
10598+
/// Syntax:
10599+
/// ```sql
10600+
/// ALTER USER [ IF EXISTS ] [ <name> ] [ OPTIONS ]
10601+
/// ```
10602+
///
10603+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/alter-user)
10604+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10605+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10606+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10607+
pub struct AlterUser {
10608+
pub if_exists: bool,
10609+
pub name: Ident,
10610+
/// The following fields are Snowflake-specific: <https://docs.snowflake.com/en/sql-reference/sql/alter-user#syntax>
10611+
pub rename_to: Option<Ident>,
10612+
pub reset_password: bool,
10613+
pub abort_all_queries: bool,
10614+
pub add_role_delegation: Option<AlterUserAddRoleDelegation>,
10615+
pub remove_role_delegation: Option<AlterUserRemoveRoleDelegation>,
10616+
pub enroll_mfa: bool,
10617+
pub set_default_mfa_method: Option<MfaMethodKind>,
10618+
pub remove_mfa_method: Option<MfaMethodKind>,
10619+
pub modify_mfa_method: Option<AlterUserModifyMfaMethod>,
10620+
pub add_mfa_method_otp: Option<AlterUserAddMfaMethodOtp>,
10621+
pub set_policy: Option<AlterUserSetPolicy>,
10622+
pub unset_policy: Option<UserPolicyKind>,
10623+
pub set_tag: KeyValueOptions,
10624+
pub unset_tag: Vec<String>,
10625+
pub set_props: KeyValueOptions,
10626+
pub unset_props: Vec<String>,
10627+
}
10628+
10629+
/// ```sql
10630+
/// ALTER USER [ IF EXISTS ] [ <name> ] ADD DELEGATED AUTHORIZATION OF ROLE <role_name> TO SECURITY INTEGRATION <integration_name>
10631+
/// ```
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 struct AlterUserAddRoleDelegation {
10636+
pub role: Ident,
10637+
pub integration: Ident,
10638+
}
10639+
10640+
/// ```sql
10641+
/// ALTER USER [ IF EXISTS ] [ <name> ] REMOVE DELEGATED { AUTHORIZATION OF ROLE <role_name> | AUTHORIZATIONS } FROM SECURITY INTEGRATION <integration_name>
10642+
/// ```
10643+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10644+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10645+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10646+
pub struct AlterUserRemoveRoleDelegation {
10647+
pub role: Option<Ident>,
10648+
pub integration: Ident,
10649+
}
10650+
10651+
/// ```sql
10652+
/// ADD MFA METHOD OTP [ COUNT = number ]
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 AlterUserAddMfaMethodOtp {
10658+
pub count: Option<Value>,
10659+
}
10660+
10661+
/// ```sql
10662+
/// ALTER USER [ IF EXISTS ] [ <name> ] MODIFY MFA METHOD <mfa_method> SET COMMENT = '<string>'
10663+
/// ```
10664+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10665+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10666+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10667+
pub struct AlterUserModifyMfaMethod {
10668+
pub method: MfaMethodKind,
10669+
pub comment: String,
10670+
}
10671+
10672+
/// Types of MFA methods
10673+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10674+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10675+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10676+
pub enum MfaMethodKind {
10677+
PassKey,
10678+
Totp,
10679+
Duo,
10680+
}
10681+
10682+
impl fmt::Display for MfaMethodKind {
10683+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10684+
match self {
10685+
MfaMethodKind::PassKey => write!(f, "PASSKEY"),
10686+
MfaMethodKind::Totp => write!(f, "TOTP"),
10687+
MfaMethodKind::Duo => write!(f, "DUO"),
10688+
}
10689+
}
10690+
}
10691+
10692+
/// ```sql
10693+
/// ALTER USER [ IF EXISTS ] [ <name> ] SET { AUTHENTICATION | PASSWORD | SESSION } POLICY <policy_name>
10694+
/// ```
10695+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10696+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10697+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10698+
pub struct AlterUserSetPolicy {
10699+
pub policy_kind: UserPolicyKind,
10700+
pub policy: Ident,
10701+
}
10702+
10703+
/// Types of user-based policies
10704+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10705+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10706+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10707+
pub enum UserPolicyKind {
10708+
Authentication,
10709+
Password,
10710+
Session,
10711+
}
10712+
10713+
impl fmt::Display for UserPolicyKind {
10714+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10715+
match self {
10716+
UserPolicyKind::Authentication => write!(f, "AUTHENTICATION"),
10717+
UserPolicyKind::Password => write!(f, "PASSWORD"),
10718+
UserPolicyKind::Session => write!(f, "SESSION"),
10719+
}
10720+
}
10721+
}
10722+
10723+
impl fmt::Display for AlterUser {
10724+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10725+
write!(f, "ALTER")?;
10726+
write!(f, " USER")?;
10727+
if self.if_exists {
10728+
write!(f, " IF EXISTS")?;
10729+
}
10730+
write!(f, " {}", self.name)?;
10731+
if let Some(new_name) = &self.rename_to {
10732+
write!(f, " RENAME TO {new_name}")?;
10733+
}
10734+
if self.reset_password {
10735+
write!(f, " RESET PASSWORD")?;
10736+
}
10737+
if self.abort_all_queries {
10738+
write!(f, " ABORT ALL QUERIES")?;
10739+
}
10740+
if let Some(role_delegation) = &self.add_role_delegation {
10741+
let role = &role_delegation.role;
10742+
let integration = &role_delegation.integration;
10743+
write!(
10744+
f,
10745+
" ADD DELEGATED AUTHORIZATION OF ROLE {role} TO SECURITY INTEGRATION {integration}"
10746+
)?;
10747+
}
10748+
if let Some(role_delegation) = &self.remove_role_delegation {
10749+
write!(f, " REMOVE DELEGATED")?;
10750+
match &role_delegation.role {
10751+
Some(role) => write!(f, " AUTHORIZATION OF ROLE {role}")?,
10752+
None => write!(f, " AUTHORIZATIONS")?,
10753+
}
10754+
let integration = &role_delegation.integration;
10755+
write!(f, " FROM SECURITY INTEGRATION {integration}")?;
10756+
}
10757+
if self.enroll_mfa {
10758+
write!(f, " ENROLL MFA")?;
10759+
}
10760+
if let Some(method) = &self.set_default_mfa_method {
10761+
write!(f, " SET DEFAULT_MFA_METHOD {method}")?
10762+
}
10763+
if let Some(method) = &self.remove_mfa_method {
10764+
write!(f, " REMOVE MFA METHOD {method}")?;
10765+
}
10766+
if let Some(modify) = &self.modify_mfa_method {
10767+
let method = &modify.method;
10768+
let comment = &modify.comment;
10769+
write!(
10770+
f,
10771+
" MODIFY MFA METHOD {method} SET COMMENT '{}'",
10772+
value::escape_single_quote_string(comment)
10773+
)?;
10774+
}
10775+
if let Some(add_mfa_method_otp) = &self.add_mfa_method_otp {
10776+
write!(f, " ADD MFA METHOD OTP")?;
10777+
if let Some(count) = &add_mfa_method_otp.count {
10778+
write!(f, " COUNT = {count}")?;
10779+
}
10780+
}
10781+
if let Some(policy) = &self.set_policy {
10782+
let policy_kind = &policy.policy_kind;
10783+
let name = &policy.policy;
10784+
write!(f, " SET {policy_kind} POLICY {name}")?;
10785+
}
10786+
if let Some(policy_kind) = &self.unset_policy {
10787+
write!(f, " UNSET {policy_kind} POLICY")?;
10788+
}
10789+
if !self.set_tag.options.is_empty() {
10790+
write!(f, " SET TAG {}", self.set_tag)?;
10791+
}
10792+
if !self.unset_tag.is_empty() {
10793+
write!(f, " UNSET TAG {}", display_comma_separated(&self.unset_tag))?;
10794+
}
10795+
let has_props = !self.set_props.options.is_empty();
10796+
if has_props {
10797+
write!(f, " SET")?;
10798+
write!(f, " {}", &self.set_props)?;
10799+
}
10800+
if !self.unset_props.is_empty() {
10801+
write!(f, " UNSET {}", display_comma_separated(&self.unset_props))?;
10802+
}
10803+
Ok(())
10804+
}
10805+
}
10806+
1059010807
/// Specifies how to create a new table based on an existing table's schema.
1059110808
/// '''sql
1059210809
/// 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)