Skip to content

Commit c439ee9

Browse files
authored
Add snowflake dynamic table parsing (#2083)
1 parent eabde4b commit c439ee9

File tree

9 files changed

+112
-17
lines changed

9 files changed

+112
-17
lines changed

src/ast/ddl.rs

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,18 @@ pub enum AlterTableOperation {
365365
DropClusteringKey,
366366
SuspendRecluster,
367367
ResumeRecluster,
368+
/// `REFRESH`
369+
///
370+
/// Note: this is Snowflake specific for dynamic tables <https://docs.snowflake.com/en/sql-reference/sql/alter-table>
371+
Refresh,
372+
/// `SUSPEND`
373+
///
374+
/// Note: this is Snowflake specific for dynamic tables <https://docs.snowflake.com/en/sql-reference/sql/alter-table>
375+
Suspend,
376+
/// `RESUME`
377+
///
378+
/// Note: this is Snowflake specific for dynamic tables <https://docs.snowflake.com/en/sql-reference/sql/alter-table>
379+
Resume,
368380
/// `ALGORITHM [=] { DEFAULT | INSTANT | INPLACE | COPY }`
369381
///
370382
/// [MySQL]-specific table alter algorithm.
@@ -845,6 +857,15 @@ impl fmt::Display for AlterTableOperation {
845857
write!(f, "RESUME RECLUSTER")?;
846858
Ok(())
847859
}
860+
AlterTableOperation::Refresh => {
861+
write!(f, "REFRESH")
862+
}
863+
AlterTableOperation::Suspend => {
864+
write!(f, "SUSPEND")
865+
}
866+
AlterTableOperation::Resume => {
867+
write!(f, "RESUME")
868+
}
848869
AlterTableOperation::AutoIncrement { equals, value } => {
849870
write!(
850871
f,
@@ -3532,6 +3553,20 @@ impl Spanned for DropExtension {
35323553
}
35333554
}
35343555

3556+
/// Table type for ALTER TABLE statements.
3557+
/// Used to distinguish between regular tables, Iceberg tables, and Dynamic tables.
3558+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
3559+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3560+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
3561+
pub enum AlterTableType {
3562+
/// Iceberg table type
3563+
/// <https://docs.snowflake.com/en/sql-reference/sql/alter-iceberg-table>
3564+
Iceberg,
3565+
/// Dynamic table type
3566+
/// <https://docs.snowflake.com/en/sql-reference/sql/alter-table>
3567+
Dynamic,
3568+
}
3569+
35353570
/// ALTER TABLE statement
35363571
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
35373572
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -3548,19 +3583,18 @@ pub struct AlterTable {
35483583
/// For example: `ALTER TABLE table_name ON CLUSTER cluster_name ADD COLUMN c UInt32`
35493584
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/update)
35503585
pub on_cluster: Option<Ident>,
3551-
/// Snowflake "ICEBERG" clause for Iceberg tables
3552-
/// <https://docs.snowflake.com/en/sql-reference/sql/alter-iceberg-table>
3553-
pub iceberg: bool,
3586+
/// Table type: None for regular tables, Some(AlterTableType) for Iceberg or Dynamic tables
3587+
pub table_type: Option<AlterTableType>,
35543588
/// Token that represents the end of the statement (semicolon or EOF)
35553589
pub end_token: AttachedToken,
35563590
}
35573591

35583592
impl fmt::Display for AlterTable {
35593593
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3560-
if self.iceberg {
3561-
write!(f, "ALTER ICEBERG TABLE ")?;
3562-
} else {
3563-
write!(f, "ALTER TABLE ")?;
3594+
match &self.table_type {
3595+
Some(AlterTableType::Iceberg) => write!(f, "ALTER ICEBERG TABLE ")?,
3596+
Some(AlterTableType::Dynamic) => write!(f, "ALTER DYNAMIC TABLE ")?,
3597+
None => write!(f, "ALTER TABLE ")?,
35643598
}
35653599

35663600
if self.if_exists {

src/ast/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ pub use self::dcl::{
6161
pub use self::ddl::{
6262
AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation, AlterPolicyOperation,
6363
AlterSchema, AlterSchemaOperation, AlterTable, AlterTableAlgorithm, AlterTableLock,
64-
AlterTableOperation, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition,
64+
AlterTableOperation, AlterTableType, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition,
6565
AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue, ClusteredBy, ColumnDef,
6666
ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy, ColumnPolicyProperty,
6767
ConstraintCharacteristics, CreateConnector, CreateDomain, CreateExtension, CreateFunction,

src/ast/spans.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,6 +1108,9 @@ impl Spanned for AlterTableOperation {
11081108
AlterTableOperation::DropClusteringKey => Span::empty(),
11091109
AlterTableOperation::SuspendRecluster => Span::empty(),
11101110
AlterTableOperation::ResumeRecluster => Span::empty(),
1111+
AlterTableOperation::Refresh => Span::empty(),
1112+
AlterTableOperation::Suspend => Span::empty(),
1113+
AlterTableOperation::Resume => Span::empty(),
11111114
AlterTableOperation::Algorithm { .. } => Span::empty(),
11121115
AlterTableOperation::AutoIncrement { value, .. } => value.span(),
11131116
AlterTableOperation::Lock { .. } => Span::empty(),

src/dialect/snowflake.rs

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#[cfg(not(feature = "std"))]
1919
use crate::alloc::string::ToString;
20+
use crate::ast::helpers::attached_token::AttachedToken;
2021
use crate::ast::helpers::key_value_options::{
2122
KeyValueOption, KeyValueOptionKind, KeyValueOptions, KeyValueOptionsDelimiter,
2223
};
@@ -26,11 +27,12 @@ use crate::ast::helpers::stmt_data_loading::{
2627
FileStagingCommand, StageLoadSelectItem, StageLoadSelectItemKind, StageParamsObject,
2728
};
2829
use crate::ast::{
29-
CatalogSyncNamespaceMode, ColumnOption, ColumnPolicy, ColumnPolicyProperty, ContactEntry,
30-
CopyIntoSnowflakeKind, CreateTableLikeKind, DollarQuotedString, Ident, IdentityParameters,
31-
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
32-
InitializeKind, ObjectName, ObjectNamePart, RefreshModeKind, RowAccessPolicy, ShowObjects,
33-
SqlOption, Statement, StorageSerializationPolicy, TagsColumnOption, Value, WrappedCollection,
30+
AlterTable, AlterTableOperation, AlterTableType, CatalogSyncNamespaceMode, ColumnOption,
31+
ColumnPolicy, ColumnPolicyProperty, ContactEntry, CopyIntoSnowflakeKind, CreateTableLikeKind,
32+
DollarQuotedString, Ident, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind,
33+
IdentityPropertyKind, IdentityPropertyOrder, InitializeKind, ObjectName, ObjectNamePart,
34+
RefreshModeKind, RowAccessPolicy, ShowObjects, SqlOption, Statement,
35+
StorageSerializationPolicy, TagsColumnOption, Value, WrappedCollection,
3436
};
3537
use crate::dialect::{Dialect, Precedence};
3638
use crate::keywords::Keyword;
@@ -214,6 +216,11 @@ impl Dialect for SnowflakeDialect {
214216
return Some(parser.parse_begin_exception_end());
215217
}
216218

219+
if parser.parse_keywords(&[Keyword::ALTER, Keyword::DYNAMIC, Keyword::TABLE]) {
220+
// ALTER DYNAMIC TABLE
221+
return Some(parse_alter_dynamic_table(parser));
222+
}
223+
217224
if parser.parse_keywords(&[Keyword::ALTER, Keyword::SESSION]) {
218225
// ALTER SESSION
219226
let set = match parser.parse_one_of_keywords(&[Keyword::SET, Keyword::UNSET]) {
@@ -604,6 +611,44 @@ fn parse_file_staging_command(kw: Keyword, parser: &mut Parser) -> Result<Statem
604611
}
605612
}
606613

614+
/// Parse snowflake alter dynamic table.
615+
/// <https://docs.snowflake.com/en/sql-reference/sql/alter-table>
616+
fn parse_alter_dynamic_table(parser: &mut Parser) -> Result<Statement, ParserError> {
617+
// Use parse_object_name(true) to support IDENTIFIER() function
618+
let table_name = parser.parse_object_name(true)?;
619+
620+
// Parse the operation (REFRESH, SUSPEND, or RESUME)
621+
let operation = if parser.parse_keyword(Keyword::REFRESH) {
622+
AlterTableOperation::Refresh
623+
} else if parser.parse_keyword(Keyword::SUSPEND) {
624+
AlterTableOperation::Suspend
625+
} else if parser.parse_keyword(Keyword::RESUME) {
626+
AlterTableOperation::Resume
627+
} else {
628+
return parser.expected(
629+
"REFRESH, SUSPEND, or RESUME after ALTER DYNAMIC TABLE",
630+
parser.peek_token(),
631+
);
632+
};
633+
634+
let end_token = if parser.peek_token_ref().token == Token::SemiColon {
635+
parser.peek_token_ref().clone()
636+
} else {
637+
parser.get_current_token().clone()
638+
};
639+
640+
Ok(Statement::AlterTable(AlterTable {
641+
name: table_name,
642+
if_exists: false,
643+
only: false,
644+
operations: vec![operation],
645+
location: None,
646+
on_cluster: None,
647+
table_type: Some(AlterTableType::Dynamic),
648+
end_token: AttachedToken(end_token),
649+
}))
650+
}
651+
607652
/// Parse snowflake alter session.
608653
/// <https://docs.snowflake.com/en/sql-reference/sql/alter-session>
609654
fn parse_alter_session(parser: &mut Parser, set: bool) -> Result<Statement, ParserError> {

src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,7 @@ define_keywords!(
783783
REF,
784784
REFERENCES,
785785
REFERENCING,
786+
REFRESH,
786787
REFRESH_MODE,
787788
REGCLASS,
788789
REGEXP,

src/parser/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9462,7 +9462,11 @@ impl<'a> Parser<'a> {
94629462
operations,
94639463
location,
94649464
on_cluster,
9465-
iceberg,
9465+
table_type: if iceberg {
9466+
Some(AlterTableType::Iceberg)
9467+
} else {
9468+
None
9469+
},
94669470
end_token: AttachedToken(end_token),
94679471
}
94689472
.into())

src/test_utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ pub fn alter_table_op_with_name(stmt: Statement, expected_name: &str) -> AlterTa
347347
assert_eq!(alter_table.name.to_string(), expected_name);
348348
assert!(!alter_table.if_exists);
349349
assert!(!alter_table.only);
350-
assert!(!alter_table.iceberg);
350+
assert_eq!(alter_table.table_type, None);
351351
only(alter_table.operations)
352352
}
353353
_ => panic!("Expected ALTER TABLE statement"),

tests/sqlparser_mysql.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2746,14 +2746,14 @@ fn parse_alter_table_add_column() {
27462746
if_exists,
27472747
only,
27482748
operations,
2749-
iceberg,
2749+
table_type,
27502750
location: _,
27512751
on_cluster: _,
27522752
end_token: _,
27532753
}) => {
27542754
assert_eq!(name.to_string(), "tab");
27552755
assert!(!if_exists);
2756-
assert!(!iceberg);
2756+
assert_eq!(table_type, None);
27572757
assert!(!only);
27582758
assert_eq!(
27592759
operations,

tests/sqlparser_snowflake.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4662,3 +4662,11 @@ fn test_drop_constraints() {
46624662
snowflake().verified_stmt("ALTER TABLE tbl DROP FOREIGN KEY k1 RESTRICT");
46634663
snowflake().verified_stmt("ALTER TABLE tbl DROP CONSTRAINT c1 CASCADE");
46644664
}
4665+
4666+
#[test]
4667+
fn test_alter_dynamic_table() {
4668+
snowflake().verified_stmt("ALTER DYNAMIC TABLE MY_DYNAMIC_TABLE REFRESH");
4669+
snowflake().verified_stmt("ALTER DYNAMIC TABLE my_database.my_schema.my_dynamic_table REFRESH");
4670+
snowflake().verified_stmt("ALTER DYNAMIC TABLE my_dyn_table SUSPEND");
4671+
snowflake().verified_stmt("ALTER DYNAMIC TABLE my_dyn_table RESUME");
4672+
}

0 commit comments

Comments
 (0)