Skip to content

Commit 4b8797e

Browse files
authored
Snowflake: Add support for CREATE DYNAMIC TABLE (#1960)
1 parent 3b52428 commit 4b8797e

File tree

11 files changed

+342
-96
lines changed

11 files changed

+342
-96
lines changed

src/ast/ddl.rs

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ use crate::ast::{
3333
display_comma_separated, display_separated, ArgMode, CommentDef, CreateFunctionBody,
3434
CreateFunctionUsing, CreateTableLikeKind, CreateTableOptions, DataType, Expr, FileFormat,
3535
FunctionBehavior, FunctionCalledOnNull, FunctionDeterminismSpecifier, FunctionParallel,
36-
HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat, Ident, MySQLColumnPosition,
37-
ObjectName, OnCommit, OneOrManyWithParens, OperateFunctionArg, OrderByExpr, ProjectionSelect,
38-
Query, RowAccessPolicy, SequenceOptions, Spanned, SqlOption, StorageSerializationPolicy, Tag,
39-
Value, ValueWithSpan, WrappedCollection,
36+
HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat, Ident, InitializeKind,
37+
MySQLColumnPosition, ObjectName, OnCommit, OneOrManyWithParens, OperateFunctionArg,
38+
OrderByExpr, ProjectionSelect, Query, RefreshModeKind, RowAccessPolicy, SequenceOptions,
39+
Spanned, SqlOption, StorageSerializationPolicy, TableVersion, Tag, Value, ValueWithSpan,
40+
WrappedCollection,
4041
};
4142
use crate::display_utils::{DisplayCommaSeparated, Indent, NewLine, SpaceOrNewline};
4243
use crate::keywords::Keyword;
@@ -2428,6 +2429,7 @@ pub struct CreateTable {
24282429
pub or_replace: bool,
24292430
pub temporary: bool,
24302431
pub external: bool,
2432+
pub dynamic: bool,
24312433
pub global: Option<bool>,
24322434
pub if_not_exists: bool,
24332435
pub transient: bool,
@@ -2448,6 +2450,7 @@ pub struct CreateTable {
24482450
pub without_rowid: bool,
24492451
pub like: Option<CreateTableLikeKind>,
24502452
pub clone: Option<ObjectName>,
2453+
pub version: Option<TableVersion>,
24512454
// For Hive dialect, the table comment is after the column definitions without `=`,
24522455
// so the `comment` field is optional and different than the comment field in the general options list.
24532456
// [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable)
@@ -2525,6 +2528,21 @@ pub struct CreateTable {
25252528
/// Snowflake "STORAGE_SERIALIZATION_POLICY" clause for Iceberg tables
25262529
/// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
25272530
pub storage_serialization_policy: Option<StorageSerializationPolicy>,
2531+
/// Snowflake "TARGET_LAG" clause for dybamic tables
2532+
/// <https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table>
2533+
pub target_lag: Option<String>,
2534+
/// Snowflake "WAREHOUSE" clause for dybamic tables
2535+
/// <https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table>
2536+
pub warehouse: Option<Ident>,
2537+
/// Snowflake "REFRESH_MODE" clause for dybamic tables
2538+
/// <https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table>
2539+
pub refresh_mode: Option<RefreshModeKind>,
2540+
/// Snowflake "INITIALIZE" clause for dybamic tables
2541+
/// <https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table>
2542+
pub initialize: Option<InitializeKind>,
2543+
/// Snowflake "REQUIRE USER" clause for dybamic tables
2544+
/// <https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table>
2545+
pub require_user: bool,
25282546
}
25292547

25302548
impl fmt::Display for CreateTable {
@@ -2538,7 +2556,7 @@ impl fmt::Display for CreateTable {
25382556
// `CREATE TABLE t (a INT) AS SELECT a from t2`
25392557
write!(
25402558
f,
2541-
"CREATE {or_replace}{external}{global}{temporary}{transient}{volatile}{iceberg}TABLE {if_not_exists}{name}",
2559+
"CREATE {or_replace}{external}{global}{temporary}{transient}{volatile}{dynamic}{iceberg}TABLE {if_not_exists}{name}",
25422560
or_replace = if self.or_replace { "OR REPLACE " } else { "" },
25432561
external = if self.external { "EXTERNAL " } else { "" },
25442562
global = self.global
@@ -2556,6 +2574,7 @@ impl fmt::Display for CreateTable {
25562574
volatile = if self.volatile { "VOLATILE " } else { "" },
25572575
// Only for Snowflake
25582576
iceberg = if self.iceberg { "ICEBERG " } else { "" },
2577+
dynamic = if self.dynamic { "DYNAMIC " } else { "" },
25592578
name = self.name,
25602579
)?;
25612580
if let Some(on_cluster) = &self.on_cluster {
@@ -2598,6 +2617,10 @@ impl fmt::Display for CreateTable {
25982617
write!(f, " CLONE {c}")?;
25992618
}
26002619

2620+
if let Some(version) = &self.version {
2621+
write!(f, " {version}")?;
2622+
}
2623+
26012624
match &self.hive_distribution {
26022625
HiveDistributionStyle::PARTITIONED { columns } => {
26032626
write!(f, " PARTITIONED BY ({})", display_comma_separated(columns))?;
@@ -2700,27 +2723,27 @@ impl fmt::Display for CreateTable {
27002723
write!(f, " {options}")?;
27012724
}
27022725
if let Some(external_volume) = self.external_volume.as_ref() {
2703-
write!(f, " EXTERNAL_VOLUME = '{external_volume}'")?;
2726+
write!(f, " EXTERNAL_VOLUME='{external_volume}'")?;
27042727
}
27052728

27062729
if let Some(catalog) = self.catalog.as_ref() {
2707-
write!(f, " CATALOG = '{catalog}'")?;
2730+
write!(f, " CATALOG='{catalog}'")?;
27082731
}
27092732

27102733
if self.iceberg {
27112734
if let Some(base_location) = self.base_location.as_ref() {
2712-
write!(f, " BASE_LOCATION = '{base_location}'")?;
2735+
write!(f, " BASE_LOCATION='{base_location}'")?;
27132736
}
27142737
}
27152738

27162739
if let Some(catalog_sync) = self.catalog_sync.as_ref() {
2717-
write!(f, " CATALOG_SYNC = '{catalog_sync}'")?;
2740+
write!(f, " CATALOG_SYNC='{catalog_sync}'")?;
27182741
}
27192742

27202743
if let Some(storage_serialization_policy) = self.storage_serialization_policy.as_ref() {
27212744
write!(
27222745
f,
2723-
" STORAGE_SERIALIZATION_POLICY = {storage_serialization_policy}"
2746+
" STORAGE_SERIALIZATION_POLICY={storage_serialization_policy}"
27242747
)?;
27252748
}
27262749

@@ -2774,6 +2797,26 @@ impl fmt::Display for CreateTable {
27742797
write!(f, " WITH TAG ({})", display_comma_separated(tag.as_slice()))?;
27752798
}
27762799

2800+
if let Some(target_lag) = &self.target_lag {
2801+
write!(f, " TARGET_LAG='{target_lag}'")?;
2802+
}
2803+
2804+
if let Some(warehouse) = &self.warehouse {
2805+
write!(f, " WAREHOUSE={warehouse}")?;
2806+
}
2807+
2808+
if let Some(refresh_mode) = &self.refresh_mode {
2809+
write!(f, " REFRESH_MODE={refresh_mode}")?;
2810+
}
2811+
2812+
if let Some(initialize) = &self.initialize {
2813+
write!(f, " INITIALIZE={initialize}")?;
2814+
}
2815+
2816+
if self.require_user {
2817+
write!(f, " REQUIRE USER")?;
2818+
}
2819+
27772820
if self.on_commit.is_some() {
27782821
let on_commit = match self.on_commit {
27792822
Some(OnCommit::DeleteRows) => "ON COMMIT DELETE ROWS",

src/ast/helpers/stmt_create_table.rs

Lines changed: 70 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ use sqlparser_derive::{Visit, VisitMut};
2626

2727
use crate::ast::{
2828
ClusteredBy, ColumnDef, CommentDef, CreateTable, CreateTableLikeKind, CreateTableOptions, Expr,
29-
FileFormat, HiveDistributionStyle, HiveFormat, Ident, ObjectName, OnCommit,
30-
OneOrManyWithParens, Query, RowAccessPolicy, Statement, StorageSerializationPolicy,
31-
TableConstraint, Tag, WrappedCollection,
29+
FileFormat, HiveDistributionStyle, HiveFormat, Ident, InitializeKind, ObjectName, OnCommit,
30+
OneOrManyWithParens, Query, RefreshModeKind, RowAccessPolicy, Statement,
31+
StorageSerializationPolicy, TableConstraint, TableVersion, Tag, WrappedCollection,
3232
};
3333

3434
use crate::parser::ParserError;
@@ -72,6 +72,7 @@ pub struct CreateTableBuilder {
7272
pub transient: bool,
7373
pub volatile: bool,
7474
pub iceberg: bool,
75+
pub dynamic: bool,
7576
pub name: ObjectName,
7677
pub columns: Vec<ColumnDef>,
7778
pub constraints: Vec<TableConstraint>,
@@ -83,6 +84,7 @@ pub struct CreateTableBuilder {
8384
pub without_rowid: bool,
8485
pub like: Option<CreateTableLikeKind>,
8586
pub clone: Option<ObjectName>,
87+
pub version: Option<TableVersion>,
8688
pub comment: Option<CommentDef>,
8789
pub on_commit: Option<OnCommit>,
8890
pub on_cluster: Option<Ident>,
@@ -108,6 +110,11 @@ pub struct CreateTableBuilder {
108110
pub catalog_sync: Option<String>,
109111
pub storage_serialization_policy: Option<StorageSerializationPolicy>,
110112
pub table_options: CreateTableOptions,
113+
pub target_lag: Option<String>,
114+
pub warehouse: Option<Ident>,
115+
pub refresh_mode: Option<RefreshModeKind>,
116+
pub initialize: Option<InitializeKind>,
117+
pub require_user: bool,
111118
}
112119

113120
impl CreateTableBuilder {
@@ -121,6 +128,7 @@ impl CreateTableBuilder {
121128
transient: false,
122129
volatile: false,
123130
iceberg: false,
131+
dynamic: false,
124132
name,
125133
columns: vec![],
126134
constraints: vec![],
@@ -132,6 +140,7 @@ impl CreateTableBuilder {
132140
without_rowid: false,
133141
like: None,
134142
clone: None,
143+
version: None,
135144
comment: None,
136145
on_commit: None,
137146
on_cluster: None,
@@ -157,6 +166,11 @@ impl CreateTableBuilder {
157166
catalog_sync: None,
158167
storage_serialization_policy: None,
159168
table_options: CreateTableOptions::None,
169+
target_lag: None,
170+
warehouse: None,
171+
refresh_mode: None,
172+
initialize: None,
173+
require_user: false,
160174
}
161175
}
162176
pub fn or_replace(mut self, or_replace: bool) -> Self {
@@ -199,6 +213,11 @@ impl CreateTableBuilder {
199213
self
200214
}
201215

216+
pub fn dynamic(mut self, dynamic: bool) -> Self {
217+
self.dynamic = dynamic;
218+
self
219+
}
220+
202221
pub fn columns(mut self, columns: Vec<ColumnDef>) -> Self {
203222
self.columns = columns;
204223
self
@@ -248,6 +267,11 @@ impl CreateTableBuilder {
248267
self
249268
}
250269

270+
pub fn version(mut self, version: Option<TableVersion>) -> Self {
271+
self.version = version;
272+
self
273+
}
274+
251275
pub fn comment_after_column_def(mut self, comment: Option<CommentDef>) -> Self {
252276
self.comment = comment;
253277
self
@@ -382,24 +406,29 @@ impl CreateTableBuilder {
382406
self
383407
}
384408

385-
/// Returns true if the statement has exactly one source of info on the schema of the new table.
386-
/// This is Snowflake-specific, some dialects allow more than one source.
387-
pub(crate) fn validate_schema_info(&self) -> bool {
388-
let mut sources = 0;
389-
if !self.columns.is_empty() {
390-
sources += 1;
391-
}
392-
if self.query.is_some() {
393-
sources += 1;
394-
}
395-
if self.like.is_some() {
396-
sources += 1;
397-
}
398-
if self.clone.is_some() {
399-
sources += 1;
400-
}
409+
pub fn target_lag(mut self, target_lag: Option<String>) -> Self {
410+
self.target_lag = target_lag;
411+
self
412+
}
413+
414+
pub fn warehouse(mut self, warehouse: Option<Ident>) -> Self {
415+
self.warehouse = warehouse;
416+
self
417+
}
401418

402-
sources == 1
419+
pub fn refresh_mode(mut self, refresh_mode: Option<RefreshModeKind>) -> Self {
420+
self.refresh_mode = refresh_mode;
421+
self
422+
}
423+
424+
pub fn initialize(mut self, initialize: Option<InitializeKind>) -> Self {
425+
self.initialize = initialize;
426+
self
427+
}
428+
429+
pub fn require_user(mut self, require_user: bool) -> Self {
430+
self.require_user = require_user;
431+
self
403432
}
404433

405434
pub fn build(self) -> Statement {
@@ -412,6 +441,7 @@ impl CreateTableBuilder {
412441
transient: self.transient,
413442
volatile: self.volatile,
414443
iceberg: self.iceberg,
444+
dynamic: self.dynamic,
415445
name: self.name,
416446
columns: self.columns,
417447
constraints: self.constraints,
@@ -423,6 +453,7 @@ impl CreateTableBuilder {
423453
without_rowid: self.without_rowid,
424454
like: self.like,
425455
clone: self.clone,
456+
version: self.version,
426457
comment: self.comment,
427458
on_commit: self.on_commit,
428459
on_cluster: self.on_cluster,
@@ -448,6 +479,11 @@ impl CreateTableBuilder {
448479
catalog_sync: self.catalog_sync,
449480
storage_serialization_policy: self.storage_serialization_policy,
450481
table_options: self.table_options,
482+
target_lag: self.target_lag,
483+
warehouse: self.warehouse,
484+
refresh_mode: self.refresh_mode,
485+
initialize: self.initialize,
486+
require_user: self.require_user,
451487
})
452488
}
453489
}
@@ -468,6 +504,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
468504
transient,
469505
volatile,
470506
iceberg,
507+
dynamic,
471508
name,
472509
columns,
473510
constraints,
@@ -479,6 +516,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
479516
without_rowid,
480517
like,
481518
clone,
519+
version,
482520
comment,
483521
on_commit,
484522
on_cluster,
@@ -504,13 +542,19 @@ impl TryFrom<Statement> for CreateTableBuilder {
504542
catalog_sync,
505543
storage_serialization_policy,
506544
table_options,
545+
target_lag,
546+
warehouse,
547+
refresh_mode,
548+
initialize,
549+
require_user,
507550
}) => Ok(Self {
508551
or_replace,
509552
temporary,
510553
external,
511554
global,
512555
if_not_exists,
513556
transient,
557+
dynamic,
514558
name,
515559
columns,
516560
constraints,
@@ -522,6 +566,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
522566
without_rowid,
523567
like,
524568
clone,
569+
version,
525570
comment,
526571
on_commit,
527572
on_cluster,
@@ -549,6 +594,11 @@ impl TryFrom<Statement> for CreateTableBuilder {
549594
catalog_sync,
550595
storage_serialization_policy,
551596
table_options,
597+
target_lag,
598+
warehouse,
599+
refresh_mode,
600+
initialize,
601+
require_user,
552602
}),
553603
_ => Err(ParserError::ParserError(format!(
554604
"Expected create table statement, but received: {stmt}"

0 commit comments

Comments
 (0)