Skip to content

Commit c314bdd

Browse files
committed
Snowflake: CREATE DYNAMIC TABLE
1 parent 23f40cd commit c314bdd

File tree

10 files changed

+303
-79
lines changed

10 files changed

+303
-79
lines changed

src/ast/dml.rs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ use serde::{Deserialize, Serialize};
2929
#[cfg(feature = "visitor")]
3030
use sqlparser_derive::{Visit, VisitMut};
3131

32-
use crate::display_utils::{indented_list, DisplayCommaSeparated, Indent, NewLine, SpaceOrNewline};
32+
use crate::{
33+
ast::{InitializeKind, RefreshModeKind, TableVersion},
34+
display_utils::{indented_list, DisplayCommaSeparated, Indent, NewLine, SpaceOrNewline},
35+
};
3336

3437
pub use super::ddl::{ColumnDef, TableConstraint};
3538

@@ -135,6 +138,7 @@ pub struct CreateTable {
135138
pub or_replace: bool,
136139
pub temporary: bool,
137140
pub external: bool,
141+
pub dynamic: bool,
138142
pub global: Option<bool>,
139143
pub if_not_exists: bool,
140144
pub transient: bool,
@@ -155,6 +159,7 @@ pub struct CreateTable {
155159
pub without_rowid: bool,
156160
pub like: Option<ObjectName>,
157161
pub clone: Option<ObjectName>,
162+
pub version: Option<TableVersion>,
158163
// For Hive dialect, the table comment is after the column definitions without `=`,
159164
// so the `comment` field is optional and different than the comment field in the general options list.
160165
// [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable)
@@ -232,6 +237,21 @@ pub struct CreateTable {
232237
/// Snowflake "STORAGE_SERIALIZATION_POLICY" clause for Iceberg tables
233238
/// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
234239
pub storage_serialization_policy: Option<StorageSerializationPolicy>,
240+
/// Snowflake "TARGET_LAG" clause for dybamic tables
241+
/// <https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table>
242+
pub target_lag: Option<String>,
243+
/// Snowflake "WAREHOUSE" clause for dybamic tables
244+
/// <https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table>
245+
pub warehouse: Option<Ident>,
246+
/// Snowflake "REFRESH_MODE" clause for dybamic tables
247+
/// <https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table>
248+
pub refresh_mode: Option<RefreshModeKind>,
249+
/// Snowflake "INITIALIZE" clause for dybamic tables
250+
/// <https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table>
251+
pub initialize: Option<InitializeKind>,
252+
/// Snowflake "REQUIRE USER" clause for dybamic tables
253+
/// <https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table>
254+
pub require_user: bool,
235255
}
236256

237257
impl Display for CreateTable {
@@ -245,7 +265,7 @@ impl Display for CreateTable {
245265
// `CREATE TABLE t (a INT) AS SELECT a from t2`
246266
write!(
247267
f,
248-
"CREATE {or_replace}{external}{global}{temporary}{transient}{volatile}{iceberg}TABLE {if_not_exists}{name}",
268+
"CREATE {or_replace}{external}{global}{temporary}{transient}{volatile}{iceberg}{dynamic}TABLE {if_not_exists}{name}",
249269
or_replace = if self.or_replace { "OR REPLACE " } else { "" },
250270
external = if self.external { "EXTERNAL " } else { "" },
251271
global = self.global
@@ -263,6 +283,7 @@ impl Display for CreateTable {
263283
volatile = if self.volatile { "VOLATILE " } else { "" },
264284
// Only for Snowflake
265285
iceberg = if self.iceberg { "ICEBERG " } else { "" },
286+
dynamic = if self.dynamic { "DYNAMIC " } else { "" },
266287
name = self.name,
267288
)?;
268289
if let Some(on_cluster) = &self.on_cluster {
@@ -480,6 +501,26 @@ impl Display for CreateTable {
480501
write!(f, " WITH TAG ({})", display_comma_separated(tag.as_slice()))?;
481502
}
482503

504+
if let Some(target_lag) = &self.target_lag {
505+
write!(f, " TARGET_LAG='{target_lag}'")?;
506+
}
507+
508+
if let Some(warehouse) = &self.warehouse {
509+
write!(f, " WAREHOUSE={warehouse}")?;
510+
}
511+
512+
if let Some(refresh_mode) = &self.refresh_mode {
513+
write!(f, " REFRESH_MODE={refresh_mode}")?;
514+
}
515+
516+
if let Some(initialize) = &self.initialize {
517+
write!(f, " INITIALIZE={initialize}")?;
518+
}
519+
520+
if self.require_user {
521+
write!(f, " REQUIRE USER")?;
522+
}
523+
483524
if self.on_commit.is_some() {
484525
let on_commit = match self.on_commit {
485526
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
@@ -27,9 +27,9 @@ use sqlparser_derive::{Visit, VisitMut};
2727
use super::super::dml::CreateTable;
2828
use crate::ast::{
2929
ClusteredBy, ColumnDef, CommentDef, CreateTableOptions, Expr, FileFormat,
30-
HiveDistributionStyle, HiveFormat, Ident, ObjectName, OnCommit, OneOrManyWithParens, Query,
31-
RowAccessPolicy, Statement, StorageSerializationPolicy, TableConstraint, Tag,
32-
WrappedCollection,
30+
HiveDistributionStyle, HiveFormat, Ident, InitializeKind, ObjectName, OnCommit,
31+
OneOrManyWithParens, Query, RefreshModeKind, RowAccessPolicy, Statement,
32+
StorageSerializationPolicy, TableConstraint, TableVersion, Tag, WrappedCollection,
3333
};
3434

3535
use crate::parser::ParserError;
@@ -73,6 +73,7 @@ pub struct CreateTableBuilder {
7373
pub transient: bool,
7474
pub volatile: bool,
7575
pub iceberg: bool,
76+
pub dynamic: bool,
7677
pub name: ObjectName,
7778
pub columns: Vec<ColumnDef>,
7879
pub constraints: Vec<TableConstraint>,
@@ -84,6 +85,7 @@ pub struct CreateTableBuilder {
8485
pub without_rowid: bool,
8586
pub like: Option<ObjectName>,
8687
pub clone: Option<ObjectName>,
88+
pub version: Option<TableVersion>,
8789
pub comment: Option<CommentDef>,
8890
pub on_commit: Option<OnCommit>,
8991
pub on_cluster: Option<Ident>,
@@ -109,6 +111,11 @@ pub struct CreateTableBuilder {
109111
pub catalog_sync: Option<String>,
110112
pub storage_serialization_policy: Option<StorageSerializationPolicy>,
111113
pub table_options: CreateTableOptions,
114+
pub target_lag: Option<String>,
115+
pub warehouse: Option<Ident>,
116+
pub refresh_mode: Option<RefreshModeKind>,
117+
pub initialize: Option<InitializeKind>,
118+
pub require_user: bool,
112119
}
113120

114121
impl CreateTableBuilder {
@@ -122,6 +129,7 @@ impl CreateTableBuilder {
122129
transient: false,
123130
volatile: false,
124131
iceberg: false,
132+
dynamic: false,
125133
name,
126134
columns: vec![],
127135
constraints: vec![],
@@ -133,6 +141,7 @@ impl CreateTableBuilder {
133141
without_rowid: false,
134142
like: None,
135143
clone: None,
144+
version: None,
136145
comment: None,
137146
on_commit: None,
138147
on_cluster: None,
@@ -158,6 +167,11 @@ impl CreateTableBuilder {
158167
catalog_sync: None,
159168
storage_serialization_policy: None,
160169
table_options: CreateTableOptions::None,
170+
target_lag: None,
171+
warehouse: None,
172+
refresh_mode: None,
173+
initialize: None,
174+
require_user: false,
161175
}
162176
}
163177
pub fn or_replace(mut self, or_replace: bool) -> Self {
@@ -200,6 +214,11 @@ impl CreateTableBuilder {
200214
self
201215
}
202216

217+
pub fn dynamic(mut self, dynamic: bool) -> Self {
218+
self.dynamic = dynamic;
219+
self
220+
}
221+
203222
pub fn columns(mut self, columns: Vec<ColumnDef>) -> Self {
204223
self.columns = columns;
205224
self
@@ -249,6 +268,11 @@ impl CreateTableBuilder {
249268
self
250269
}
251270

271+
pub fn version(mut self, version: Option<TableVersion>) -> Self {
272+
self.version = version;
273+
self
274+
}
275+
252276
pub fn comment_after_column_def(mut self, comment: Option<CommentDef>) -> Self {
253277
self.comment = comment;
254278
self
@@ -383,24 +407,29 @@ impl CreateTableBuilder {
383407
self
384408
}
385409

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

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

406435
pub fn build(self) -> Statement {
@@ -413,6 +442,7 @@ impl CreateTableBuilder {
413442
transient: self.transient,
414443
volatile: self.volatile,
415444
iceberg: self.iceberg,
445+
dynamic: self.dynamic,
416446
name: self.name,
417447
columns: self.columns,
418448
constraints: self.constraints,
@@ -424,6 +454,7 @@ impl CreateTableBuilder {
424454
without_rowid: self.without_rowid,
425455
like: self.like,
426456
clone: self.clone,
457+
version: self.version,
427458
comment: self.comment,
428459
on_commit: self.on_commit,
429460
on_cluster: self.on_cluster,
@@ -449,6 +480,11 @@ impl CreateTableBuilder {
449480
catalog_sync: self.catalog_sync,
450481
storage_serialization_policy: self.storage_serialization_policy,
451482
table_options: self.table_options,
483+
target_lag: self.target_lag,
484+
warehouse: self.warehouse,
485+
refresh_mode: self.refresh_mode,
486+
initialize: self.initialize,
487+
require_user: self.require_user,
452488
})
453489
}
454490
}
@@ -469,6 +505,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
469505
transient,
470506
volatile,
471507
iceberg,
508+
dynamic,
472509
name,
473510
columns,
474511
constraints,
@@ -480,6 +517,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
480517
without_rowid,
481518
like,
482519
clone,
520+
version,
483521
comment,
484522
on_commit,
485523
on_cluster,
@@ -505,13 +543,19 @@ impl TryFrom<Statement> for CreateTableBuilder {
505543
catalog_sync,
506544
storage_serialization_policy,
507545
table_options,
546+
target_lag,
547+
warehouse,
548+
refresh_mode,
549+
initialize,
550+
require_user,
508551
}) => Ok(Self {
509552
or_replace,
510553
temporary,
511554
external,
512555
global,
513556
if_not_exists,
514557
transient,
558+
dynamic,
515559
name,
516560
columns,
517561
constraints,
@@ -523,6 +567,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
523567
without_rowid,
524568
like,
525569
clone,
570+
version,
526571
comment,
527572
on_commit,
528573
on_cluster,
@@ -550,6 +595,11 @@ impl TryFrom<Statement> for CreateTableBuilder {
550595
catalog_sync,
551596
storage_serialization_policy,
552597
table_options,
598+
target_lag,
599+
warehouse,
600+
refresh_mode,
601+
initialize,
602+
require_user,
553603
}),
554604
_ => Err(ParserError::ParserError(format!(
555605
"Expected create table statement, but received: {stmt}"

src/ast/mod.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10099,6 +10099,48 @@ impl fmt::Display for MemberOf {
1009910099
}
1010010100
}
1010110101

10102+
/// Specifies the refresh mode for the dynamic table.
10103+
///
10104+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table)
10105+
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10106+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10107+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10108+
pub enum RefreshModeKind {
10109+
Auto,
10110+
Full,
10111+
Incremental,
10112+
}
10113+
10114+
impl fmt::Display for RefreshModeKind {
10115+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10116+
match self {
10117+
RefreshModeKind::Auto => write!(f, "AUTO"),
10118+
RefreshModeKind::Full => write!(f, "FULL"),
10119+
RefreshModeKind::Incremental => write!(f, "INCREMENTAL"),
10120+
}
10121+
}
10122+
}
10123+
10124+
/// Specifies the behavior of the initial refresh of the dynamic table.
10125+
///
10126+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table)
10127+
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10128+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10129+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10130+
pub enum InitializeKind {
10131+
OnCreate,
10132+
OnSchedule,
10133+
}
10134+
10135+
impl fmt::Display for InitializeKind {
10136+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10137+
match self {
10138+
InitializeKind::OnCreate => write!(f, "ON_CREATE"),
10139+
InitializeKind::OnSchedule => write!(f, "ON_SCHEDULE"),
10140+
}
10141+
}
10142+
}
10143+
1010210144
#[cfg(test)]
1010310145
mod tests {
1010410146
use crate::tokenizer::Location;

src/ast/spans.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,7 @@ impl Spanned for CreateTable {
562562
temporary: _, // bool
563563
external: _, // bool
564564
global: _, // bool
565+
dynamic: _, // bool
565566
if_not_exists: _, // bool
566567
transient: _, // bool
567568
volatile: _, // bool
@@ -602,6 +603,12 @@ impl Spanned for CreateTable {
602603
catalog_sync: _, // todo, Snowflake specific
603604
storage_serialization_policy: _,
604605
table_options,
606+
target_lag: _,
607+
warehouse: _,
608+
version: _,
609+
refresh_mode: _,
610+
initialize: _,
611+
require_user: _,
605612
} = self;
606613

607614
union_spans(

0 commit comments

Comments
 (0)