This is an automated email from the ASF dual-hosted git repository.
iffyio pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
The following commit(s) were added to refs/heads/main by this push:
new 3ace97c0 Implement SnowFlake ALTER SESSION (#1712)
3ace97c0 is described below
commit 3ace97c0efd46f8128590f88392ac30d3e99feae
Author: Artem Osipov <[email protected]>
AuthorDate: Fri Feb 21 09:22:24 2025 +0300
Implement SnowFlake ALTER SESSION (#1712)
---
src/ast/helpers/key_value_options.rs | 89 +++++++++++++++
src/ast/helpers/mod.rs | 1 +
src/ast/helpers/stmt_data_loading.rs | 65 +----------
src/ast/mod.rs | 49 +++++++--
src/ast/spans.rs | 1 +
src/dialect/snowflake.rs | 121 ++++++++++++++++-----
src/keywords.rs | 1 +
tests/sqlparser_snowflake.rs | 205 ++++++++++++++++++-----------------
8 files changed, 335 insertions(+), 197 deletions(-)
diff --git a/src/ast/helpers/key_value_options.rs
b/src/ast/helpers/key_value_options.rs
new file mode 100644
index 00000000..06f028dd
--- /dev/null
+++ b/src/ast/helpers/key_value_options.rs
@@ -0,0 +1,89 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Key-value options for SQL statements.
+//! 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 core::fmt;
+use core::fmt::Formatter;
+
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+
+#[cfg(feature = "visitor")]
+use sqlparser_derive::{Visit, VisitMut};
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct KeyValueOptions {
+ pub options: Vec<KeyValueOption>,
+}
+
+#[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,
+}
+
+#[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,
+}
+
+impl fmt::Display for KeyValueOptions {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ if !self.options.is_empty() {
+ let mut first = false;
+ for option in &self.options {
+ if !first {
+ first = true;
+ } else {
+ f.write_str(" ")?;
+ }
+ write!(f, "{}", option)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+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)?;
+ }
+ KeyValueOptionType::ENUM | KeyValueOptionType::BOOLEAN |
KeyValueOptionType::NUMBER => {
+ write!(f, "{}={}", self.option_name, self.value)?;
+ }
+ }
+ Ok(())
+ }
+}
diff --git a/src/ast/helpers/mod.rs b/src/ast/helpers/mod.rs
index a96bffc5..55831220 100644
--- a/src/ast/helpers/mod.rs
+++ b/src/ast/helpers/mod.rs
@@ -15,5 +15,6 @@
// specific language governing permissions and limitations
// under the License.
pub mod attached_token;
+pub mod key_value_options;
pub mod stmt_create_table;
pub mod stmt_data_loading;
diff --git a/src/ast/helpers/stmt_data_loading.rs
b/src/ast/helpers/stmt_data_loading.rs
index 77de5d9e..cc4fa12f 100644
--- a/src/ast/helpers/stmt_data_loading.rs
+++ b/src/ast/helpers/stmt_data_loading.rs
@@ -24,11 +24,11 @@ use alloc::string::String;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::fmt;
-use core::fmt::Formatter;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
+use crate::ast::helpers::key_value_options::KeyValueOptions;
use crate::ast::{Ident, ObjectName};
#[cfg(feature = "visitor")]
use sqlparser_derive::{Visit, VisitMut};
@@ -38,36 +38,10 @@ use sqlparser_derive::{Visit, VisitMut};
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct StageParamsObject {
pub url: Option<String>,
- pub encryption: DataLoadingOptions,
+ pub encryption: KeyValueOptions,
pub endpoint: Option<String>,
pub storage_integration: Option<String>,
- pub credentials: DataLoadingOptions,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
-pub struct DataLoadingOptions {
- pub options: Vec<DataLoadingOption>,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
-pub enum DataLoadingOptionType {
- STRING,
- BOOLEAN,
- ENUM,
- NUMBER,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
-pub struct DataLoadingOption {
- pub option_name: String,
- pub option_type: DataLoadingOptionType,
- pub value: String,
+ pub credentials: KeyValueOptions,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -106,39 +80,6 @@ impl fmt::Display for StageParamsObject {
}
}
-impl fmt::Display for DataLoadingOptions {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- if !self.options.is_empty() {
- let mut first = false;
- for option in &self.options {
- if !first {
- first = true;
- } else {
- f.write_str(" ")?;
- }
- write!(f, "{}", option)?;
- }
- }
- Ok(())
- }
-}
-
-impl fmt::Display for DataLoadingOption {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self.option_type {
- DataLoadingOptionType::STRING => {
- write!(f, "{}='{}'", self.option_name, self.value)?;
- }
- DataLoadingOptionType::ENUM
- | DataLoadingOptionType::BOOLEAN
- | DataLoadingOptionType::NUMBER => {
- write!(f, "{}={}", self.option_name, self.value)?;
- }
- }
- Ok(())
- }
-}
-
impl fmt::Display for StageLoadSelectItem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.alias.is_some() {
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index dc50871c..2b9016d9 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -89,9 +89,8 @@ pub use self::value::{
NormalizationForm, TrimWhereField, Value,
};
-use crate::ast::helpers::stmt_data_loading::{
- DataLoadingOptions, StageLoadSelectItem, StageParamsObject,
-};
+use crate::ast::helpers::key_value_options::KeyValueOptions;
+use crate::ast::helpers::stmt_data_loading::{StageLoadSelectItem,
StageParamsObject};
#[cfg(feature = "visitor")]
pub use visitor::*;
@@ -2504,8 +2503,8 @@ pub enum Statement {
from_query: Option<Box<Query>>,
files: Option<Vec<String>>,
pattern: Option<String>,
- file_format: DataLoadingOptions,
- copy_options: DataLoadingOptions,
+ file_format: KeyValueOptions,
+ copy_options: KeyValueOptions,
validation_mode: Option<String>,
partition: Option<Box<Expr>>,
},
@@ -2713,6 +2712,17 @@ pub enum Statement {
owner: Option<ddl::AlterConnectorOwner>,
},
/// ```sql
+ /// ALTER SESSION SET sessionParam
+ /// ALTER SESSION UNSET <param_name> [ , <param_name> , ... ]
+ /// ```
+ /// See <https://docs.snowflake.com/en/sql-reference/sql/alter-session>
+ AlterSession {
+ /// true is to set for the session parameters, false is to unset
+ set: bool,
+ /// The session parameters to set or unset
+ session_params: KeyValueOptions,
+ },
+ /// ```sql
/// ATTACH DATABASE 'path/to/file' AS alias
/// ```
/// (SQLite-specific)
@@ -3240,9 +3250,9 @@ pub enum Statement {
if_not_exists: bool,
name: ObjectName,
stage_params: StageParamsObject,
- directory_table_params: DataLoadingOptions,
- file_format: DataLoadingOptions,
- copy_options: DataLoadingOptions,
+ directory_table_params: KeyValueOptions,
+ file_format: KeyValueOptions,
+ copy_options: KeyValueOptions,
comment: Option<String>,
},
/// ```sql
@@ -4467,6 +4477,29 @@ impl fmt::Display for Statement {
}
Ok(())
}
+ Statement::AlterSession {
+ set,
+ session_params,
+ } => {
+ write!(
+ f,
+ "ALTER SESSION {set}",
+ set = if *set { "SET" } else { "UNSET" }
+ )?;
+ if !session_params.options.is_empty() {
+ if *set {
+ write!(f, " {}", session_params)?;
+ } else {
+ let options = session_params
+ .options
+ .iter()
+ .map(|p| p.option_name.clone())
+ .collect::<Vec<_>>();
+ write!(f, " {}", display_separated(&options, ", "))?;
+ }
+ }
+ Ok(())
+ }
Statement::Drop {
object_type,
if_exists,
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index de39e50d..3de41902 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -430,6 +430,7 @@ impl Spanned for Statement {
// These statements need to be implemented
Statement::AlterType { .. } => Span::empty(),
Statement::AlterRole { .. } => Span::empty(),
+ Statement::AlterSession { .. } => Span::empty(),
Statement::AttachDatabase { .. } => Span::empty(),
Statement::AttachDuckDBDatabase { .. } => Span::empty(),
Statement::DetachDuckDBDatabase { .. } => Span::empty(),
diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs
index 6702b94b..72252b27 100644
--- a/src/dialect/snowflake.rs
+++ b/src/dialect/snowflake.rs
@@ -17,10 +17,10 @@
#[cfg(not(feature = "std"))]
use crate::alloc::string::ToString;
+use crate::ast::helpers::key_value_options::{KeyValueOption,
KeyValueOptionType, KeyValueOptions};
use crate::ast::helpers::stmt_create_table::CreateTableBuilder;
use crate::ast::helpers::stmt_data_loading::{
- DataLoadingOption, DataLoadingOptionType, DataLoadingOptions,
FileStagingCommand,
- StageLoadSelectItem, StageParamsObject,
+ FileStagingCommand, StageLoadSelectItem, StageParamsObject,
};
use crate::ast::{
ColumnOption, ColumnPolicy, ColumnPolicyProperty, CopyIntoSnowflakeKind,
Ident,
@@ -130,6 +130,16 @@ impl Dialect for SnowflakeDialect {
}
fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement,
ParserError>> {
+ if parser.parse_keywords(&[Keyword::ALTER, Keyword::SESSION]) {
+ // ALTER SESSION
+ let set = match parser.parse_one_of_keywords(&[Keyword::SET,
Keyword::UNSET]) {
+ Some(Keyword::SET) => true,
+ Some(Keyword::UNSET) => false,
+ _ => return Some(parser.expected("SET or UNSET",
parser.peek_token())),
+ };
+ return Some(parse_alter_session(parser, set));
+ }
+
if parser.parse_keyword(Keyword::CREATE) {
// possibly CREATE STAGE
//[ OR REPLACE ]
@@ -358,6 +368,18 @@ fn parse_file_staging_command(kw: Keyword, parser: &mut
Parser) -> Result<Statem
}
}
+/// Parse snowflake alter session.
+/// <https://docs.snowflake.com/en/sql-reference/sql/alter-session>
+fn parse_alter_session(parser: &mut Parser, set: bool) -> Result<Statement,
ParserError> {
+ let session_options = parse_session_options(parser, set)?;
+ Ok(Statement::AlterSession {
+ set,
+ session_params: KeyValueOptions {
+ options: session_options,
+ },
+ })
+}
+
/// Parse snowflake create table statement.
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
/// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
@@ -634,13 +656,13 @@ pub fn parse_create_stage(
if_not_exists,
name,
stage_params,
- directory_table_params: DataLoadingOptions {
+ directory_table_params: KeyValueOptions {
options: directory_table_params,
},
- file_format: DataLoadingOptions {
+ file_format: KeyValueOptions {
options: file_format,
},
- copy_options: DataLoadingOptions {
+ copy_options: KeyValueOptions {
options: copy_options,
},
comment,
@@ -708,10 +730,10 @@ pub fn parse_copy_into(parser: &mut Parser) ->
Result<Statement, ParserError> {
let mut from_stage = None;
let mut stage_params = StageParamsObject {
url: None,
- encryption: DataLoadingOptions { options: vec![] },
+ encryption: KeyValueOptions { options: vec![] },
endpoint: None,
storage_integration: None,
- credentials: DataLoadingOptions { options: vec![] },
+ credentials: KeyValueOptions { options: vec![] },
};
let mut from_query = None;
let mut partition = None;
@@ -818,7 +840,7 @@ pub fn parse_copy_into(parser: &mut Parser) ->
Result<Statement, ParserError> {
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(parse_copy_option(parser, key)?),
+ Token::Word(key) => copy_options.push(parse_option(parser,
key)?),
_ => return parser.expected("another copy option, ; or EOF'",
parser.peek_token()),
}
}
@@ -834,10 +856,10 @@ pub fn parse_copy_into(parser: &mut Parser) ->
Result<Statement, ParserError> {
from_query,
files: if files.is_empty() { None } else { Some(files) },
pattern,
- file_format: DataLoadingOptions {
+ file_format: KeyValueOptions {
options: file_format,
},
- copy_options: DataLoadingOptions {
+ copy_options: KeyValueOptions {
options: copy_options,
},
validation_mode,
@@ -931,8 +953,8 @@ fn parse_select_items_for_data_load(
fn parse_stage_params(parser: &mut Parser) -> Result<StageParamsObject,
ParserError> {
let (mut url, mut storage_integration, mut endpoint) = (None, None, None);
- let mut encryption: DataLoadingOptions = DataLoadingOptions { options:
vec![] };
- let mut credentials: DataLoadingOptions = DataLoadingOptions { options:
vec![] };
+ let mut encryption: KeyValueOptions = KeyValueOptions { options: vec![] };
+ let mut credentials: KeyValueOptions = KeyValueOptions { options: vec![] };
// URL
if parser.parse_keyword(Keyword::URL) {
@@ -961,7 +983,7 @@ fn parse_stage_params(parser: &mut Parser) ->
Result<StageParamsObject, ParserEr
// CREDENTIALS
if parser.parse_keyword(Keyword::CREDENTIALS) {
parser.expect_token(&Token::Eq)?;
- credentials = DataLoadingOptions {
+ credentials = KeyValueOptions {
options: parse_parentheses_options(parser)?,
};
}
@@ -969,7 +991,7 @@ fn parse_stage_params(parser: &mut Parser) ->
Result<StageParamsObject, ParserEr
// ENCRYPTION
if parser.parse_keyword(Keyword::ENCRYPTION) {
parser.expect_token(&Token::Eq)?;
- encryption = DataLoadingOptions {
+ encryption = KeyValueOptions {
options: parse_parentheses_options(parser)?,
};
}
@@ -983,20 +1005,63 @@ fn parse_stage_params(parser: &mut Parser) ->
Result<StageParamsObject, ParserEr
})
}
+/// Parses options separated by blank spaces, commas, or new lines like:
+/// ABORT_DETACHED_QUERY = { TRUE | FALSE }
+/// [ ACTIVE_PYTHON_PROFILER = { 'LINE' | 'MEMORY' } ]
+/// [ BINARY_INPUT_FORMAT = '\<string\>' ]
+fn parse_session_options(
+ parser: &mut Parser,
+ set: bool,
+) -> Result<Vec<KeyValueOption>, ParserError> {
+ let mut options: Vec<KeyValueOption> = Vec::new();
+ let empty = String::new;
+ loop {
+ match parser.next_token().token {
+ Token::Comma => continue,
+ Token::Word(key) => {
+ if set {
+ let option = parse_option(parser, key)?;
+ options.push(option);
+ } else {
+ options.push(KeyValueOption {
+ option_name: key.value,
+ option_type: KeyValueOptionType::STRING,
+ value: empty(),
+ });
+ }
+ }
+ _ => {
+ if parser.peek_token().token == Token::EOF {
+ break;
+ }
+ return parser.expected("another option", parser.peek_token());
+ }
+ }
+ }
+ options
+ .is_empty()
+ .then(|| {
+ Err(ParserError::ParserError(
+ "expected at least one option".to_string(),
+ ))
+ })
+ .unwrap_or(Ok(options))
+}
+
/// Parses options provided within parentheses like:
/// ( ENABLE = { TRUE | FALSE }
/// [ AUTO_REFRESH = { TRUE | FALSE } ]
/// [ REFRESH_ON_CREATE = { TRUE | FALSE } ]
/// [ NOTIFICATION_INTEGRATION = '<notification_integration_name>' ] )
///
-fn parse_parentheses_options(parser: &mut Parser) ->
Result<Vec<DataLoadingOption>, ParserError> {
- let mut options: Vec<DataLoadingOption> = Vec::new();
+fn parse_parentheses_options(parser: &mut Parser) ->
Result<Vec<KeyValueOption>, ParserError> {
+ let mut options: Vec<KeyValueOption> = Vec::new();
parser.expect_token(&Token::LParen)?;
loop {
match parser.next_token().token {
Token::RParen => break,
Token::Comma => continue,
- Token::Word(key) => options.push(parse_copy_option(parser, key)?),
+ Token::Word(key) => options.push(parse_option(parser, key)?),
_ => return parser.expected("another option or ')'",
parser.peek_token()),
};
}
@@ -1004,35 +1069,35 @@ fn parse_parentheses_options(parser: &mut Parser) ->
Result<Vec<DataLoadingOptio
}
/// Parses a `KEY = VALUE` construct based on the specified key
-fn parse_copy_option(parser: &mut Parser, key: Word) ->
Result<DataLoadingOption, ParserError> {
+fn parse_option(parser: &mut Parser, key: Word) -> Result<KeyValueOption,
ParserError> {
parser.expect_token(&Token::Eq)?;
if parser.parse_keyword(Keyword::TRUE) {
- Ok(DataLoadingOption {
+ Ok(KeyValueOption {
option_name: key.value,
- option_type: DataLoadingOptionType::BOOLEAN,
+ option_type: KeyValueOptionType::BOOLEAN,
value: "TRUE".to_string(),
})
} else if parser.parse_keyword(Keyword::FALSE) {
- Ok(DataLoadingOption {
+ Ok(KeyValueOption {
option_name: key.value,
- option_type: DataLoadingOptionType::BOOLEAN,
+ option_type: KeyValueOptionType::BOOLEAN,
value: "FALSE".to_string(),
})
} else {
match parser.next_token().token {
- Token::SingleQuotedString(value) => Ok(DataLoadingOption {
+ Token::SingleQuotedString(value) => Ok(KeyValueOption {
option_name: key.value,
- option_type: DataLoadingOptionType::STRING,
+ option_type: KeyValueOptionType::STRING,
value,
}),
- Token::Word(word) => Ok(DataLoadingOption {
+ Token::Word(word) => Ok(KeyValueOption {
option_name: key.value,
- option_type: DataLoadingOptionType::ENUM,
+ option_type: KeyValueOptionType::ENUM,
value: word.value,
}),
- Token::Number(n, _) => Ok(DataLoadingOption {
+ Token::Number(n, _) => Ok(KeyValueOption {
option_name: key.value,
- option_type: DataLoadingOptionType::NUMBER,
+ option_type: KeyValueOptionType::NUMBER,
value: n,
}),
_ => parser.expected("expected option value", parser.peek_token()),
diff --git a/src/keywords.rs b/src/keywords.rs
index 46c40fba..d62a038b 100644
--- a/src/keywords.rs
+++ b/src/keywords.rs
@@ -913,6 +913,7 @@ define_keywords!(
UNNEST,
UNPIVOT,
UNSAFE,
+ UNSET,
UNSIGNED,
UNTIL,
UPDATE,
diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs
index 76550b8e..12796bb6 100644
--- a/tests/sqlparser_snowflake.rs
+++ b/tests/sqlparser_snowflake.rs
@@ -19,9 +19,8 @@
//! Test SQL syntax specific to Snowflake. The parser based on the
//! generic dialect is also tested (on the inputs it can handle).
-use sqlparser::ast::helpers::stmt_data_loading::{
- DataLoadingOption, DataLoadingOptionType, StageLoadSelectItem,
-};
+use sqlparser::ast::helpers::key_value_options::{KeyValueOption,
KeyValueOptionType};
+use sqlparser::ast::helpers::stmt_data_loading::StageLoadSelectItem;
use sqlparser::ast::*;
use sqlparser::dialect::{Dialect, GenericDialect, SnowflakeDialect};
use sqlparser::parser::{ParserError, ParserOptions};
@@ -1914,38 +1913,26 @@ fn test_create_stage_with_stage_params() {
"<s3_api_compatible_endpoint>",
stage_params.endpoint.unwrap()
);
- assert!(stage_params
- .credentials
- .options
- .contains(&DataLoadingOption {
- option_name: "AWS_KEY_ID".to_string(),
- option_type: DataLoadingOptionType::STRING,
- value: "1a2b3c".to_string()
- }));
- assert!(stage_params
- .credentials
- .options
- .contains(&DataLoadingOption {
- option_name: "AWS_SECRET_KEY".to_string(),
- option_type: DataLoadingOptionType::STRING,
- value: "4x5y6z".to_string()
- }));
- assert!(stage_params
- .encryption
- .options
- .contains(&DataLoadingOption {
- option_name: "MASTER_KEY".to_string(),
- option_type: DataLoadingOptionType::STRING,
- value: "key".to_string()
- }));
- assert!(stage_params
- .encryption
- .options
- .contains(&DataLoadingOption {
- option_name: "TYPE".to_string(),
- option_type: DataLoadingOptionType::STRING,
- value: "AWS_SSE_KMS".to_string()
- }));
+ assert!(stage_params.credentials.options.contains(&KeyValueOption {
+ option_name: "AWS_KEY_ID".to_string(),
+ option_type: KeyValueOptionType::STRING,
+ value: "1a2b3c".to_string()
+ }));
+ assert!(stage_params.credentials.options.contains(&KeyValueOption {
+ option_name: "AWS_SECRET_KEY".to_string(),
+ option_type: KeyValueOptionType::STRING,
+ value: "4x5y6z".to_string()
+ }));
+ assert!(stage_params.encryption.options.contains(&KeyValueOption {
+ option_name: "MASTER_KEY".to_string(),
+ option_type: KeyValueOptionType::STRING,
+ value: "key".to_string()
+ }));
+ assert!(stage_params.encryption.options.contains(&KeyValueOption {
+ option_name: "TYPE".to_string(),
+ option_type: KeyValueOptionType::STRING,
+ value: "AWS_SSE_KMS".to_string()
+ }));
}
_ => unreachable!(),
};
@@ -1966,19 +1953,19 @@ fn test_create_stage_with_directory_table_params() {
directory_table_params,
..
} => {
- assert!(directory_table_params.options.contains(&DataLoadingOption
{
+ assert!(directory_table_params.options.contains(&KeyValueOption {
option_name: "ENABLE".to_string(),
- option_type: DataLoadingOptionType::BOOLEAN,
+ option_type: KeyValueOptionType::BOOLEAN,
value: "TRUE".to_string()
}));
- assert!(directory_table_params.options.contains(&DataLoadingOption
{
+ assert!(directory_table_params.options.contains(&KeyValueOption {
option_name: "REFRESH_ON_CREATE".to_string(),
- option_type: DataLoadingOptionType::BOOLEAN,
+ option_type: KeyValueOptionType::BOOLEAN,
value: "FALSE".to_string()
}));
- assert!(directory_table_params.options.contains(&DataLoadingOption
{
+ assert!(directory_table_params.options.contains(&KeyValueOption {
option_name: "NOTIFICATION_INTEGRATION".to_string(),
- option_type: DataLoadingOptionType::STRING,
+ option_type: KeyValueOptionType::STRING,
value: "some-string".to_string()
}));
}
@@ -1997,19 +1984,19 @@ fn test_create_stage_with_file_format() {
match snowflake_without_unescape().verified_stmt(sql) {
Statement::CreateStage { file_format, .. } => {
- assert!(file_format.options.contains(&DataLoadingOption {
+ assert!(file_format.options.contains(&KeyValueOption {
option_name: "COMPRESSION".to_string(),
- option_type: DataLoadingOptionType::ENUM,
+ option_type: KeyValueOptionType::ENUM,
value: "AUTO".to_string()
}));
- assert!(file_format.options.contains(&DataLoadingOption {
+ assert!(file_format.options.contains(&KeyValueOption {
option_name: "BINARY_FORMAT".to_string(),
- option_type: DataLoadingOptionType::ENUM,
+ option_type: KeyValueOptionType::ENUM,
value: "HEX".to_string()
}));
- assert!(file_format.options.contains(&DataLoadingOption {
+ assert!(file_format.options.contains(&KeyValueOption {
option_name: "ESCAPE".to_string(),
- option_type: DataLoadingOptionType::STRING,
+ option_type: KeyValueOptionType::STRING,
value: r#"\\"#.to_string()
}));
}
@@ -2030,14 +2017,14 @@ fn test_create_stage_with_copy_options() {
);
match snowflake().verified_stmt(sql) {
Statement::CreateStage { copy_options, .. } => {
- assert!(copy_options.options.contains(&DataLoadingOption {
+ assert!(copy_options.options.contains(&KeyValueOption {
option_name: "ON_ERROR".to_string(),
- option_type: DataLoadingOptionType::ENUM,
+ option_type: KeyValueOptionType::ENUM,
value: "CONTINUE".to_string()
}));
- assert!(copy_options.options.contains(&DataLoadingOption {
+ assert!(copy_options.options.contains(&KeyValueOption {
option_name: "FORCE".to_string(),
- option_type: DataLoadingOptionType::BOOLEAN,
+ option_type: KeyValueOptionType::BOOLEAN,
value: "TRUE".to_string()
}));
}
@@ -2167,38 +2154,26 @@ fn test_copy_into_with_stage_params() {
"<s3_api_compatible_endpoint>",
stage_params.endpoint.unwrap()
);
- assert!(stage_params
- .credentials
- .options
- .contains(&DataLoadingOption {
- option_name: "AWS_KEY_ID".to_string(),
- option_type: DataLoadingOptionType::STRING,
- value: "1a2b3c".to_string()
- }));
- assert!(stage_params
- .credentials
- .options
- .contains(&DataLoadingOption {
- option_name: "AWS_SECRET_KEY".to_string(),
- option_type: DataLoadingOptionType::STRING,
- value: "4x5y6z".to_string()
- }));
- assert!(stage_params
- .encryption
- .options
- .contains(&DataLoadingOption {
- option_name: "MASTER_KEY".to_string(),
- option_type: DataLoadingOptionType::STRING,
- value: "key".to_string()
- }));
- assert!(stage_params
- .encryption
- .options
- .contains(&DataLoadingOption {
- option_name: "TYPE".to_string(),
- option_type: DataLoadingOptionType::STRING,
- value: "AWS_SSE_KMS".to_string()
- }));
+ assert!(stage_params.credentials.options.contains(&KeyValueOption {
+ option_name: "AWS_KEY_ID".to_string(),
+ option_type: KeyValueOptionType::STRING,
+ value: "1a2b3c".to_string()
+ }));
+ assert!(stage_params.credentials.options.contains(&KeyValueOption {
+ option_name: "AWS_SECRET_KEY".to_string(),
+ option_type: KeyValueOptionType::STRING,
+ value: "4x5y6z".to_string()
+ }));
+ assert!(stage_params.encryption.options.contains(&KeyValueOption {
+ option_name: "MASTER_KEY".to_string(),
+ option_type: KeyValueOptionType::STRING,
+ value: "key".to_string()
+ }));
+ assert!(stage_params.encryption.options.contains(&KeyValueOption {
+ option_name: "TYPE".to_string(),
+ option_type: KeyValueOptionType::STRING,
+ value: "AWS_SSE_KMS".to_string()
+ }));
}
_ => unreachable!(),
};
@@ -2326,19 +2301,19 @@ fn test_copy_into_file_format() {
match snowflake_without_unescape().verified_stmt(sql) {
Statement::CopyIntoSnowflake { file_format, .. } => {
- assert!(file_format.options.contains(&DataLoadingOption {
+ assert!(file_format.options.contains(&KeyValueOption {
option_name: "COMPRESSION".to_string(),
- option_type: DataLoadingOptionType::ENUM,
+ option_type: KeyValueOptionType::ENUM,
value: "AUTO".to_string()
}));
- assert!(file_format.options.contains(&DataLoadingOption {
+ assert!(file_format.options.contains(&KeyValueOption {
option_name: "BINARY_FORMAT".to_string(),
- option_type: DataLoadingOptionType::ENUM,
+ option_type: KeyValueOptionType::ENUM,
value: "HEX".to_string()
}));
- assert!(file_format.options.contains(&DataLoadingOption {
+ assert!(file_format.options.contains(&KeyValueOption {
option_name: "ESCAPE".to_string(),
- option_type: DataLoadingOptionType::STRING,
+ option_type: KeyValueOptionType::STRING,
value: r#"\\"#.to_string()
}));
}
@@ -2365,19 +2340,19 @@ fn test_copy_into_file_format() {
.unwrap()
{
Statement::CopyIntoSnowflake { file_format, .. } => {
- assert!(file_format.options.contains(&DataLoadingOption {
+ assert!(file_format.options.contains(&KeyValueOption {
option_name: "COMPRESSION".to_string(),
- option_type: DataLoadingOptionType::ENUM,
+ option_type: KeyValueOptionType::ENUM,
value: "AUTO".to_string()
}));
- assert!(file_format.options.contains(&DataLoadingOption {
+ assert!(file_format.options.contains(&KeyValueOption {
option_name: "BINARY_FORMAT".to_string(),
- option_type: DataLoadingOptionType::ENUM,
+ option_type: KeyValueOptionType::ENUM,
value: "HEX".to_string()
}));
- assert!(file_format.options.contains(&DataLoadingOption {
+ assert!(file_format.options.contains(&KeyValueOption {
option_name: "ESCAPE".to_string(),
- option_type: DataLoadingOptionType::STRING,
+ option_type: KeyValueOptionType::STRING,
value: r#"\\"#.to_string()
}));
}
@@ -2397,14 +2372,14 @@ fn test_copy_into_copy_options() {
match snowflake().verified_stmt(sql) {
Statement::CopyIntoSnowflake { copy_options, .. } => {
- assert!(copy_options.options.contains(&DataLoadingOption {
+ assert!(copy_options.options.contains(&KeyValueOption {
option_name: "ON_ERROR".to_string(),
- option_type: DataLoadingOptionType::ENUM,
+ option_type: KeyValueOptionType::ENUM,
value: "CONTINUE".to_string()
}));
- assert!(copy_options.options.contains(&DataLoadingOption {
+ assert!(copy_options.options.contains(&KeyValueOption {
option_name: "FORCE".to_string(),
- option_type: DataLoadingOptionType::BOOLEAN,
+ option_type: KeyValueOptionType::BOOLEAN,
value: "TRUE".to_string()
}));
}
@@ -3475,3 +3450,35 @@ fn test_grant_database_role_to() {
snowflake_and_generic().verified_stmt("GRANT DATABASE ROLE r1 TO ROLE r2");
snowflake_and_generic().verified_stmt("GRANT DATABASE ROLE db1.sc1.r1 TO
ROLE db1.sc1.r2");
}
+
+#[test]
+fn test_alter_session() {
+ assert_eq!(
+ snowflake()
+ .parse_sql_statements("ALTER SESSION SET")
+ .unwrap_err()
+ .to_string(),
+ "sql parser error: expected at least one option"
+ );
+ assert_eq!(
+ snowflake()
+ .parse_sql_statements("ALTER SESSION UNSET")
+ .unwrap_err()
+ .to_string(),
+ "sql parser error: expected at least one option"
+ );
+
+ snowflake().verified_stmt("ALTER SESSION SET AUTOCOMMIT=TRUE");
+ snowflake().verified_stmt("ALTER SESSION SET AUTOCOMMIT=FALSE
QUERY_TAG='tag'");
+ snowflake().verified_stmt("ALTER SESSION UNSET AUTOCOMMIT");
+ snowflake().verified_stmt("ALTER SESSION UNSET AUTOCOMMIT, QUERY_TAG");
+ snowflake().one_statement_parses_to(
+ "ALTER SESSION SET A=false, B='tag';",
+ "ALTER SESSION SET A=FALSE B='tag'",
+ );
+ snowflake().one_statement_parses_to(
+ "ALTER SESSION SET A=true \nB='tag'",
+ "ALTER SESSION SET A=TRUE B='tag'",
+ );
+ snowflake().one_statement_parses_to("ALTER SESSION UNSET a\nB", "ALTER
SESSION UNSET a, B");
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]