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

@ -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,