From bc1e867f118090c0c9780e0311382df62cd82ef4 Mon Sep 17 00:00:00 2001 From: Lukasz Stefaniak Date: Tue, 26 Mar 2024 10:57:29 +0100 Subject: [PATCH 1/3] bigquery: ALTER TABLE SET OPTIONS --- src/ast/ddl.rs | 10 +++++++++- src/ast/value.rs | 11 ++++++++--- src/parser/mod.rs | 13 +++++++++++++ tests/sqlparser_bigquery.rs | 5 +++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 13cddf566..03277ef7e 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -99,10 +99,15 @@ pub enum AlterTableOperation { column_name: Ident, op: AlterColumnOperation, }, - /// 'SWAP WITH ' + /// `SWAP WITH ` /// /// Note: this is Snowflake specific SwapWith { table_name: ObjectName }, + + /// `SET OPTIONS(table_set_options_list)` + /// + /// Note: this is BigQuery specific + SetOptions { options: Vec }, } #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] @@ -213,6 +218,9 @@ impl fmt::Display for AlterTableOperation { AlterTableOperation::SwapWith { table_name } => { write!(f, "SWAP WITH {table_name}") } + AlterTableOperation::SetOptions { options } => { + write!(f, "SET OPTIONS({})", display_comma_separated(options)) + } } } } diff --git a/src/ast/value.rs b/src/ast/value.rs index bb51002b8..14d88699c 100644 --- a/src/ast/value.rs +++ b/src/ast/value.rs @@ -20,7 +20,7 @@ use bigdecimal::BigDecimal; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use crate::ast::Expr; +use crate::ast::{display_comma_separated, Expr}; #[cfg(feature = "visitor")] use sqlparser_derive::{Visit, VisitMut}; @@ -79,6 +79,9 @@ pub enum Value { /// https://docs.snowflake.com/en/sql-reference/data-types-semistructured#object-constants ObjectConstant(Vec), Array(Vec), + /// TUPLE as used by BigQuery + /// ("org_unit", "development") + Tuple(Vec), } impl fmt::Display for Value { @@ -116,8 +119,10 @@ impl fmt::Display for Value { } } Value::Array(values) => { - let collected: Vec = values.iter().map(|v| format!("{}", v)).collect(); - write!(f, "[{}]", collected.join(", ")) + write!(f, "[{}]", display_comma_separated(values)) + } + Value::Tuple(values) => { + write!(f, "({})", display_comma_separated(values)) } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 926fc200e..5d8a32ec9 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -4836,6 +4836,11 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::WITH)?; let table_name = self.parse_object_name()?; AlterTableOperation::SwapWith { table_name } + } else if self.parse_keyword(Keyword::SET) { + self.expect_keyword(Keyword::OPTIONS)?; + self.prev_token(); + let options = self.parse_options(Keyword::OPTIONS)?; + AlterTableOperation::SetOptions { options } } else { return self.expected( "ADD, RENAME, PARTITION, SWAP or DROP after ALTER TABLE", @@ -5231,6 +5236,14 @@ impl<'a> Parser<'a> { self.expect_token(&Token::RBracket)?; Ok(Value::Array(fields)) } + Token::LParen => { + let values = self.parse_comma_separated(Parser::parse_value)?; + if values.len() == 1 { + Ok(values.into_iter().next().unwrap()) + } else { + Ok(Value::Tuple(values)) + } + } unexpected => self.expected( "a value", TokenWithLocation { diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 0286a48cb..ec21fc306 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -1292,3 +1292,8 @@ fn test_create_view_options() { "CREATE VIEW `myproject`.`mydataset`.`newview` OPTIONS(friendly_name = \"newview\", description = \"a view that expires in 2 days\") AS SELECT col_1 FROM `myproject`.`mydataset`.`mytable`", ); } + +#[test] +fn test_alter_table_set_options() { + bigquery().verified_stmt("ALTER TABLE tbl SET OPTIONS(description = \"Desc.\")"); +} From a0824d6c41b150aa4ced9e7a92819e4e7d9483d1 Mon Sep 17 00:00:00 2001 From: Lukasz Stefaniak Date: Tue, 26 Mar 2024 11:12:16 +0100 Subject: [PATCH 2/3] bigquery: ALTER VIEW SET OPTIONS --- src/ast/mod.rs | 14 +++++++++++--- src/parser/mod.rs | 15 +++++++++++++-- tests/sqlparser_bigquery.rs | 5 +++++ tests/sqlparser_common.rs | 8 ++++++-- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index b27752fc0..8eea94e66 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1695,14 +1695,15 @@ pub enum Statement { name: ObjectName, operation: AlterIndexOperation, }, - /// ALTER VIEW + /// ALTER VIEW AS AlterView { /// View name #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))] name: ObjectName, columns: Vec>, - query: Box, + query: Option, with_options: Vec, + set_options: Vec, }, /// ALTER ROLE AlterRole { @@ -3030,6 +3031,7 @@ impl fmt::Display for Statement { columns, query, with_options, + set_options, } => { write!(f, "ALTER VIEW {name}")?; if !with_options.is_empty() { @@ -3038,7 +3040,13 @@ impl fmt::Display for Statement { if !columns.is_empty() { write!(f, " ({})", display_comma_separated(columns))?; } - write!(f, " AS {query}") + if let Some(query) = query { + write!(f, " AS {query}")?; + } + if !set_options.is_empty() { + write!(f, " SET OPTIONS ({})", display_comma_separated(set_options))?; + } + Ok(()) } Statement::AlterRole { name, operation } => { write!(f, "ALTER ROLE {name} {operation}") diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 5d8a32ec9..dfcd8c8b2 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -4901,14 +4901,25 @@ impl<'a> Parser<'a> { let with_options = self.parse_options(Keyword::WITH)?; - self.expect_keyword(Keyword::AS)?; - let query = Box::new(self.parse_query()?); + let query = if self.parse_keyword(Keyword::AS) { + Some(self.parse_query()?) + } else { + None + }; + + let set_options = if self.parse_keywords(&[Keyword::SET, Keyword::OPTIONS]) { + self.prev_token(); + self.parse_options(Keyword::OPTIONS)? + } else { + vec![] + }; Ok(Statement::AlterView { name, columns, query, with_options, + set_options, }) } diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index ec21fc306..d4cd3493f 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -1297,3 +1297,8 @@ fn test_create_view_options() { fn test_alter_table_set_options() { bigquery().verified_stmt("ALTER TABLE tbl SET OPTIONS(description = \"Desc.\")"); } + +#[test] +fn test_alter_view_set_options() { + bigquery().verified_stmt("ALTER VIEW tbl SET OPTIONS (description = \"Desc.\")"); +} diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index ced15b79d..eba766961 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -3231,11 +3231,13 @@ fn parse_alter_view() { columns, query, with_options, + set_options, } => { assert_eq!("myschema.myview", name.to_string()); assert_eq!(Vec::>::new(), columns); - assert_eq!("SELECT foo FROM bar", query.to_string()); + assert_eq!("SELECT foo FROM bar", query.unwrap().to_string()); assert_eq!(with_options, vec![]); + assert_eq!(set_options, vec![]); } _ => unreachable!(), } @@ -3273,6 +3275,7 @@ fn parse_alter_view_with_columns() { columns, query, with_options, + set_options, } => { assert_eq!("v", name.to_string()); assert_eq!( @@ -3282,8 +3285,9 @@ fn parse_alter_view_with_columns() { Ident::new("cols").empty_span(), ] ); - assert_eq!("SELECT 1, 2", query.to_string()); + assert_eq!("SELECT 1, 2", query.unwrap().to_string()); assert_eq!(with_options, vec![]); + assert_eq!(set_options, vec![]); } _ => unreachable!(), } From f66f248dcc26738b883321a17b0c33d161e9f568 Mon Sep 17 00:00:00 2001 From: Lukasz Stefaniak Date: Tue, 26 Mar 2024 11:18:02 +0100 Subject: [PATCH 3/3] bigquery: ALTER SCHEMA SET OPTIONS --- src/ast/mod.rs | 14 ++++++++++++++ src/parser/mod.rs | 13 +++++++++++++ tests/sqlparser_bigquery.rs | 6 ++++++ 3 files changed, 33 insertions(+) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 8eea94e66..f82dc2f2c 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1705,6 +1705,13 @@ pub enum Statement { with_options: Vec, set_options: Vec, }, + /// ALTER SCHEMA + AlterSchema { + /// View name + #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))] + name: ObjectName, + set_options: Vec, + }, /// ALTER ROLE AlterRole { name: Ident, @@ -3048,6 +3055,13 @@ impl fmt::Display for Statement { } Ok(()) } + Statement::AlterSchema { name, set_options } => { + write!(f, "ALTER SCHEMA {name}")?; + if !set_options.is_empty() { + write!(f, " SET OPTIONS ({})", display_comma_separated(set_options))?; + } + Ok(()) + } Statement::AlterRole { name, operation } => { write!(f, "ALTER ROLE {name} {operation}") } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index dfcd8c8b2..7189ccde4 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -4856,6 +4856,7 @@ impl<'a> Parser<'a> { Keyword::TABLE, Keyword::INDEX, Keyword::ROLE, + Keyword::SCHEMA, ])?; match object_type { Keyword::VIEW => self.parse_alter_view(), @@ -4890,6 +4891,7 @@ impl<'a> Parser<'a> { }) } Keyword::ROLE => self.parse_alter_role(), + Keyword::SCHEMA => self.parse_alter_schema(), // unreachable because expect_one_of_keywords used above _ => unreachable!(), } @@ -4923,6 +4925,17 @@ impl<'a> Parser<'a> { }) } + pub fn parse_alter_schema(&mut self) -> Result { + let name = self.parse_object_name()?; + + self.expect_keywords(&[Keyword::SET, Keyword::OPTIONS])?; + self.prev_token(); + + let set_options = self.parse_options(Keyword::OPTIONS)?; + + Ok(Statement::AlterSchema { name, set_options }) + } + /// Parse a copy statement pub fn parse_copy(&mut self) -> Result { let source; diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index d4cd3493f..e40f5a41a 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -1302,3 +1302,9 @@ fn test_alter_table_set_options() { fn test_alter_view_set_options() { bigquery().verified_stmt("ALTER VIEW tbl SET OPTIONS (description = \"Desc.\")"); } + +#[test] +fn test_alter_schema_set_options() { + bigquery() + .verified_stmt("ALTER SCHEMA mydataset SET OPTIONS (default_table_expiration_days = 3.75)"); +}