Skip to content

Commit a430838

Browse files
authored
Snowflake: ALTER USER and KeyValueOptions Refactoring (apache#2035)
1 parent 54a24e7 commit a430838

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