Skip to content

Commit 2ed2cbe

Browse files
authored
Snowflake: Add support for CREATE USER (#1950)
1 parent 4921846 commit 2ed2cbe

File tree

6 files changed

+281
-85
lines changed

6 files changed

+281
-85
lines changed

src/ast/helpers/key_value_options.rs

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

34+
use crate::ast::display_separated;
35+
3436
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3537
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3638
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
3739
pub struct KeyValueOptions {
3840
pub options: Vec<KeyValueOption>,
41+
pub delimiter: KeyValueOptionsDelimiter,
42+
}
43+
44+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
45+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
46+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
47+
pub enum KeyValueOptionsDelimiter {
48+
Space,
49+
Comma,
3950
}
4051

4152
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -59,18 +70,11 @@ pub struct KeyValueOption {
5970

6071
impl fmt::Display for KeyValueOptions {
6172
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
62-
if !self.options.is_empty() {
63-
let mut first = false;
64-
for option in &self.options {
65-
if !first {
66-
first = true;
67-
} else {
68-
f.write_str(" ")?;
69-
}
70-
write!(f, "{option}")?;
71-
}
72-
}
73-
Ok(())
73+
let sep = match self.delimiter {
74+
KeyValueOptionsDelimiter::Space => " ",
75+
KeyValueOptionsDelimiter::Comma => ", ",
76+
};
77+
write!(f, "{}", display_separated(&self.options, sep))
7478
}
7579
}
7680

src/ast/mod.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4355,6 +4355,11 @@ pub enum Statement {
43554355
///
43564356
/// See [ReturnStatement]
43574357
Return(ReturnStatement),
4358+
/// ```sql
4359+
/// CREATE [OR REPLACE] USER <user> [IF NOT EXISTS]
4360+
/// ```
4361+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-user)
4362+
CreateUser(CreateUser),
43584363
}
43594364

43604365
/// ```sql
@@ -6193,6 +6198,7 @@ impl fmt::Display for Statement {
61936198
Statement::Return(r) => write!(f, "{r}"),
61946199
Statement::List(command) => write!(f, "LIST {command}"),
61956200
Statement::Remove(command) => write!(f, "REMOVE {command}"),
6201+
Statement::CreateUser(s) => write!(f, "{s}"),
61966202
}
61976203
}
61986204
}
@@ -10125,6 +10131,50 @@ impl fmt::Display for MemberOf {
1012510131
}
1012610132
}
1012710133

10134+
/// Creates a user
10135+
///
10136+
/// Syntax:
10137+
/// ```sql
10138+
/// CREATE [OR REPLACE] USER [IF NOT EXISTS] <name> [OPTIONS]
10139+
/// ```
10140+
///
10141+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-user)
10142+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10143+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10144+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10145+
pub struct CreateUser {
10146+
pub or_replace: bool,
10147+
pub if_not_exists: bool,
10148+
pub name: Ident,
10149+
pub options: KeyValueOptions,
10150+
pub with_tags: bool,
10151+
pub tags: KeyValueOptions,
10152+
}
10153+
10154+
impl fmt::Display for CreateUser {
10155+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10156+
write!(f, "CREATE")?;
10157+
if self.or_replace {
10158+
write!(f, " OR REPLACE")?;
10159+
}
10160+
write!(f, " USER")?;
10161+
if self.if_not_exists {
10162+
write!(f, " IF NOT EXISTS")?;
10163+
}
10164+
write!(f, " {}", self.name)?;
10165+
if !self.options.options.is_empty() {
10166+
write!(f, " {}", self.options)?;
10167+
}
10168+
if !self.tags.options.is_empty() {
10169+
if self.with_tags {
10170+
write!(f, " WITH")?;
10171+
}
10172+
write!(f, " TAG ({})", self.tags)?;
10173+
}
10174+
Ok(())
10175+
}
10176+
}
10177+
1012810178
#[cfg(test)]
1012910179
mod tests {
1013010180
use crate::tokenizer::Location;

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ impl Spanned for Statement {
531531
Statement::Print { .. } => Span::empty(),
532532
Statement::Return { .. } => Span::empty(),
533533
Statement::List(..) | Statement::Remove(..) => Span::empty(),
534+
Statement::CreateUser(..) => Span::empty(),
534535
}
535536
}
536537
}

src/dialect/snowflake.rs

Lines changed: 37 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
#[cfg(not(feature = "std"))]
1919
use crate::alloc::string::ToString;
20-
use crate::ast::helpers::key_value_options::{KeyValueOption, KeyValueOptionType, KeyValueOptions};
20+
use crate::ast::helpers::key_value_options::{
21+
KeyValueOption, KeyValueOptionType, KeyValueOptions, KeyValueOptionsDelimiter,
22+
};
2123
use crate::ast::helpers::stmt_create_table::CreateTableBuilder;
2224
use crate::ast::helpers::stmt_data_loading::{
2325
FileStagingCommand, StageLoadSelectItem, StageLoadSelectItemKind, StageParamsObject,
@@ -31,7 +33,7 @@ use crate::ast::{
3133
use crate::dialect::{Dialect, Precedence};
3234
use crate::keywords::Keyword;
3335
use crate::parser::{IsOptional, Parser, ParserError};
34-
use crate::tokenizer::{Token, Word};
36+
use crate::tokenizer::Token;
3537
#[cfg(not(feature = "std"))]
3638
use alloc::boxed::Box;
3739
#[cfg(not(feature = "std"))]
@@ -516,6 +518,7 @@ fn parse_alter_session(parser: &mut Parser, set: bool) -> Result<Statement, Pars
516518
set,
517519
session_params: KeyValueOptions {
518520
options: session_options,
521+
delimiter: KeyValueOptionsDelimiter::Space,
519522
},
520523
})
521524
}
@@ -777,19 +780,19 @@ pub fn parse_create_stage(
777780
// [ directoryTableParams ]
778781
if parser.parse_keyword(Keyword::DIRECTORY) {
779782
parser.expect_token(&Token::Eq)?;
780-
directory_table_params = parse_parentheses_options(parser)?;
783+
directory_table_params = parser.parse_key_value_options(true, &[])?;
781784
}
782785

783786
// [ file_format]
784787
if parser.parse_keyword(Keyword::FILE_FORMAT) {
785788
parser.expect_token(&Token::Eq)?;
786-
file_format = parse_parentheses_options(parser)?;
789+
file_format = parser.parse_key_value_options(true, &[])?;
787790
}
788791

789792
// [ copy_options ]
790793
if parser.parse_keyword(Keyword::COPY_OPTIONS) {
791794
parser.expect_token(&Token::Eq)?;
792-
copy_options = parse_parentheses_options(parser)?;
795+
copy_options = parser.parse_key_value_options(true, &[])?;
793796
}
794797

795798
// [ comment ]
@@ -806,12 +809,15 @@ pub fn parse_create_stage(
806809
stage_params,
807810
directory_table_params: KeyValueOptions {
808811
options: directory_table_params,
812+
delimiter: KeyValueOptionsDelimiter::Space,
809813
},
810814
file_format: KeyValueOptions {
811815
options: file_format,
816+
delimiter: KeyValueOptionsDelimiter::Space,
812817
},
813818
copy_options: KeyValueOptions {
814819
options: copy_options,
820+
delimiter: KeyValueOptionsDelimiter::Space,
815821
},
816822
comment,
817823
})
@@ -879,10 +885,16 @@ pub fn parse_copy_into(parser: &mut Parser) -> Result<Statement, ParserError> {
879885
let mut from_stage = None;
880886
let mut stage_params = StageParamsObject {
881887
url: None,
882-
encryption: KeyValueOptions { options: vec![] },
888+
encryption: KeyValueOptions {
889+
options: vec![],
890+
delimiter: KeyValueOptionsDelimiter::Space,
891+
},
883892
endpoint: None,
884893
storage_integration: None,
885-
credentials: KeyValueOptions { options: vec![] },
894+
credentials: KeyValueOptions {
895+
options: vec![],
896+
delimiter: KeyValueOptionsDelimiter::Space,
897+
},
886898
};
887899
let mut from_query = None;
888900
let mut partition = None;
@@ -944,7 +956,7 @@ pub fn parse_copy_into(parser: &mut Parser) -> Result<Statement, ParserError> {
944956
// FILE_FORMAT
945957
if parser.parse_keyword(Keyword::FILE_FORMAT) {
946958
parser.expect_token(&Token::Eq)?;
947-
file_format = parse_parentheses_options(parser)?;
959+
file_format = parser.parse_key_value_options(true, &[])?;
948960
// PARTITION BY
949961
} else if parser.parse_keywords(&[Keyword::PARTITION, Keyword::BY]) {
950962
partition = Some(Box::new(parser.parse_expr()?))
@@ -982,14 +994,14 @@ pub fn parse_copy_into(parser: &mut Parser) -> Result<Statement, ParserError> {
982994
// COPY OPTIONS
983995
} else if parser.parse_keyword(Keyword::COPY_OPTIONS) {
984996
parser.expect_token(&Token::Eq)?;
985-
copy_options = parse_parentheses_options(parser)?;
997+
copy_options = parser.parse_key_value_options(true, &[])?;
986998
} else {
987999
match parser.next_token().token {
9881000
Token::SemiColon | Token::EOF => break,
9891001
Token::Comma => continue,
9901002
// In `COPY INTO <location>` the copy options do not have a shared key
9911003
// like in `COPY INTO <table>`
992-
Token::Word(key) => copy_options.push(parse_option(parser, key)?),
1004+
Token::Word(key) => copy_options.push(parser.parse_key_value_option(key)?),
9931005
_ => return parser.expected("another copy option, ; or EOF'", parser.peek_token()),
9941006
}
9951007
}
@@ -1008,9 +1020,11 @@ pub fn parse_copy_into(parser: &mut Parser) -> Result<Statement, ParserError> {
10081020
pattern,
10091021
file_format: KeyValueOptions {
10101022
options: file_format,
1023+
delimiter: KeyValueOptionsDelimiter::Space,
10111024
},
10121025
copy_options: KeyValueOptions {
10131026
options: copy_options,
1027+
delimiter: KeyValueOptionsDelimiter::Space,
10141028
},
10151029
validation_mode,
10161030
partition,
@@ -1110,8 +1124,14 @@ fn parse_select_item_for_data_load(
11101124

11111125
fn parse_stage_params(parser: &mut Parser) -> Result<StageParamsObject, ParserError> {
11121126
let (mut url, mut storage_integration, mut endpoint) = (None, None, None);
1113-
let mut encryption: KeyValueOptions = KeyValueOptions { options: vec![] };
1114-
let mut credentials: KeyValueOptions = KeyValueOptions { options: vec![] };
1127+
let mut encryption: KeyValueOptions = KeyValueOptions {
1128+
options: vec![],
1129+
delimiter: KeyValueOptionsDelimiter::Space,
1130+
};
1131+
let mut credentials: KeyValueOptions = KeyValueOptions {
1132+
options: vec![],
1133+
delimiter: KeyValueOptionsDelimiter::Space,
1134+
};
11151135

11161136
// URL
11171137
if parser.parse_keyword(Keyword::URL) {
@@ -1141,15 +1161,17 @@ fn parse_stage_params(parser: &mut Parser) -> Result<StageParamsObject, ParserEr
11411161
if parser.parse_keyword(Keyword::CREDENTIALS) {
11421162
parser.expect_token(&Token::Eq)?;
11431163
credentials = KeyValueOptions {
1144-
options: parse_parentheses_options(parser)?,
1164+
options: parser.parse_key_value_options(true, &[])?,
1165+
delimiter: KeyValueOptionsDelimiter::Space,
11451166
};
11461167
}
11471168

11481169
// ENCRYPTION
11491170
if parser.parse_keyword(Keyword::ENCRYPTION) {
11501171
parser.expect_token(&Token::Eq)?;
11511172
encryption = KeyValueOptions {
1152-
options: parse_parentheses_options(parser)?,
1173+
options: parser.parse_key_value_options(true, &[])?,
1174+
delimiter: KeyValueOptionsDelimiter::Space,
11531175
};
11541176
}
11551177

@@ -1183,7 +1205,7 @@ fn parse_session_options(
11831205
Token::Word(key) => {
11841206
parser.advance_token();
11851207
if set {
1186-
let option = parse_option(parser, key)?;
1208+
let option = parser.parse_key_value_option(key)?;
11871209
options.push(option);
11881210
} else {
11891211
options.push(KeyValueOption {
@@ -1207,63 +1229,6 @@ fn parse_session_options(
12071229
}
12081230
}
12091231

1210-
/// Parses options provided within parentheses like:
1211-
/// ( ENABLE = { TRUE | FALSE }
1212-
/// [ AUTO_REFRESH = { TRUE | FALSE } ]
1213-
/// [ REFRESH_ON_CREATE = { TRUE | FALSE } ]
1214-
/// [ NOTIFICATION_INTEGRATION = '<notification_integration_name>' ] )
1215-
///
1216-
fn parse_parentheses_options(parser: &mut Parser) -> Result<Vec<KeyValueOption>, ParserError> {
1217-
let mut options: Vec<KeyValueOption> = Vec::new();
1218-
parser.expect_token(&Token::LParen)?;
1219-
loop {
1220-
match parser.next_token().token {
1221-
Token::RParen => break,
1222-
Token::Comma => continue,
1223-
Token::Word(key) => options.push(parse_option(parser, key)?),
1224-
_ => return parser.expected("another option or ')'", parser.peek_token()),
1225-
};
1226-
}
1227-
Ok(options)
1228-
}
1229-
1230-
/// Parses a `KEY = VALUE` construct based on the specified key
1231-
fn parse_option(parser: &mut Parser, key: Word) -> Result<KeyValueOption, ParserError> {
1232-
parser.expect_token(&Token::Eq)?;
1233-
if parser.parse_keyword(Keyword::TRUE) {
1234-
Ok(KeyValueOption {
1235-
option_name: key.value,
1236-
option_type: KeyValueOptionType::BOOLEAN,
1237-
value: "TRUE".to_string(),
1238-
})
1239-
} else if parser.parse_keyword(Keyword::FALSE) {
1240-
Ok(KeyValueOption {
1241-
option_name: key.value,
1242-
option_type: KeyValueOptionType::BOOLEAN,
1243-
value: "FALSE".to_string(),
1244-
})
1245-
} else {
1246-
match parser.next_token().token {
1247-
Token::SingleQuotedString(value) => Ok(KeyValueOption {
1248-
option_name: key.value,
1249-
option_type: KeyValueOptionType::STRING,
1250-
value,
1251-
}),
1252-
Token::Word(word) => Ok(KeyValueOption {
1253-
option_name: key.value,
1254-
option_type: KeyValueOptionType::ENUM,
1255-
value: word.value,
1256-
}),
1257-
Token::Number(n, _) => Ok(KeyValueOption {
1258-
option_name: key.value,
1259-
option_type: KeyValueOptionType::NUMBER,
1260-
value: n,
1261-
}),
1262-
_ => parser.expected("expected option value", parser.peek_token()),
1263-
}
1264-
}
1265-
}
1266-
12671232
/// Parsing a property of identity or autoincrement column option
12681233
/// Syntax:
12691234
/// ```sql

0 commit comments

Comments
 (0)