mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-12-23 11:12:51 +00:00
Snowflake: ALTER USER and KeyValueOptions Refactoring (#2035)
Some checks are pending
license / Release Audit Tool (RAT) (push) Waiting to run
Rust / codestyle (push) Waiting to run
Rust / lint (push) Waiting to run
Rust / benchmark-lint (push) Waiting to run
Rust / compile (push) Waiting to run
Rust / docs (push) Waiting to run
Rust / compile-no-std (push) Waiting to run
Rust / test (beta) (push) Waiting to run
Rust / test (nightly) (push) Waiting to run
Rust / test (stable) (push) Waiting to run
Some checks are pending
license / Release Audit Tool (RAT) (push) Waiting to run
Rust / codestyle (push) Waiting to run
Rust / lint (push) Waiting to run
Rust / benchmark-lint (push) Waiting to run
Rust / compile (push) Waiting to run
Rust / docs (push) Waiting to run
Rust / compile-no-std (push) Waiting to run
Rust / test (beta) (push) Waiting to run
Rust / test (nightly) (push) Waiting to run
Rust / test (stable) (push) Waiting to run
This commit is contained in:
parent
54a24e76a9
commit
a430838974
11 changed files with 809 additions and 135 deletions
|
|
@ -16,12 +16,7 @@
|
|||
// under the License.
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
format,
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use alloc::{boxed::Box, format, string::ToString, vec::Vec};
|
||||
|
||||
use core::fmt::{self, Display};
|
||||
#[cfg(feature = "serde")]
|
||||
|
|
|
|||
|
|
@ -19,9 +19,7 @@
|
|||
//! See [this page](https://docs.snowflake.com/en/sql-reference/commands-data-loading) for more details.
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc::string::String;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{boxed::Box, string::String, vec::Vec};
|
||||
use core::fmt;
|
||||
use core::fmt::Formatter;
|
||||
|
||||
|
|
@ -31,7 +29,7 @@ use serde::{Deserialize, Serialize};
|
|||
#[cfg(feature = "visitor")]
|
||||
use sqlparser_derive::{Visit, VisitMut};
|
||||
|
||||
use crate::ast::display_separated;
|
||||
use crate::ast::{display_comma_separated, display_separated, Value};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
|
|
@ -52,20 +50,23 @@ pub enum KeyValueOptionsDelimiter {
|
|||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum KeyValueOptionType {
|
||||
STRING,
|
||||
BOOLEAN,
|
||||
ENUM,
|
||||
NUMBER,
|
||||
pub struct KeyValueOption {
|
||||
pub option_name: String,
|
||||
pub option_value: KeyValueOptionKind,
|
||||
}
|
||||
|
||||
/// An option can have a single value, multiple values or a nested list of values.
|
||||
///
|
||||
/// A value can be numeric, boolean, etc. Enum-style values are represented
|
||||
/// as Value::Placeholder. For example: MFA_METHOD=SMS will be represented as
|
||||
/// `Value::Placeholder("SMS".to_string)`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub struct KeyValueOption {
|
||||
pub option_name: String,
|
||||
pub option_type: KeyValueOptionType,
|
||||
pub value: String,
|
||||
pub enum KeyValueOptionKind {
|
||||
Single(Value),
|
||||
Multi(Vec<Value>),
|
||||
KeyValueOptions(Box<KeyValueOptions>),
|
||||
}
|
||||
|
||||
impl fmt::Display for KeyValueOptions {
|
||||
|
|
@ -80,12 +81,20 @@ impl fmt::Display for KeyValueOptions {
|
|||
|
||||
impl fmt::Display for KeyValueOption {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.option_type {
|
||||
KeyValueOptionType::STRING => {
|
||||
write!(f, "{}='{}'", self.option_name, self.value)?;
|
||||
match &self.option_value {
|
||||
KeyValueOptionKind::Single(value) => {
|
||||
write!(f, "{}={value}", self.option_name)?;
|
||||
}
|
||||
KeyValueOptionType::ENUM | KeyValueOptionType::BOOLEAN | KeyValueOptionType::NUMBER => {
|
||||
write!(f, "{}={}", self.option_name, self.value)?;
|
||||
KeyValueOptionKind::Multi(values) => {
|
||||
write!(
|
||||
f,
|
||||
"{}=({})",
|
||||
self.option_name,
|
||||
display_comma_separated(values)
|
||||
)?;
|
||||
}
|
||||
KeyValueOptionKind::KeyValueOptions(options) => {
|
||||
write!(f, "{}=({options})", self.option_name)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
// under the License.
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc::{boxed::Box, format, string::String, vec, vec::Vec};
|
||||
use alloc::{format, string::String, vec::Vec};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
|||
217
src/ast/mod.rs
217
src/ast/mod.rs
|
|
@ -4310,6 +4310,11 @@ pub enum Statement {
|
|||
/// ```
|
||||
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-user)
|
||||
CreateUser(CreateUser),
|
||||
/// ```sql
|
||||
/// ALTER USER \[ IF EXISTS \] \[ <name> \]
|
||||
/// ```
|
||||
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/alter-user)
|
||||
AlterUser(AlterUser),
|
||||
/// Re-sorts rows and reclaims space in either a specified table or all tables in the current database
|
||||
///
|
||||
/// ```sql
|
||||
|
|
@ -6183,6 +6188,7 @@ impl fmt::Display for Statement {
|
|||
Statement::CreateUser(s) => write!(f, "{s}"),
|
||||
Statement::AlterSchema(s) => write!(f, "{s}"),
|
||||
Statement::Vacuum(s) => write!(f, "{s}"),
|
||||
Statement::AlterUser(s) => write!(f, "{s}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10558,6 +10564,217 @@ impl fmt::Display for CreateUser {
|
|||
}
|
||||
}
|
||||
|
||||
/// Modifies the properties of a user
|
||||
///
|
||||
/// Syntax:
|
||||
/// ```sql
|
||||
/// ALTER USER [ IF EXISTS ] [ <name> ] [ OPTIONS ]
|
||||
/// ```
|
||||
///
|
||||
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/alter-user)
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub struct AlterUser {
|
||||
pub if_exists: bool,
|
||||
pub name: Ident,
|
||||
/// The following fields are Snowflake-specific: <https://docs.snowflake.com/en/sql-reference/sql/alter-user#syntax>
|
||||
pub rename_to: Option<Ident>,
|
||||
pub reset_password: bool,
|
||||
pub abort_all_queries: bool,
|
||||
pub add_role_delegation: Option<AlterUserAddRoleDelegation>,
|
||||
pub remove_role_delegation: Option<AlterUserRemoveRoleDelegation>,
|
||||
pub enroll_mfa: bool,
|
||||
pub set_default_mfa_method: Option<MfaMethodKind>,
|
||||
pub remove_mfa_method: Option<MfaMethodKind>,
|
||||
pub modify_mfa_method: Option<AlterUserModifyMfaMethod>,
|
||||
pub add_mfa_method_otp: Option<AlterUserAddMfaMethodOtp>,
|
||||
pub set_policy: Option<AlterUserSetPolicy>,
|
||||
pub unset_policy: Option<UserPolicyKind>,
|
||||
pub set_tag: KeyValueOptions,
|
||||
pub unset_tag: Vec<String>,
|
||||
pub set_props: KeyValueOptions,
|
||||
pub unset_props: Vec<String>,
|
||||
}
|
||||
|
||||
/// ```sql
|
||||
/// ALTER USER [ IF EXISTS ] [ <name> ] ADD DELEGATED AUTHORIZATION OF ROLE <role_name> TO SECURITY INTEGRATION <integration_name>
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub struct AlterUserAddRoleDelegation {
|
||||
pub role: Ident,
|
||||
pub integration: Ident,
|
||||
}
|
||||
|
||||
/// ```sql
|
||||
/// ALTER USER [ IF EXISTS ] [ <name> ] REMOVE DELEGATED { AUTHORIZATION OF ROLE <role_name> | AUTHORIZATIONS } FROM SECURITY INTEGRATION <integration_name>
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub struct AlterUserRemoveRoleDelegation {
|
||||
pub role: Option<Ident>,
|
||||
pub integration: Ident,
|
||||
}
|
||||
|
||||
/// ```sql
|
||||
/// ADD MFA METHOD OTP [ COUNT = number ]
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub struct AlterUserAddMfaMethodOtp {
|
||||
pub count: Option<Value>,
|
||||
}
|
||||
|
||||
/// ```sql
|
||||
/// ALTER USER [ IF EXISTS ] [ <name> ] MODIFY MFA METHOD <mfa_method> SET COMMENT = '<string>'
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub struct AlterUserModifyMfaMethod {
|
||||
pub method: MfaMethodKind,
|
||||
pub comment: String,
|
||||
}
|
||||
|
||||
/// Types of MFA methods
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum MfaMethodKind {
|
||||
PassKey,
|
||||
Totp,
|
||||
Duo,
|
||||
}
|
||||
|
||||
impl fmt::Display for MfaMethodKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
MfaMethodKind::PassKey => write!(f, "PASSKEY"),
|
||||
MfaMethodKind::Totp => write!(f, "TOTP"),
|
||||
MfaMethodKind::Duo => write!(f, "DUO"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ```sql
|
||||
/// ALTER USER [ IF EXISTS ] [ <name> ] SET { AUTHENTICATION | PASSWORD | SESSION } POLICY <policy_name>
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub struct AlterUserSetPolicy {
|
||||
pub policy_kind: UserPolicyKind,
|
||||
pub policy: Ident,
|
||||
}
|
||||
|
||||
/// Types of user-based policies
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum UserPolicyKind {
|
||||
Authentication,
|
||||
Password,
|
||||
Session,
|
||||
}
|
||||
|
||||
impl fmt::Display for UserPolicyKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
UserPolicyKind::Authentication => write!(f, "AUTHENTICATION"),
|
||||
UserPolicyKind::Password => write!(f, "PASSWORD"),
|
||||
UserPolicyKind::Session => write!(f, "SESSION"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AlterUser {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "ALTER")?;
|
||||
write!(f, " USER")?;
|
||||
if self.if_exists {
|
||||
write!(f, " IF EXISTS")?;
|
||||
}
|
||||
write!(f, " {}", self.name)?;
|
||||
if let Some(new_name) = &self.rename_to {
|
||||
write!(f, " RENAME TO {new_name}")?;
|
||||
}
|
||||
if self.reset_password {
|
||||
write!(f, " RESET PASSWORD")?;
|
||||
}
|
||||
if self.abort_all_queries {
|
||||
write!(f, " ABORT ALL QUERIES")?;
|
||||
}
|
||||
if let Some(role_delegation) = &self.add_role_delegation {
|
||||
let role = &role_delegation.role;
|
||||
let integration = &role_delegation.integration;
|
||||
write!(
|
||||
f,
|
||||
" ADD DELEGATED AUTHORIZATION OF ROLE {role} TO SECURITY INTEGRATION {integration}"
|
||||
)?;
|
||||
}
|
||||
if let Some(role_delegation) = &self.remove_role_delegation {
|
||||
write!(f, " REMOVE DELEGATED")?;
|
||||
match &role_delegation.role {
|
||||
Some(role) => write!(f, " AUTHORIZATION OF ROLE {role}")?,
|
||||
None => write!(f, " AUTHORIZATIONS")?,
|
||||
}
|
||||
let integration = &role_delegation.integration;
|
||||
write!(f, " FROM SECURITY INTEGRATION {integration}")?;
|
||||
}
|
||||
if self.enroll_mfa {
|
||||
write!(f, " ENROLL MFA")?;
|
||||
}
|
||||
if let Some(method) = &self.set_default_mfa_method {
|
||||
write!(f, " SET DEFAULT_MFA_METHOD {method}")?
|
||||
}
|
||||
if let Some(method) = &self.remove_mfa_method {
|
||||
write!(f, " REMOVE MFA METHOD {method}")?;
|
||||
}
|
||||
if let Some(modify) = &self.modify_mfa_method {
|
||||
let method = &modify.method;
|
||||
let comment = &modify.comment;
|
||||
write!(
|
||||
f,
|
||||
" MODIFY MFA METHOD {method} SET COMMENT '{}'",
|
||||
value::escape_single_quote_string(comment)
|
||||
)?;
|
||||
}
|
||||
if let Some(add_mfa_method_otp) = &self.add_mfa_method_otp {
|
||||
write!(f, " ADD MFA METHOD OTP")?;
|
||||
if let Some(count) = &add_mfa_method_otp.count {
|
||||
write!(f, " COUNT = {count}")?;
|
||||
}
|
||||
}
|
||||
if let Some(policy) = &self.set_policy {
|
||||
let policy_kind = &policy.policy_kind;
|
||||
let name = &policy.policy;
|
||||
write!(f, " SET {policy_kind} POLICY {name}")?;
|
||||
}
|
||||
if let Some(policy_kind) = &self.unset_policy {
|
||||
write!(f, " UNSET {policy_kind} POLICY")?;
|
||||
}
|
||||
if !self.set_tag.options.is_empty() {
|
||||
write!(f, " SET TAG {}", self.set_tag)?;
|
||||
}
|
||||
if !self.unset_tag.is_empty() {
|
||||
write!(f, " UNSET TAG {}", display_comma_separated(&self.unset_tag))?;
|
||||
}
|
||||
let has_props = !self.set_props.options.is_empty();
|
||||
if has_props {
|
||||
write!(f, " SET")?;
|
||||
write!(f, " {}", &self.set_props)?;
|
||||
}
|
||||
if !self.unset_props.is_empty() {
|
||||
write!(f, " UNSET {}", display_comma_separated(&self.unset_props))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies how to create a new table based on an existing table's schema.
|
||||
/// '''sql
|
||||
/// CREATE TABLE new LIKE old ...
|
||||
|
|
|
|||
|
|
@ -555,6 +555,7 @@ impl Spanned for Statement {
|
|||
Statement::CreateUser(..) => Span::empty(),
|
||||
Statement::AlterSchema(s) => s.span(),
|
||||
Statement::Vacuum(..) => Span::empty(),
|
||||
Statement::AlterUser(..) => Span::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
#[cfg(not(feature = "std"))]
|
||||
use crate::alloc::string::ToString;
|
||||
use crate::ast::helpers::key_value_options::{
|
||||
KeyValueOption, KeyValueOptionType, KeyValueOptions, KeyValueOptionsDelimiter,
|
||||
KeyValueOption, KeyValueOptionKind, KeyValueOptions, KeyValueOptionsDelimiter,
|
||||
};
|
||||
use crate::ast::helpers::stmt_create_database::CreateDatabaseBuilder;
|
||||
use crate::ast::helpers::stmt_create_table::CreateTableBuilder;
|
||||
|
|
@ -30,7 +30,7 @@ use crate::ast::{
|
|||
CopyIntoSnowflakeKind, CreateTableLikeKind, DollarQuotedString, Ident, IdentityParameters,
|
||||
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
|
||||
InitializeKind, ObjectName, ObjectNamePart, RefreshModeKind, RowAccessPolicy, ShowObjects,
|
||||
SqlOption, Statement, StorageSerializationPolicy, TagsColumnOption, WrappedCollection,
|
||||
SqlOption, Statement, StorageSerializationPolicy, TagsColumnOption, Value, WrappedCollection,
|
||||
};
|
||||
use crate::dialect::{Dialect, Precedence};
|
||||
use crate::keywords::Keyword;
|
||||
|
|
@ -1004,19 +1004,19 @@ pub fn parse_create_stage(
|
|||
// [ directoryTableParams ]
|
||||
if parser.parse_keyword(Keyword::DIRECTORY) {
|
||||
parser.expect_token(&Token::Eq)?;
|
||||
directory_table_params = parser.parse_key_value_options(true, &[])?;
|
||||
directory_table_params = parser.parse_key_value_options(true, &[])?.options;
|
||||
}
|
||||
|
||||
// [ file_format]
|
||||
if parser.parse_keyword(Keyword::FILE_FORMAT) {
|
||||
parser.expect_token(&Token::Eq)?;
|
||||
file_format = parser.parse_key_value_options(true, &[])?;
|
||||
file_format = parser.parse_key_value_options(true, &[])?.options;
|
||||
}
|
||||
|
||||
// [ copy_options ]
|
||||
if parser.parse_keyword(Keyword::COPY_OPTIONS) {
|
||||
parser.expect_token(&Token::Eq)?;
|
||||
copy_options = parser.parse_key_value_options(true, &[])?;
|
||||
copy_options = parser.parse_key_value_options(true, &[])?.options;
|
||||
}
|
||||
|
||||
// [ comment ]
|
||||
|
|
@ -1182,7 +1182,7 @@ pub fn parse_copy_into(parser: &mut Parser) -> Result<Statement, ParserError> {
|
|||
// FILE_FORMAT
|
||||
if parser.parse_keyword(Keyword::FILE_FORMAT) {
|
||||
parser.expect_token(&Token::Eq)?;
|
||||
file_format = parser.parse_key_value_options(true, &[])?;
|
||||
file_format = parser.parse_key_value_options(true, &[])?.options;
|
||||
// PARTITION BY
|
||||
} else if parser.parse_keywords(&[Keyword::PARTITION, Keyword::BY]) {
|
||||
partition = Some(Box::new(parser.parse_expr()?))
|
||||
|
|
@ -1220,14 +1220,14 @@ pub fn parse_copy_into(parser: &mut Parser) -> Result<Statement, ParserError> {
|
|||
// COPY OPTIONS
|
||||
} else if parser.parse_keyword(Keyword::COPY_OPTIONS) {
|
||||
parser.expect_token(&Token::Eq)?;
|
||||
copy_options = parser.parse_key_value_options(true, &[])?;
|
||||
copy_options = parser.parse_key_value_options(true, &[])?.options;
|
||||
} else {
|
||||
match parser.next_token().token {
|
||||
Token::SemiColon | Token::EOF => break,
|
||||
Token::Comma => continue,
|
||||
// In `COPY INTO <location>` the copy options do not have a shared key
|
||||
// like in `COPY INTO <table>`
|
||||
Token::Word(key) => copy_options.push(parser.parse_key_value_option(key)?),
|
||||
Token::Word(key) => copy_options.push(parser.parse_key_value_option(&key)?),
|
||||
_ => return parser.expected("another copy option, ; or EOF'", parser.peek_token()),
|
||||
}
|
||||
}
|
||||
|
|
@ -1387,7 +1387,7 @@ fn parse_stage_params(parser: &mut Parser) -> Result<StageParamsObject, ParserEr
|
|||
if parser.parse_keyword(Keyword::CREDENTIALS) {
|
||||
parser.expect_token(&Token::Eq)?;
|
||||
credentials = KeyValueOptions {
|
||||
options: parser.parse_key_value_options(true, &[])?,
|
||||
options: parser.parse_key_value_options(true, &[])?.options,
|
||||
delimiter: KeyValueOptionsDelimiter::Space,
|
||||
};
|
||||
}
|
||||
|
|
@ -1396,7 +1396,7 @@ fn parse_stage_params(parser: &mut Parser) -> Result<StageParamsObject, ParserEr
|
|||
if parser.parse_keyword(Keyword::ENCRYPTION) {
|
||||
parser.expect_token(&Token::Eq)?;
|
||||
encryption = KeyValueOptions {
|
||||
options: parser.parse_key_value_options(true, &[])?,
|
||||
options: parser.parse_key_value_options(true, &[])?.options,
|
||||
delimiter: KeyValueOptionsDelimiter::Space,
|
||||
};
|
||||
}
|
||||
|
|
@ -1431,13 +1431,12 @@ fn parse_session_options(
|
|||
Token::Word(key) => {
|
||||
parser.advance_token();
|
||||
if set {
|
||||
let option = parser.parse_key_value_option(key)?;
|
||||
let option = parser.parse_key_value_option(&key)?;
|
||||
options.push(option);
|
||||
} else {
|
||||
options.push(KeyValueOption {
|
||||
option_name: key.value,
|
||||
option_type: KeyValueOptionType::STRING,
|
||||
value: empty(),
|
||||
option_value: KeyValueOptionKind::Single(Value::Placeholder(empty())),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ define_keywords!(
|
|||
AUDIT,
|
||||
AUTHENTICATION,
|
||||
AUTHORIZATION,
|
||||
AUTHORIZATIONS,
|
||||
AUTO,
|
||||
AUTOEXTEND_SIZE,
|
||||
AUTOINCREMENT,
|
||||
|
|
@ -279,6 +280,8 @@ define_keywords!(
|
|||
DEFAULT,
|
||||
DEFAULTS,
|
||||
DEFAULT_DDL_COLLATION,
|
||||
DEFAULT_MFA_METHOD,
|
||||
DEFAULT_SECONDARY_ROLES,
|
||||
DEFERRABLE,
|
||||
DEFERRED,
|
||||
DEFINE,
|
||||
|
|
@ -286,6 +289,7 @@ define_keywords!(
|
|||
DEFINER,
|
||||
DELAYED,
|
||||
DELAY_KEY_WRITE,
|
||||
DELEGATED,
|
||||
DELETE,
|
||||
DELIMITED,
|
||||
DELIMITER,
|
||||
|
|
@ -314,6 +318,7 @@ define_keywords!(
|
|||
DOY,
|
||||
DROP,
|
||||
DRY,
|
||||
DUO,
|
||||
DUPLICATE,
|
||||
DYNAMIC,
|
||||
EACH,
|
||||
|
|
@ -336,6 +341,7 @@ define_keywords!(
|
|||
ENFORCED,
|
||||
ENGINE,
|
||||
ENGINE_ATTRIBUTE,
|
||||
ENROLL,
|
||||
ENUM,
|
||||
ENUM16,
|
||||
ENUM8,
|
||||
|
|
@ -586,6 +592,7 @@ define_keywords!(
|
|||
METHOD,
|
||||
METRIC,
|
||||
METRICS,
|
||||
MFA,
|
||||
MICROSECOND,
|
||||
MICROSECONDS,
|
||||
MILLENIUM,
|
||||
|
|
@ -685,6 +692,7 @@ define_keywords!(
|
|||
ORDINALITY,
|
||||
ORGANIZATION,
|
||||
OTHER,
|
||||
OTP,
|
||||
OUT,
|
||||
OUTER,
|
||||
OUTPUT,
|
||||
|
|
@ -709,6 +717,7 @@ define_keywords!(
|
|||
PARTITIONED,
|
||||
PARTITIONS,
|
||||
PASSING,
|
||||
PASSKEY,
|
||||
PASSWORD,
|
||||
PAST,
|
||||
PATH,
|
||||
|
|
@ -753,6 +762,7 @@ define_keywords!(
|
|||
PURGE,
|
||||
QUALIFY,
|
||||
QUARTER,
|
||||
QUERIES,
|
||||
QUERY,
|
||||
QUOTE,
|
||||
RAISE,
|
||||
|
|
@ -969,6 +979,7 @@ define_keywords!(
|
|||
TO,
|
||||
TOP,
|
||||
TOTALS,
|
||||
TOTP,
|
||||
TRACE,
|
||||
TRAILING,
|
||||
TRANSACTION,
|
||||
|
|
@ -1067,6 +1078,7 @@ define_keywords!(
|
|||
WITHOUT,
|
||||
WITHOUT_ARRAY_WRAPPER,
|
||||
WORK,
|
||||
WORKLOAD_IDENTITY,
|
||||
WRAPPER,
|
||||
WRITE,
|
||||
XML,
|
||||
|
|
|
|||
|
|
@ -13,13 +13,16 @@
|
|||
//! SQL Parser for ALTER
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc::vec;
|
||||
use alloc::{string::ToString, vec};
|
||||
|
||||
use super::{Parser, ParserError};
|
||||
use crate::{
|
||||
ast::{
|
||||
AlterConnectorOwner, AlterPolicyOperation, AlterRoleOperation, Expr, Password, ResetConfig,
|
||||
RoleOption, SetConfigValue, Statement,
|
||||
helpers::key_value_options::{KeyValueOptions, KeyValueOptionsDelimiter},
|
||||
AlterConnectorOwner, AlterPolicyOperation, AlterRoleOperation, AlterUser,
|
||||
AlterUserAddMfaMethodOtp, AlterUserAddRoleDelegation, AlterUserModifyMfaMethod,
|
||||
AlterUserRemoveRoleDelegation, AlterUserSetPolicy, Expr, MfaMethodKind, Password,
|
||||
ResetConfig, RoleOption, SetConfigValue, Statement, UserPolicyKind,
|
||||
},
|
||||
dialect::{MsSqlDialect, PostgreSqlDialect},
|
||||
keywords::Keyword,
|
||||
|
|
@ -140,6 +143,189 @@ impl Parser<'_> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Parse an `ALTER USER` statement
|
||||
/// ```sql
|
||||
/// ALTER USER [ IF EXISTS ] [ <name> ] [ OPTIONS ]
|
||||
/// ```
|
||||
pub fn parse_alter_user(&mut self) -> Result<Statement, ParserError> {
|
||||
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
|
||||
let name = self.parse_identifier()?;
|
||||
let rename_to = if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) {
|
||||
Some(self.parse_identifier()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let reset_password = self.parse_keywords(&[Keyword::RESET, Keyword::PASSWORD]);
|
||||
let abort_all_queries =
|
||||
self.parse_keywords(&[Keyword::ABORT, Keyword::ALL, Keyword::QUERIES]);
|
||||
let add_role_delegation = if self.parse_keywords(&[
|
||||
Keyword::ADD,
|
||||
Keyword::DELEGATED,
|
||||
Keyword::AUTHORIZATION,
|
||||
Keyword::OF,
|
||||
Keyword::ROLE,
|
||||
]) {
|
||||
let role = self.parse_identifier()?;
|
||||
self.expect_keywords(&[Keyword::TO, Keyword::SECURITY, Keyword::INTEGRATION])?;
|
||||
let integration = self.parse_identifier()?;
|
||||
Some(AlterUserAddRoleDelegation { role, integration })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let remove_role_delegation = if self.parse_keywords(&[Keyword::REMOVE, Keyword::DELEGATED])
|
||||
{
|
||||
let role = if self.parse_keywords(&[Keyword::AUTHORIZATION, Keyword::OF, Keyword::ROLE])
|
||||
{
|
||||
Some(self.parse_identifier()?)
|
||||
} else if self.parse_keyword(Keyword::AUTHORIZATIONS) {
|
||||
None
|
||||
} else {
|
||||
return self.expected(
|
||||
"REMOVE DELEGATED AUTHORIZATION OF ROLE | REMOVE DELEGATED AUTHORIZATIONS",
|
||||
self.peek_token(),
|
||||
);
|
||||
};
|
||||
self.expect_keywords(&[Keyword::FROM, Keyword::SECURITY, Keyword::INTEGRATION])?;
|
||||
let integration = self.parse_identifier()?;
|
||||
Some(AlterUserRemoveRoleDelegation { role, integration })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let enroll_mfa = self.parse_keywords(&[Keyword::ENROLL, Keyword::MFA]);
|
||||
let set_default_mfa_method =
|
||||
if self.parse_keywords(&[Keyword::SET, Keyword::DEFAULT_MFA_METHOD]) {
|
||||
Some(self.parse_mfa_method()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let remove_mfa_method =
|
||||
if self.parse_keywords(&[Keyword::REMOVE, Keyword::MFA, Keyword::METHOD]) {
|
||||
Some(self.parse_mfa_method()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let modify_mfa_method =
|
||||
if self.parse_keywords(&[Keyword::MODIFY, Keyword::MFA, Keyword::METHOD]) {
|
||||
let method = self.parse_mfa_method()?;
|
||||
self.expect_keywords(&[Keyword::SET, Keyword::COMMENT])?;
|
||||
let comment = self.parse_literal_string()?;
|
||||
Some(AlterUserModifyMfaMethod { method, comment })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let add_mfa_method_otp =
|
||||
if self.parse_keywords(&[Keyword::ADD, Keyword::MFA, Keyword::METHOD, Keyword::OTP]) {
|
||||
let count = if self.parse_keyword(Keyword::COUNT) {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
Some(self.parse_value()?.into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(AlterUserAddMfaMethodOtp { count })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let set_policy =
|
||||
if self.parse_keywords(&[Keyword::SET, Keyword::AUTHENTICATION, Keyword::POLICY]) {
|
||||
Some(AlterUserSetPolicy {
|
||||
policy_kind: UserPolicyKind::Authentication,
|
||||
policy: self.parse_identifier()?,
|
||||
})
|
||||
} else if self.parse_keywords(&[Keyword::SET, Keyword::PASSWORD, Keyword::POLICY]) {
|
||||
Some(AlterUserSetPolicy {
|
||||
policy_kind: UserPolicyKind::Password,
|
||||
policy: self.parse_identifier()?,
|
||||
})
|
||||
} else if self.parse_keywords(&[Keyword::SET, Keyword::SESSION, Keyword::POLICY]) {
|
||||
Some(AlterUserSetPolicy {
|
||||
policy_kind: UserPolicyKind::Session,
|
||||
policy: self.parse_identifier()?,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let unset_policy =
|
||||
if self.parse_keywords(&[Keyword::UNSET, Keyword::AUTHENTICATION, Keyword::POLICY]) {
|
||||
Some(UserPolicyKind::Authentication)
|
||||
} else if self.parse_keywords(&[Keyword::UNSET, Keyword::PASSWORD, Keyword::POLICY]) {
|
||||
Some(UserPolicyKind::Password)
|
||||
} else if self.parse_keywords(&[Keyword::UNSET, Keyword::SESSION, Keyword::POLICY]) {
|
||||
Some(UserPolicyKind::Session)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let set_tag = if self.parse_keywords(&[Keyword::SET, Keyword::TAG]) {
|
||||
self.parse_key_value_options(false, &[])?
|
||||
} else {
|
||||
KeyValueOptions {
|
||||
delimiter: KeyValueOptionsDelimiter::Comma,
|
||||
options: vec![],
|
||||
}
|
||||
};
|
||||
|
||||
let unset_tag = if self.parse_keywords(&[Keyword::UNSET, Keyword::TAG]) {
|
||||
self.parse_comma_separated(Parser::parse_identifier)?
|
||||
.iter()
|
||||
.map(|i| i.to_string())
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let set_props = if self.parse_keyword(Keyword::SET) {
|
||||
self.parse_key_value_options(false, &[])?
|
||||
} else {
|
||||
KeyValueOptions {
|
||||
delimiter: KeyValueOptionsDelimiter::Comma,
|
||||
options: vec![],
|
||||
}
|
||||
};
|
||||
|
||||
let unset_props = if self.parse_keyword(Keyword::UNSET) {
|
||||
self.parse_comma_separated(Parser::parse_identifier)?
|
||||
.iter()
|
||||
.map(|i| i.to_string())
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
Ok(Statement::AlterUser(AlterUser {
|
||||
if_exists,
|
||||
name,
|
||||
rename_to,
|
||||
reset_password,
|
||||
abort_all_queries,
|
||||
add_role_delegation,
|
||||
remove_role_delegation,
|
||||
enroll_mfa,
|
||||
set_default_mfa_method,
|
||||
remove_mfa_method,
|
||||
modify_mfa_method,
|
||||
add_mfa_method_otp,
|
||||
set_policy,
|
||||
unset_policy,
|
||||
set_tag,
|
||||
unset_tag,
|
||||
set_props,
|
||||
unset_props,
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_mfa_method(&mut self) -> Result<MfaMethodKind, ParserError> {
|
||||
if self.parse_keyword(Keyword::PASSKEY) {
|
||||
Ok(MfaMethodKind::PassKey)
|
||||
} else if self.parse_keyword(Keyword::TOTP) {
|
||||
Ok(MfaMethodKind::Totp)
|
||||
} else if self.parse_keyword(Keyword::DUO) {
|
||||
Ok(MfaMethodKind::Duo)
|
||||
} else {
|
||||
self.expected("PASSKEY, TOTP or DUO", self.peek_token())
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_mssql_alter_role(&mut self) -> Result<Statement, ParserError> {
|
||||
let role_name = self.parse_identifier()?;
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ use IsOptional::*;
|
|||
|
||||
use crate::ast::helpers::{
|
||||
key_value_options::{
|
||||
KeyValueOption, KeyValueOptionType, KeyValueOptions, KeyValueOptionsDelimiter,
|
||||
KeyValueOption, KeyValueOptionKind, KeyValueOptions, KeyValueOptionsDelimiter,
|
||||
},
|
||||
stmt_create_table::{CreateTableBuilder, CreateTableConfiguration},
|
||||
};
|
||||
|
|
@ -4796,10 +4796,12 @@ impl<'a> Parser<'a> {
|
|||
fn parse_create_user(&mut self, or_replace: bool) -> Result<Statement, ParserError> {
|
||||
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
|
||||
let name = self.parse_identifier()?;
|
||||
let options = self.parse_key_value_options(false, &[Keyword::WITH, Keyword::TAG])?;
|
||||
let options = self
|
||||
.parse_key_value_options(false, &[Keyword::WITH, Keyword::TAG])?
|
||||
.options;
|
||||
let with_tags = self.parse_keyword(Keyword::WITH);
|
||||
let tags = if self.parse_keyword(Keyword::TAG) {
|
||||
self.parse_key_value_options(true, &[])?
|
||||
self.parse_key_value_options(true, &[])?.options
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
|
@ -9277,6 +9279,7 @@ impl<'a> Parser<'a> {
|
|||
Keyword::CONNECTOR,
|
||||
Keyword::ICEBERG,
|
||||
Keyword::SCHEMA,
|
||||
Keyword::USER,
|
||||
])?;
|
||||
match object_type {
|
||||
Keyword::SCHEMA => {
|
||||
|
|
@ -9312,6 +9315,7 @@ impl<'a> Parser<'a> {
|
|||
Keyword::ROLE => self.parse_alter_role(),
|
||||
Keyword::POLICY => self.parse_alter_policy(),
|
||||
Keyword::CONNECTOR => self.parse_alter_connector(),
|
||||
Keyword::USER => self.parse_alter_user(),
|
||||
// unreachable because expect_one_of_keywords used above
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
|
@ -17488,8 +17492,9 @@ impl<'a> Parser<'a> {
|
|||
&mut self,
|
||||
parenthesized: bool,
|
||||
end_words: &[Keyword],
|
||||
) -> Result<Vec<KeyValueOption>, ParserError> {
|
||||
) -> Result<KeyValueOptions, ParserError> {
|
||||
let mut options: Vec<KeyValueOption> = Vec::new();
|
||||
let mut delimiter = KeyValueOptionsDelimiter::Space;
|
||||
if parenthesized {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
}
|
||||
|
|
@ -17503,9 +17508,12 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
Token::EOF => break,
|
||||
Token::Comma => continue,
|
||||
Token::Comma => {
|
||||
delimiter = KeyValueOptionsDelimiter::Comma;
|
||||
continue;
|
||||
}
|
||||
Token::Word(w) if !end_words.contains(&w.keyword) => {
|
||||
options.push(self.parse_key_value_option(w)?)
|
||||
options.push(self.parse_key_value_option(&w)?)
|
||||
}
|
||||
Token::Word(w) if end_words.contains(&w.keyword) => {
|
||||
self.prev_token();
|
||||
|
|
@ -17514,40 +17522,67 @@ impl<'a> Parser<'a> {
|
|||
_ => return self.expected("another option, EOF, Comma or ')'", self.peek_token()),
|
||||
};
|
||||
}
|
||||
Ok(options)
|
||||
|
||||
Ok(KeyValueOptions { delimiter, options })
|
||||
}
|
||||
|
||||
/// Parses a `KEY = VALUE` construct based on the specified key
|
||||
pub(crate) fn parse_key_value_option(
|
||||
&mut self,
|
||||
key: Word,
|
||||
key: &Word,
|
||||
) -> Result<KeyValueOption, ParserError> {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
match self.next_token().token {
|
||||
Token::SingleQuotedString(value) => Ok(KeyValueOption {
|
||||
option_name: key.value,
|
||||
option_type: KeyValueOptionType::STRING,
|
||||
value,
|
||||
match self.peek_token().token {
|
||||
Token::SingleQuotedString(_) => Ok(KeyValueOption {
|
||||
option_name: key.value.clone(),
|
||||
option_value: KeyValueOptionKind::Single(self.parse_value()?.into()),
|
||||
}),
|
||||
Token::Word(word)
|
||||
if word.keyword == Keyword::TRUE || word.keyword == Keyword::FALSE =>
|
||||
{
|
||||
Ok(KeyValueOption {
|
||||
option_name: key.value,
|
||||
option_type: KeyValueOptionType::BOOLEAN,
|
||||
value: word.value.to_uppercase(),
|
||||
option_name: key.value.clone(),
|
||||
option_value: KeyValueOptionKind::Single(self.parse_value()?.into()),
|
||||
})
|
||||
}
|
||||
Token::Word(word) => Ok(KeyValueOption {
|
||||
option_name: key.value,
|
||||
option_type: KeyValueOptionType::ENUM,
|
||||
value: word.value,
|
||||
}),
|
||||
Token::Number(n, _) => Ok(KeyValueOption {
|
||||
option_name: key.value,
|
||||
option_type: KeyValueOptionType::NUMBER,
|
||||
value: n,
|
||||
Token::Number(..) => Ok(KeyValueOption {
|
||||
option_name: key.value.clone(),
|
||||
option_value: KeyValueOptionKind::Single(self.parse_value()?.into()),
|
||||
}),
|
||||
Token::Word(word) => {
|
||||
self.next_token();
|
||||
Ok(KeyValueOption {
|
||||
option_name: key.value.clone(),
|
||||
option_value: KeyValueOptionKind::Single(Value::Placeholder(
|
||||
word.value.clone(),
|
||||
)),
|
||||
})
|
||||
}
|
||||
Token::LParen => {
|
||||
// Can be a list of values or a list of key value properties.
|
||||
// Try to parse a list of values and if that fails, try to parse
|
||||
// a list of key-value properties.
|
||||
match self.maybe_parse(|parser| {
|
||||
parser.expect_token(&Token::LParen)?;
|
||||
let values = parser.parse_comma_separated0(|p| p.parse_value(), Token::RParen);
|
||||
parser.expect_token(&Token::RParen)?;
|
||||
values
|
||||
})? {
|
||||
Some(values) => {
|
||||
let values = values.into_iter().map(|v| v.value).collect();
|
||||
Ok(KeyValueOption {
|
||||
option_name: key.value.clone(),
|
||||
option_value: KeyValueOptionKind::Multi(values),
|
||||
})
|
||||
}
|
||||
None => Ok(KeyValueOption {
|
||||
option_name: key.value.clone(),
|
||||
option_value: KeyValueOptionKind::KeyValueOptions(Box::new(
|
||||
self.parse_key_value_options(true, &[])?,
|
||||
)),
|
||||
}),
|
||||
}
|
||||
}
|
||||
_ => self.expected("expected option value", self.peek_token()),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue