Skip to content

Commit b374f0e

Browse files
committed
Add support for BigQuery table and view options
Extends the parser with BigQuery support for - Table/View level options - Column level options - Table creation configurations `CLUSTER BY`, `PARTITION BY`
1 parent 8d97330 commit b374f0e

File tree

8 files changed

+462
-53
lines changed

8 files changed

+462
-53
lines changed

src/ast/ddl.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use sqlparser_derive::{Visit, VisitMut};
2626
use crate::ast::value::escape_single_quote_string;
2727
use crate::ast::{
2828
display_comma_separated, display_separated, DataType, Expr, Ident, ObjectName, SequenceOptions,
29+
SqlOption,
2930
};
3031
use crate::tokenizer::Token;
3132

@@ -527,6 +528,29 @@ impl fmt::Display for ColumnDef {
527528
}
528529
}
529530

531+
/// Column definition for a view.
532+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
533+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
534+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
535+
pub struct ViewColumnDef {
536+
pub name: Ident,
537+
pub options: Option<Vec<SqlOption>>,
538+
}
539+
540+
impl fmt::Display for ViewColumnDef {
541+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
542+
write!(f, "{}", self.name)?;
543+
if let Some(options) = self.options.as_ref() {
544+
write!(
545+
f,
546+
" OPTIONS ({})",
547+
display_comma_separated(options.as_slice())
548+
)?;
549+
}
550+
Ok(())
551+
}
552+
}
553+
530554
/// An optionally-named `ColumnOption`: `[ CONSTRAINT <name> ] <column-option>`.
531555
///
532556
/// Note that implementations are substantially more permissive than the ANSI
@@ -601,6 +625,14 @@ pub enum ColumnOption {
601625
generation_expr: Option<Expr>,
602626
generation_expr_mode: Option<GeneratedExpressionMode>,
603627
},
628+
/// BigQuery specific: Explicit column options in a view [1] or table [2]
629+
/// Syntax
630+
/// ```sql
631+
/// OPTIONS (description="field desc")
632+
/// ```
633+
/// [1]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#view_column_option_list
634+
/// [2]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#column_option_list
635+
SqlOptions(Vec<SqlOption>),
604636
}
605637

606638
impl fmt::Display for ColumnOption {
@@ -674,6 +706,9 @@ impl fmt::Display for ColumnOption {
674706
Ok(())
675707
}
676708
}
709+
SqlOptions(options) => {
710+
write!(f, "OPTIONS ({})", display_comma_separated(options))
711+
}
677712
}
678713
}
679714
}

src/ast/helpers/stmt_create_table.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize};
88
use sqlparser_derive::{Visit, VisitMut};
99

1010
use crate::ast::{
11-
ColumnDef, FileFormat, HiveDistributionStyle, HiveFormat, Ident, ObjectName, OnCommit, Query,
12-
SqlOption, Statement, TableConstraint,
11+
BigQueryCreateTableConfiguration, ColumnDef, FileFormat, HiveDistributionStyle, HiveFormat,
12+
Ident, ObjectName, OnCommit, Query, SqlOption, Statement, TableConstraint,
1313
};
1414
use crate::parser::ParserError;
1515

@@ -72,6 +72,7 @@ pub struct CreateTableBuilder {
7272
pub on_commit: Option<OnCommit>,
7373
pub on_cluster: Option<String>,
7474
pub order_by: Option<Vec<Ident>>,
75+
pub big_query_config: Option<Box<BigQueryCreateTableConfiguration>>,
7576
pub strict: bool,
7677
}
7778

@@ -105,6 +106,7 @@ impl CreateTableBuilder {
105106
on_commit: None,
106107
on_cluster: None,
107108
order_by: None,
109+
big_query_config: None,
108110
strict: false,
109111
}
110112
}
@@ -236,6 +238,14 @@ impl CreateTableBuilder {
236238
self
237239
}
238240

241+
pub fn big_query_config(
242+
mut self,
243+
big_query_config: Option<Box<BigQueryCreateTableConfiguration>>,
244+
) -> Self {
245+
self.big_query_config = big_query_config;
246+
self
247+
}
248+
239249
pub fn strict(mut self, strict: bool) -> Self {
240250
self.strict = strict;
241251
self
@@ -270,6 +280,7 @@ impl CreateTableBuilder {
270280
on_commit: self.on_commit,
271281
on_cluster: self.on_cluster,
272282
order_by: self.order_by,
283+
big_query_config: self.big_query_config,
273284
strict: self.strict,
274285
}
275286
}
@@ -310,6 +321,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
310321
on_commit,
311322
on_cluster,
312323
order_by,
324+
big_query_config,
313325
strict,
314326
} => Ok(Self {
315327
or_replace,
@@ -339,6 +351,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
339351
on_commit,
340352
on_cluster,
341353
order_by,
354+
big_query_config,
342355
strict,
343356
}),
344357
_ => Err(ParserError::ParserError(format!(

src/ast/mod.rs

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub use self::ddl::{
3333
AlterColumnOperation, AlterIndexOperation, AlterTableOperation, ColumnDef, ColumnOption,
3434
ColumnOptionDef, GeneratedAs, GeneratedExpressionMode, IndexType, KeyOrIndexDisplay, Partition,
3535
ProcedureParam, ReferentialAction, TableConstraint, UserDefinedTypeCompositeAttributeDef,
36-
UserDefinedTypeRepresentation,
36+
UserDefinedTypeRepresentation, ViewColumnDef,
3737
};
3838
pub use self::operator::{BinaryOperator, UnaryOperator};
3939
pub use self::query::{
@@ -1364,6 +1364,38 @@ pub enum Password {
13641364
NullPassword,
13651365
}
13661366

1367+
/// Sql options of a `CREATE TABLE` statement.
1368+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1369+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1370+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1371+
pub enum CreateTableOptions {
1372+
None,
1373+
/// Options specified using the `WITH` keyword.
1374+
/// e.g. `WITH (description = "123")`
1375+
///
1376+
/// <https://www.postgresql.org/docs/current/sql-createtable.html>
1377+
With(Vec<SqlOption>),
1378+
/// Options specified using the `OPTIONS` keyword.
1379+
/// e.g. `OPTIONS (description = "123")`
1380+
///
1381+
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#table_option_list>
1382+
Options(Vec<SqlOption>),
1383+
}
1384+
1385+
impl fmt::Display for CreateTableOptions {
1386+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1387+
match self {
1388+
CreateTableOptions::With(with_options) => {
1389+
write!(f, "WITH ({})", display_comma_separated(with_options))
1390+
}
1391+
CreateTableOptions::Options(options) => {
1392+
write!(f, "OPTIONS ({})", display_comma_separated(options))
1393+
}
1394+
CreateTableOptions::None => Ok(()),
1395+
}
1396+
}
1397+
}
1398+
13671399
/// A top-level statement (SELECT, INSERT, CREATE, etc.)
13681400
#[allow(clippy::large_enum_variant)]
13691401
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
@@ -1512,9 +1544,9 @@ pub enum Statement {
15121544
materialized: bool,
15131545
/// View name
15141546
name: ObjectName,
1515-
columns: Vec<Ident>,
1547+
columns: Vec<ViewColumnDef>,
15161548
query: Box<Query>,
1517-
with_options: Vec<SqlOption>,
1549+
options: CreateTableOptions,
15181550
cluster_by: Vec<Ident>,
15191551
/// if true, has RedShift [`WITH NO SCHEMA BINDING`] clause <https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_VIEW.html>
15201552
with_no_schema_binding: bool,
@@ -1560,6 +1592,9 @@ pub enum Statement {
15601592
/// than empty (represented as ()), the latter meaning "no sorting".
15611593
/// <https://clickhouse.com/docs/en/sql-reference/statements/create/table/>
15621594
order_by: Option<Vec<Ident>>,
1595+
/// BigQuery specific configuration during table creation.
1596+
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_table_statement>
1597+
big_query_config: Option<Box<BigQueryCreateTableConfiguration>>,
15631598
/// SQLite "STRICT" clause.
15641599
/// if the "STRICT" table-option keyword is added to the end, after the closing ")",
15651600
/// then strict typing rules apply to that table.
@@ -2499,7 +2534,7 @@ impl fmt::Display for Statement {
24992534
columns,
25002535
query,
25012536
materialized,
2502-
with_options,
2537+
options,
25032538
cluster_by,
25042539
with_no_schema_binding,
25052540
if_not_exists,
@@ -2514,15 +2549,18 @@ impl fmt::Display for Statement {
25142549
temporary = if *temporary { "TEMPORARY " } else { "" },
25152550
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }
25162551
)?;
2517-
if !with_options.is_empty() {
2518-
write!(f, " WITH ({})", display_comma_separated(with_options))?;
2552+
if matches!(options, CreateTableOptions::With(_)) {
2553+
write!(f, " {options}")?;
25192554
}
25202555
if !columns.is_empty() {
25212556
write!(f, " ({})", display_comma_separated(columns))?;
25222557
}
25232558
if !cluster_by.is_empty() {
25242559
write!(f, " CLUSTER BY ({})", display_comma_separated(cluster_by))?;
25252560
}
2561+
if matches!(options, CreateTableOptions::Options(_)) {
2562+
write!(f, " {options}")?;
2563+
}
25262564
write!(f, " AS {query}")?;
25272565
if *with_no_schema_binding {
25282566
write!(f, " WITH NO SCHEMA BINDING")?;
@@ -2557,6 +2595,7 @@ impl fmt::Display for Statement {
25572595
on_commit,
25582596
on_cluster,
25592597
order_by,
2598+
big_query_config,
25602599
strict,
25612600
} => {
25622601
// We want to allow the following options
@@ -2713,6 +2752,25 @@ impl fmt::Display for Statement {
27132752
if let Some(order_by) = order_by {
27142753
write!(f, " ORDER BY ({})", display_comma_separated(order_by))?;
27152754
}
2755+
if let Some(bigquery_config) = big_query_config {
2756+
if let Some(partition_by) = bigquery_config.partition_by.as_ref() {
2757+
write!(f, " PARTITION BY {partition_by}")?;
2758+
}
2759+
if let Some(cluster_by) = bigquery_config.cluster_by.as_ref() {
2760+
write!(
2761+
f,
2762+
" CLUSTER BY {}",
2763+
display_comma_separated(cluster_by.as_slice())
2764+
)?;
2765+
}
2766+
if let Some(options) = bigquery_config.options.as_ref() {
2767+
write!(
2768+
f,
2769+
" OPTIONS({})",
2770+
display_comma_separated(options.as_slice())
2771+
)?;
2772+
}
2773+
}
27162774
if let Some(query) = query {
27172775
write!(f, " AS {query}")?;
27182776
}
@@ -4220,12 +4278,31 @@ pub struct HiveFormat {
42204278
pub location: Option<String>,
42214279
}
42224280

4281+
/// Represents BigQuery specific configuration like partitioning, clustering
4282+
/// information during table creation.
4283+
///
4284+
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_table_statement>
4285+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
4286+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
4287+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
4288+
pub struct BigQueryCreateTableConfiguration {
4289+
/// A partition expression for the table.
4290+
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#partition_expression>
4291+
pub partition_by: Option<Expr>,
4292+
/// Table clustering column list.
4293+
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#table_option_list>
4294+
pub cluster_by: Option<Vec<Ident>>,
4295+
/// Table options list.
4296+
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#table_option_list>
4297+
pub options: Option<Vec<SqlOption>>,
4298+
}
4299+
42234300
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
42244301
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
42254302
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
42264303
pub struct SqlOption {
42274304
pub name: Ident,
4228-
pub value: Value,
4305+
pub value: Expr,
42294306
}
42304307

42314308
impl fmt::Display for SqlOption {

0 commit comments

Comments
 (0)