diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 9d535a7f2..c720c7aad 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -26,6 +26,7 @@ use sqlparser_derive::{Visit, VisitMut}; use crate::ast::value::escape_single_quote_string; use crate::ast::{ display_comma_separated, display_separated, DataType, Expr, Ident, ObjectName, SequenceOptions, + SqlOption, }; use crate::tokenizer::Token; @@ -634,6 +635,42 @@ impl fmt::Display for ColumnDef { } } +/// Column definition specified in a `CREATE VIEW` statement. +/// +/// Syntax +/// ```markdown +/// [OPTIONS(option, ...)] +/// +/// option: = +/// ``` +/// +/// Examples: +/// ```sql +/// name +/// age OPTIONS(description = "age column", tag = "prod") +/// ``` +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub struct ViewColumnDef { + pub name: Ident, + pub options: Option>, +} + +impl fmt::Display for ViewColumnDef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.name)?; + if let Some(options) = self.options.as_ref() { + write!( + f, + " OPTIONS({})", + display_comma_separated(options.as_slice()) + )?; + } + Ok(()) + } +} + /// An optionally-named `ColumnOption`: `[ CONSTRAINT ] `. /// /// Note that implementations are substantially more permissive than the ANSI @@ -710,6 +747,14 @@ pub enum ColumnOption { /// false if 'GENERATED ALWAYS' is skipped (option starts with AS) generated_keyword: bool, }, + /// BigQuery specific: Explicit column options in a view [1] or table [2] + /// Syntax + /// ```sql + /// OPTIONS(description="field desc") + /// ``` + /// [1]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#view_column_option_list + /// [2]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#column_option_list + Options(Vec), } impl fmt::Display for ColumnOption { @@ -788,6 +833,9 @@ impl fmt::Display for ColumnOption { Ok(()) } } + Options(options) => { + write!(f, "OPTIONS({})", display_comma_separated(options)) + } } } } diff --git a/src/ast/helpers/stmt_create_table.rs b/src/ast/helpers/stmt_create_table.rs index 17327e7f8..126542379 100644 --- a/src/ast/helpers/stmt_create_table.rs +++ b/src/ast/helpers/stmt_create_table.rs @@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize}; use sqlparser_derive::{Visit, VisitMut}; use crate::ast::{ - ColumnDef, FileFormat, HiveDistributionStyle, HiveFormat, Ident, ObjectName, OnCommit, Query, - SqlOption, Statement, TableConstraint, + ColumnDef, Expr, FileFormat, HiveDistributionStyle, HiveFormat, Ident, ObjectName, OnCommit, + Query, SqlOption, Statement, TableConstraint, }; use crate::parser::ParserError; @@ -72,6 +72,9 @@ pub struct CreateTableBuilder { pub on_commit: Option, pub on_cluster: Option, pub order_by: Option>, + pub partition_by: Option>, + pub cluster_by: Option>, + pub options: Option>, pub strict: bool, } @@ -105,6 +108,9 @@ impl CreateTableBuilder { on_commit: None, on_cluster: None, order_by: None, + partition_by: None, + cluster_by: None, + options: None, strict: false, } } @@ -236,6 +242,21 @@ impl CreateTableBuilder { self } + pub fn partition_by(mut self, partition_by: Option>) -> Self { + self.partition_by = partition_by; + self + } + + pub fn cluster_by(mut self, cluster_by: Option>) -> Self { + self.cluster_by = cluster_by; + self + } + + pub fn options(mut self, options: Option>) -> Self { + self.options = options; + self + } + pub fn strict(mut self, strict: bool) -> Self { self.strict = strict; self @@ -270,6 +291,9 @@ impl CreateTableBuilder { on_commit: self.on_commit, on_cluster: self.on_cluster, order_by: self.order_by, + partition_by: self.partition_by, + cluster_by: self.cluster_by, + options: self.options, strict: self.strict, } } @@ -310,6 +334,9 @@ impl TryFrom for CreateTableBuilder { on_commit, on_cluster, order_by, + partition_by, + cluster_by, + options, strict, } => Ok(Self { or_replace, @@ -339,6 +366,9 @@ impl TryFrom for CreateTableBuilder { on_commit, on_cluster, order_by, + partition_by, + cluster_by, + options, strict, }), _ => Err(ParserError::ParserError(format!( @@ -348,6 +378,14 @@ impl TryFrom for CreateTableBuilder { } } +/// Helper return type when parsing configuration for a BigQuery `CREATE TABLE` statement. +#[derive(Default)] +pub(crate) struct BigQueryTableConfiguration { + pub partition_by: Option>, + pub cluster_by: Option>, + pub options: Option>, +} + #[cfg(test)] mod tests { use crate::ast::helpers::stmt_create_table::CreateTableBuilder; diff --git a/src/ast/mod.rs b/src/ast/mod.rs index d32cb93ef..43a5de661 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -34,7 +34,7 @@ pub use self::ddl::{ AlterColumnOperation, AlterIndexOperation, AlterTableOperation, ColumnDef, ColumnOption, ColumnOptionDef, GeneratedAs, GeneratedExpressionMode, IndexType, KeyOrIndexDisplay, Partition, ProcedureParam, ReferentialAction, TableConstraint, UserDefinedTypeCompositeAttributeDef, - UserDefinedTypeRepresentation, + UserDefinedTypeRepresentation, ViewColumnDef, }; pub use self::operator::{BinaryOperator, UnaryOperator}; pub use self::query::{ @@ -1367,6 +1367,38 @@ pub enum Password { NullPassword, } +/// Sql options of a `CREATE TABLE` statement. +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub enum CreateTableOptions { + None, + /// Options specified using the `WITH` keyword. + /// e.g. `WITH (description = "123")` + /// + /// + With(Vec), + /// Options specified using the `OPTIONS` keyword. + /// e.g. `OPTIONS(description = "123")` + /// + /// + Options(Vec), +} + +impl fmt::Display for CreateTableOptions { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + CreateTableOptions::With(with_options) => { + write!(f, "WITH ({})", display_comma_separated(with_options)) + } + CreateTableOptions::Options(options) => { + write!(f, "OPTIONS({})", display_comma_separated(options)) + } + CreateTableOptions::None => Ok(()), + } + } +} + /// A top-level statement (SELECT, INSERT, CREATE, etc.) #[allow(clippy::large_enum_variant)] #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] @@ -1550,9 +1582,9 @@ pub enum Statement { materialized: bool, /// View name name: ObjectName, - columns: Vec, + columns: Vec, query: Box, - with_options: Vec, + options: CreateTableOptions, cluster_by: Vec, /// if true, has RedShift [`WITH NO SCHEMA BINDING`] clause with_no_schema_binding: bool, @@ -1600,6 +1632,15 @@ pub enum Statement { /// than empty (represented as ()), the latter meaning "no sorting". /// order_by: Option>, + /// BigQuery: A partition expression for the table. + /// + partition_by: Option>, + /// BigQuery: Table clustering column list. + /// + cluster_by: Option>, + /// BigQuery: Table options list. + /// + options: Option>, /// SQLite "STRICT" clause. /// if the "STRICT" table-option keyword is added to the end, after the closing ")", /// then strict typing rules apply to that table. @@ -2731,7 +2772,7 @@ impl fmt::Display for Statement { columns, query, materialized, - with_options, + options, cluster_by, with_no_schema_binding, if_not_exists, @@ -2746,8 +2787,8 @@ impl fmt::Display for Statement { temporary = if *temporary { "TEMPORARY " } else { "" }, if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" } )?; - if !with_options.is_empty() { - write!(f, " WITH ({})", display_comma_separated(with_options))?; + if matches!(options, CreateTableOptions::With(_)) { + write!(f, " {options}")?; } if !columns.is_empty() { write!(f, " ({})", display_comma_separated(columns))?; @@ -2755,6 +2796,9 @@ impl fmt::Display for Statement { if !cluster_by.is_empty() { write!(f, " CLUSTER BY ({})", display_comma_separated(cluster_by))?; } + if matches!(options, CreateTableOptions::Options(_)) { + write!(f, " {options}")?; + } write!(f, " AS {query}")?; if *with_no_schema_binding { write!(f, " WITH NO SCHEMA BINDING")?; @@ -2789,6 +2833,9 @@ impl fmt::Display for Statement { on_commit, on_cluster, order_by, + partition_by, + cluster_by, + options, strict, } => { // We want to allow the following options @@ -2945,6 +2992,23 @@ impl fmt::Display for Statement { if let Some(order_by) = order_by { write!(f, " ORDER BY ({})", display_comma_separated(order_by))?; } + if let Some(partition_by) = partition_by.as_ref() { + write!(f, " PARTITION BY {partition_by}")?; + } + if let Some(cluster_by) = cluster_by.as_ref() { + write!( + f, + " CLUSTER BY {}", + display_comma_separated(cluster_by.as_slice()) + )?; + } + if let Some(options) = options.as_ref() { + write!( + f, + " OPTIONS({})", + display_comma_separated(options.as_slice()) + )?; + } if let Some(query) = query { write!(f, " AS {query}")?; } @@ -4496,7 +4560,7 @@ pub struct HiveFormat { #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct SqlOption { pub name: Ident, - pub value: Value, + pub value: Expr, } impl fmt::Display for SqlOption { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 08fbc6d3c..c898824e2 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -27,7 +27,7 @@ use log::debug; use IsLateral::*; use IsOptional::*; -use crate::ast::helpers::stmt_create_table::CreateTableBuilder; +use crate::ast::helpers::stmt_create_table::{BigQueryTableConfiguration, CreateTableBuilder}; use crate::ast::*; use crate::dialect::*; use crate::keywords::{self, Keyword, ALL_KEYWORDS}; @@ -3455,8 +3455,12 @@ impl<'a> Parser<'a> { // Many dialects support `OR ALTER` right after `CREATE`, but we don't (yet). // ANSI SQL and Postgres support RECURSIVE here, but we don't support it either. let name = self.parse_object_name()?; - let columns = self.parse_parenthesized_column_list(Optional, false)?; + let columns = self.parse_view_columns()?; + let mut options = CreateTableOptions::None; let with_options = self.parse_options(Keyword::WITH)?; + if !with_options.is_empty() { + options = CreateTableOptions::With(with_options); + } let cluster_by = if self.parse_keyword(Keyword::CLUSTER) { self.expect_keyword(Keyword::BY)?; @@ -3465,6 +3469,17 @@ impl<'a> Parser<'a> { vec![] }; + if dialect_of!(self is BigQueryDialect | GenericDialect) { + if let Token::Word(word) = self.peek_token().token { + if word.keyword == Keyword::OPTIONS { + let opts = self.parse_options(Keyword::OPTIONS)?; + if !opts.is_empty() { + options = CreateTableOptions::Options(opts); + } + } + }; + } + self.expect_keyword(Keyword::AS)?; let query = Box::new(self.parse_query()?); // Optional `WITH [ CASCADED | LOCAL ] CHECK OPTION` is widely supported here. @@ -3483,7 +3498,7 @@ impl<'a> Parser<'a> { query, materialized, or_replace, - with_options, + options, cluster_by, with_no_schema_binding, if_not_exists, @@ -4169,6 +4184,12 @@ impl<'a> Parser<'a> { None }; + let big_query_config = if dialect_of!(self is BigQueryDialect | GenericDialect) { + self.parse_optional_big_query_create_table_config()? + } else { + Default::default() + }; + // Parse optional `AS ( query )` let query = if self.parse_keyword(Keyword::AS) { Some(Box::new(self.parse_query()?)) @@ -4252,10 +4273,42 @@ impl<'a> Parser<'a> { .collation(collation) .on_commit(on_commit) .on_cluster(on_cluster) + .partition_by(big_query_config.partition_by) + .cluster_by(big_query_config.cluster_by) + .options(big_query_config.options) .strict(strict) .build()) } + /// Parse configuration like partitioning, clustering information during big-query table creation. + /// + fn parse_optional_big_query_create_table_config( + &mut self, + ) -> Result { + let mut partition_by = None; + if self.parse_keywords(&[Keyword::PARTITION, Keyword::BY]) { + partition_by = Some(Box::new(self.parse_expr()?)); + }; + + let mut cluster_by = None; + if self.parse_keywords(&[Keyword::CLUSTER, Keyword::BY]) { + cluster_by = Some(self.parse_comma_separated(Parser::parse_identifier)?); + }; + + let mut options = None; + if let Token::Word(word) = self.peek_token().token { + if word.keyword == Keyword::OPTIONS { + options = Some(self.parse_options(Keyword::OPTIONS)?); + } + }; + + Ok(BigQueryTableConfiguration { + partition_by, + cluster_by, + options, + }) + } + pub fn parse_optional_procedure_parameters( &mut self, ) -> Result>, ParserError> { @@ -4445,6 +4498,13 @@ impl<'a> Parser<'a> { Ok(Some(ColumnOption::OnUpdate(expr))) } else if self.parse_keyword(Keyword::GENERATED) { self.parse_optional_column_option_generated() + } else if dialect_of!(self is BigQueryDialect | GenericDialect) + && self.parse_keyword(Keyword::OPTIONS) + { + self.prev_token(); + Ok(Some(ColumnOption::Options( + self.parse_options(Keyword::OPTIONS)?, + ))) } else if self.parse_keyword(Keyword::AS) && dialect_of!(self is MySqlDialect | SQLiteDialect | DuckDbDialect | GenericDialect) { @@ -4723,7 +4783,7 @@ impl<'a> Parser<'a> { pub fn parse_sql_option(&mut self) -> Result { let name = self.parse_identifier()?; self.expect_token(&Token::Eq)?; - let value = self.parse_value()?; + let value = self.parse_expr()?; Ok(SqlOption { name, value }) } @@ -5944,6 +6004,36 @@ impl<'a> Parser<'a> { } } + /// Parses a parenthesized, comma-separated list of column definitions within a view. + fn parse_view_columns(&mut self) -> Result, ParserError> { + if self.consume_token(&Token::LParen) { + if self.peek_token().token == Token::RParen { + self.next_token(); + Ok(vec![]) + } else { + let cols = self.parse_comma_separated(Parser::parse_view_column)?; + self.expect_token(&Token::RParen)?; + Ok(cols) + } + } else { + Ok(vec![]) + } + } + + /// Parses a column definition within a view. + fn parse_view_column(&mut self) -> Result { + let name = self.parse_identifier()?; + let options = if dialect_of!(self is BigQueryDialect | GenericDialect) + && self.parse_keyword(Keyword::OPTIONS) + { + self.prev_token(); + Some(self.parse_options(Keyword::OPTIONS)?) + } else { + None + }; + Ok(ViewColumnDef { name, options }) + } + /// Parse a parenthesized comma-separated list of unqualified, possibly quoted identifiers pub fn parse_parenthesized_column_list( &mut self, diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 04cca0b7c..90e1293df 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -86,6 +86,168 @@ fn parse_raw_literal() { panic!("invalid query") } +#[test] +fn parse_create_view_with_options() { + let sql = concat!( + "CREATE VIEW myproject.mydataset.newview ", + r#"(name, age OPTIONS(description = "field age")) "#, + r#"OPTIONS(expiration_timestamp = TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 48 HOUR), "#, + r#"friendly_name = "newview", description = "a view that expires in 2 days", labels = [("org_unit", "development")]) "#, + "AS SELECT column_1, column_2, column_3 FROM myproject.mydataset.mytable", + ); + match bigquery().verified_stmt(sql) { + Statement::CreateView { + name, + query, + options, + columns, + .. + } => { + assert_eq!( + name, + ObjectName(vec![ + "myproject".into(), + "mydataset".into(), + "newview".into() + ]) + ); + assert_eq!( + vec![ + ViewColumnDef { + name: Ident::new("name"), + options: None, + }, + ViewColumnDef { + name: Ident::new("age"), + options: Some(vec![SqlOption { + name: Ident::new("description"), + value: Expr::Value(Value::DoubleQuotedString("field age".to_string())), + }]) + }, + ], + columns + ); + assert_eq!( + "SELECT column_1, column_2, column_3 FROM myproject.mydataset.mytable", + query.to_string() + ); + assert_eq!( + r#"OPTIONS(expiration_timestamp = TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 48 HOUR), friendly_name = "newview", description = "a view that expires in 2 days", labels = [("org_unit", "development")])"#, + options.to_string() + ); + let CreateTableOptions::Options(options) = options else { + unreachable!() + }; + assert_eq!( + &SqlOption { + name: Ident::new("description"), + value: Expr::Value(Value::DoubleQuotedString( + "a view that expires in 2 days".to_string() + )), + }, + &options[2], + ); + } + _ => unreachable!(), + } +} + +#[test] +fn parse_create_table_with_options() { + let sql = concat!( + "CREATE TABLE mydataset.newtable ", + r#"(x INT64 NOT NULL OPTIONS(description = "field x"), "#, + r#"y BOOL OPTIONS(description = "field y")) "#, + "PARTITION BY _PARTITIONDATE ", + "CLUSTER BY userid, age ", + r#"OPTIONS(partition_expiration_days = 1, description = "table option description")"# + ); + match bigquery().verified_stmt(sql) { + Statement::CreateTable { + name, + columns, + partition_by, + cluster_by, + options, + .. + } => { + assert_eq!( + name, + ObjectName(vec!["mydataset".into(), "newtable".into()]) + ); + assert_eq!( + vec![ + ColumnDef { + name: Ident::new("x"), + data_type: DataType::Int64, + collation: None, + options: vec![ + ColumnOptionDef { + name: None, + option: ColumnOption::NotNull, + }, + ColumnOptionDef { + name: None, + option: ColumnOption::Options(vec![SqlOption { + name: Ident::new("description"), + value: Expr::Value(Value::DoubleQuotedString( + "field x".to_string() + )), + },]) + }, + ] + }, + ColumnDef { + name: Ident::new("y"), + data_type: DataType::Bool, + collation: None, + options: vec![ColumnOptionDef { + name: None, + option: ColumnOption::Options(vec![SqlOption { + name: Ident::new("description"), + value: Expr::Value(Value::DoubleQuotedString( + "field y".to_string() + )), + },]) + }] + }, + ], + columns + ); + assert_eq!( + ( + Some(Box::new(Expr::Identifier(Ident::new("_PARTITIONDATE")))), + Some(vec![Ident::new("userid"), Ident::new("age"),]), + Some(vec![ + SqlOption { + name: Ident::new("partition_expiration_days"), + value: Expr::Value(number("1")), + }, + SqlOption { + name: Ident::new("description"), + value: Expr::Value(Value::DoubleQuotedString( + "table option description".to_string() + )), + }, + ]) + ), + (partition_by, cluster_by, options) + ) + } + _ => unreachable!(), + } + + let sql = concat!( + "CREATE TABLE mydataset.newtable ", + r#"(x INT64 NOT NULL OPTIONS(description = "field x"), "#, + r#"y BOOL OPTIONS(description = "field y")) "#, + "CLUSTER BY userid ", + r#"OPTIONS(partition_expiration_days = 1, "#, + r#"description = "table option description")"# + ); + bigquery().verified_stmt(sql); +} + #[test] fn parse_nested_data_types() { let sql = "CREATE TABLE table (x STRUCT, b BYTES(42)>, y ARRAY>)"; diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index ec34015a6..c23814e47 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -2996,11 +2996,11 @@ fn parse_create_table_with_options() { vec![ SqlOption { name: "foo".into(), - value: Value::SingleQuotedString("bar".into()), + value: Expr::Value(Value::SingleQuotedString("bar".into())), }, SqlOption { name: "a".into(), - value: number("123"), + value: Expr::Value(number("123")), }, ], with_options @@ -3259,11 +3259,11 @@ fn parse_alter_view_with_options() { vec![ SqlOption { name: "foo".into(), - value: Value::SingleQuotedString("bar".into()), + value: Expr::Value(Value::SingleQuotedString("bar".into())), }, SqlOption { name: "a".into(), - value: number("123"), + value: Expr::Value(number("123")), }, ], with_options @@ -5588,18 +5588,18 @@ fn parse_create_view() { query, or_replace, materialized, - with_options, + options, cluster_by, with_no_schema_binding: late_binding, if_not_exists, temporary, } => { assert_eq!("myschema.myview", name.to_string()); - assert_eq!(Vec::::new(), columns); + assert_eq!(Vec::::new(), columns); assert_eq!("SELECT foo FROM bar", query.to_string()); assert!(!materialized); assert!(!or_replace); - assert_eq!(with_options, vec![]); + assert_eq!(options, CreateTableOptions::None); assert_eq!(cluster_by, vec![]); assert!(!late_binding); assert!(!if_not_exists); @@ -5613,19 +5613,19 @@ fn parse_create_view() { fn parse_create_view_with_options() { let sql = "CREATE VIEW v WITH (foo = 'bar', a = 123) AS SELECT 1"; match verified_stmt(sql) { - Statement::CreateView { with_options, .. } => { + Statement::CreateView { options, .. } => { assert_eq!( - vec![ + CreateTableOptions::With(vec![ SqlOption { name: "foo".into(), - value: Value::SingleQuotedString("bar".into()), + value: Expr::Value(Value::SingleQuotedString("bar".into())), }, SqlOption { name: "a".into(), - value: number("123"), + value: Expr::Value(number("123")), }, - ], - with_options + ]), + options ); } _ => unreachable!(), @@ -5640,7 +5640,7 @@ fn parse_create_view_with_columns() { name, columns, or_replace, - with_options, + options, query, materialized, cluster_by, @@ -5649,8 +5649,17 @@ fn parse_create_view_with_columns() { temporary, } => { assert_eq!("v", name.to_string()); - assert_eq!(columns, vec![Ident::new("has"), Ident::new("cols")]); - assert_eq!(with_options, vec![]); + assert_eq!( + columns, + vec![Ident::new("has"), Ident::new("cols"),] + .into_iter() + .map(|name| ViewColumnDef { + name, + options: None + }) + .collect::>() + ); + assert_eq!(options, CreateTableOptions::None); assert_eq!("SELECT 1, 2", query.to_string()); assert!(!materialized); assert!(!or_replace); @@ -5673,18 +5682,18 @@ fn parse_create_view_temporary() { query, or_replace, materialized, - with_options, + options, cluster_by, with_no_schema_binding: late_binding, if_not_exists, temporary, } => { assert_eq!("myschema.myview", name.to_string()); - assert_eq!(Vec::::new(), columns); + assert_eq!(Vec::::new(), columns); assert_eq!("SELECT foo FROM bar", query.to_string()); assert!(!materialized); assert!(!or_replace); - assert_eq!(with_options, vec![]); + assert_eq!(options, CreateTableOptions::None); assert_eq!(cluster_by, vec![]); assert!(!late_binding); assert!(!if_not_exists); @@ -5702,7 +5711,7 @@ fn parse_create_or_replace_view() { name, columns, or_replace, - with_options, + options, query, materialized, cluster_by, @@ -5712,7 +5721,7 @@ fn parse_create_or_replace_view() { } => { assert_eq!("v", name.to_string()); assert_eq!(columns, vec![]); - assert_eq!(with_options, vec![]); + assert_eq!(options, CreateTableOptions::None); assert_eq!("SELECT 1", query.to_string()); assert!(!materialized); assert!(or_replace); @@ -5737,7 +5746,7 @@ fn parse_create_or_replace_materialized_view() { name, columns, or_replace, - with_options, + options, query, materialized, cluster_by, @@ -5747,7 +5756,7 @@ fn parse_create_or_replace_materialized_view() { } => { assert_eq!("v", name.to_string()); assert_eq!(columns, vec![]); - assert_eq!(with_options, vec![]); + assert_eq!(options, CreateTableOptions::None); assert_eq!("SELECT 1", query.to_string()); assert!(materialized); assert!(or_replace); @@ -5770,17 +5779,17 @@ fn parse_create_materialized_view() { columns, query, materialized, - with_options, + options, cluster_by, with_no_schema_binding: late_binding, if_not_exists, temporary, } => { assert_eq!("myschema.myview", name.to_string()); - assert_eq!(Vec::::new(), columns); + assert_eq!(Vec::::new(), columns); assert_eq!("SELECT foo FROM bar", query.to_string()); assert!(materialized); - assert_eq!(with_options, vec![]); + assert_eq!(options, CreateTableOptions::None); assert!(!or_replace); assert_eq!(cluster_by, vec![]); assert!(!late_binding); @@ -5801,17 +5810,17 @@ fn parse_create_materialized_view_with_cluster_by() { columns, query, materialized, - with_options, + options, cluster_by, with_no_schema_binding: late_binding, if_not_exists, temporary, } => { assert_eq!("myschema.myview", name.to_string()); - assert_eq!(Vec::::new(), columns); + assert_eq!(Vec::::new(), columns); assert_eq!("SELECT foo FROM bar", query.to_string()); assert!(materialized); - assert_eq!(with_options, vec![]); + assert_eq!(options, CreateTableOptions::None); assert!(!or_replace); assert_eq!(cluster_by, vec![Ident::new("foo")]); assert!(!late_binding); @@ -7438,11 +7447,11 @@ fn parse_cache_table() { options: vec![ SqlOption { name: Ident::with_quote('\'', "K1"), - value: Value::SingleQuotedString("V1".into()), + value: Expr::Value(Value::SingleQuotedString("V1".into())), }, SqlOption { name: Ident::with_quote('\'', "K2"), - value: number("0.88"), + value: Expr::Value(number("0.88")), }, ], query: None, @@ -7463,11 +7472,11 @@ fn parse_cache_table() { options: vec![ SqlOption { name: Ident::with_quote('\'', "K1"), - value: Value::SingleQuotedString("V1".into()), + value: Expr::Value(Value::SingleQuotedString("V1".into())), }, SqlOption { name: Ident::with_quote('\'', "K2"), - value: number("0.88"), + value: Expr::Value(number("0.88")), }, ], query: Some(query.clone()), @@ -7488,11 +7497,11 @@ fn parse_cache_table() { options: vec![ SqlOption { name: Ident::with_quote('\'', "K1"), - value: Value::SingleQuotedString("V1".into()), + value: Expr::Value(Value::SingleQuotedString("V1".into())), }, SqlOption { name: Ident::with_quote('\'', "K2"), - value: number("0.88"), + value: Expr::Value(number("0.88")), }, ], query: Some(query.clone()), diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 8d90ca91f..ee0d8baea 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -459,15 +459,15 @@ fn parse_create_table_with_defaults() { vec![ SqlOption { name: "fillfactor".into(), - value: number("20") + value: Expr::Value(number("20")) }, SqlOption { name: "user_catalog_table".into(), - value: Value::Boolean(true) + value: Expr::Value(Value::Boolean(true)) }, SqlOption { name: "autovacuum_vacuum_threshold".into(), - value: number("100") + value: Expr::Value(number("100")) }, ] ); diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index e2e5fbece..2276004ec 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -165,18 +165,18 @@ fn parse_create_view_temporary_if_not_exists() { query, or_replace, materialized, - with_options, + options, cluster_by, with_no_schema_binding: late_binding, if_not_exists, temporary, } => { assert_eq!("myschema.myview", name.to_string()); - assert_eq!(Vec::::new(), columns); + assert_eq!(Vec::::new(), columns); assert_eq!("SELECT foo FROM bar", query.to_string()); assert!(!materialized); assert!(!or_replace); - assert_eq!(with_options, vec![]); + assert_eq!(options, CreateTableOptions::None); assert_eq!(cluster_by, vec![]); assert!(!late_binding); assert!(if_not_exists);