Add support for BigQuery table and view options (#1061)

This commit is contained in:
Ifeanyi Ubah 2024-01-23 23:21:53 +01:00 committed by GitHub
parent c7d2903c6d
commit 3a6d3ecba2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 465 additions and 54 deletions

View file

@ -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
/// <name> [OPTIONS(option, ...)]
///
/// option: <name> = <value>
/// ```
///
/// 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<Vec<SqlOption>>,
}
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 <name> ] <column-option>`.
///
/// 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<SqlOption>),
}
impl fmt::Display for ColumnOption {
@ -788,6 +833,9 @@ impl fmt::Display for ColumnOption {
Ok(())
}
}
Options(options) => {
write!(f, "OPTIONS({})", display_comma_separated(options))
}
}
}
}

View file

@ -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<OnCommit>,
pub on_cluster: Option<String>,
pub order_by: Option<Vec<Ident>>,
pub partition_by: Option<Box<Expr>>,
pub cluster_by: Option<Vec<Ident>>,
pub options: Option<Vec<SqlOption>>,
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<Box<Expr>>) -> Self {
self.partition_by = partition_by;
self
}
pub fn cluster_by(mut self, cluster_by: Option<Vec<Ident>>) -> Self {
self.cluster_by = cluster_by;
self
}
pub fn options(mut self, options: Option<Vec<SqlOption>>) -> 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<Statement> for CreateTableBuilder {
on_commit,
on_cluster,
order_by,
partition_by,
cluster_by,
options,
strict,
} => Ok(Self {
or_replace,
@ -339,6 +366,9 @@ impl TryFrom<Statement> for CreateTableBuilder {
on_commit,
on_cluster,
order_by,
partition_by,
cluster_by,
options,
strict,
}),
_ => Err(ParserError::ParserError(format!(
@ -348,6 +378,14 @@ impl TryFrom<Statement> for CreateTableBuilder {
}
}
/// Helper return type when parsing configuration for a BigQuery `CREATE TABLE` statement.
#[derive(Default)]
pub(crate) struct BigQueryTableConfiguration {
pub partition_by: Option<Box<Expr>>,
pub cluster_by: Option<Vec<Ident>>,
pub options: Option<Vec<SqlOption>>,
}
#[cfg(test)]
mod tests {
use crate::ast::helpers::stmt_create_table::CreateTableBuilder;

View file

@ -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")`
///
/// <https://www.postgresql.org/docs/current/sql-createtable.html>
With(Vec<SqlOption>),
/// Options specified using the `OPTIONS` keyword.
/// e.g. `OPTIONS(description = "123")`
///
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#table_option_list>
Options(Vec<SqlOption>),
}
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<Ident>,
columns: Vec<ViewColumnDef>,
query: Box<Query>,
with_options: Vec<SqlOption>,
options: CreateTableOptions,
cluster_by: Vec<Ident>,
/// if true, has RedShift [`WITH NO SCHEMA BINDING`] clause <https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_VIEW.html>
with_no_schema_binding: bool,
@ -1600,6 +1632,15 @@ pub enum Statement {
/// than empty (represented as ()), the latter meaning "no sorting".
/// <https://clickhouse.com/docs/en/sql-reference/statements/create/table/>
order_by: Option<Vec<Ident>>,
/// BigQuery: A partition expression for the table.
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#partition_expression>
partition_by: Option<Box<Expr>>,
/// BigQuery: Table clustering column list.
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#table_option_list>
cluster_by: Option<Vec<Ident>>,
/// BigQuery: Table options list.
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#table_option_list>
options: Option<Vec<SqlOption>>,
/// 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 {

View file

@ -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};
@ -3463,8 +3463,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)?;
@ -3473,6 +3477,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.
@ -3491,7 +3506,7 @@ impl<'a> Parser<'a> {
query,
materialized,
or_replace,
with_options,
options,
cluster_by,
with_no_schema_binding,
if_not_exists,
@ -4177,6 +4192,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()?))
@ -4260,10 +4281,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.
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_2>
fn parse_optional_big_query_create_table_config(
&mut self,
) -> Result<BigQueryTableConfiguration, ParserError> {
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<Option<Vec<ProcedureParam>>, ParserError> {
@ -4453,6 +4506,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)
{
@ -4731,7 +4791,7 @@ impl<'a> Parser<'a> {
pub fn parse_sql_option(&mut self) -> Result<SqlOption, ParserError> {
let name = self.parse_identifier()?;
self.expect_token(&Token::Eq)?;
let value = self.parse_value()?;
let value = self.parse_expr()?;
Ok(SqlOption { name, value })
}
@ -5952,6 +6012,36 @@ impl<'a> Parser<'a> {
}
}
/// Parses a parenthesized, comma-separated list of column definitions within a view.
fn parse_view_columns(&mut self) -> Result<Vec<ViewColumnDef>, 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<ViewColumnDef, ParserError> {
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,

View file

@ -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<a ARRAY<INT64>, b BYTES(42)>, y ARRAY<STRUCT<INT64>>)";

View file

@ -2997,11 +2997,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
@ -3260,11 +3260,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
@ -5589,18 +5589,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::<Ident>::new(), columns);
assert_eq!(Vec::<ViewColumnDef>::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);
@ -5614,19 +5614,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!(),
@ -5641,7 +5641,7 @@ fn parse_create_view_with_columns() {
name,
columns,
or_replace,
with_options,
options,
query,
materialized,
cluster_by,
@ -5650,8 +5650,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::<Vec<_>>()
);
assert_eq!(options, CreateTableOptions::None);
assert_eq!("SELECT 1, 2", query.to_string());
assert!(!materialized);
assert!(!or_replace);
@ -5674,18 +5683,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::<Ident>::new(), columns);
assert_eq!(Vec::<ViewColumnDef>::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);
@ -5703,7 +5712,7 @@ fn parse_create_or_replace_view() {
name,
columns,
or_replace,
with_options,
options,
query,
materialized,
cluster_by,
@ -5713,7 +5722,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);
@ -5738,7 +5747,7 @@ fn parse_create_or_replace_materialized_view() {
name,
columns,
or_replace,
with_options,
options,
query,
materialized,
cluster_by,
@ -5748,7 +5757,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);
@ -5771,17 +5780,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::<Ident>::new(), columns);
assert_eq!(Vec::<ViewColumnDef>::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);
@ -5802,17 +5811,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::<Ident>::new(), columns);
assert_eq!(Vec::<ViewColumnDef>::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);
@ -7439,11 +7448,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,
@ -7464,11 +7473,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()),
@ -7489,11 +7498,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()),

View file

@ -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"))
},
]
);

View file

@ -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::<Ident>::new(), columns);
assert_eq!(Vec::<ViewColumnDef>::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);