Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 41 additions & 7 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,18 @@ pub enum AlterTableOperation {
DropClusteringKey,
SuspendRecluster,
ResumeRecluster,
/// `REFRESH`
///
/// Note: this is Snowflake specific for dynamic tables <https://docs.snowflake.com/en/sql-reference/sql/alter-table>
Refresh,
/// `SUSPEND`
///
/// Note: this is Snowflake specific for dynamic tables <https://docs.snowflake.com/en/sql-reference/sql/alter-table>
Suspend,
/// `RESUME`
///
/// Note: this is Snowflake specific for dynamic tables <https://docs.snowflake.com/en/sql-reference/sql/alter-table>
Resume,
/// `ALGORITHM [=] { DEFAULT | INSTANT | INPLACE | COPY }`
///
/// [MySQL]-specific table alter algorithm.
Expand Down Expand Up @@ -845,6 +857,15 @@ impl fmt::Display for AlterTableOperation {
write!(f, "RESUME RECLUSTER")?;
Ok(())
}
AlterTableOperation::Refresh => {
write!(f, "REFRESH")
}
AlterTableOperation::Suspend => {
write!(f, "SUSPEND")
}
AlterTableOperation::Resume => {
write!(f, "RESUME")
}
AlterTableOperation::AutoIncrement { equals, value } => {
write!(
f,
Expand Down Expand Up @@ -3532,6 +3553,20 @@ impl Spanned for DropExtension {
}
}

/// Table type for ALTER TABLE statements.
/// Used to distinguish between regular tables, Iceberg tables, and Dynamic tables.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum AlterTableType {
/// Iceberg table type
/// <https://docs.snowflake.com/en/sql-reference/sql/alter-iceberg-table>
Iceberg,
/// Dynamic table type
/// <https://docs.snowflake.com/en/sql-reference/sql/alter-table>
Dynamic,
}

/// ALTER TABLE statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
Expand All @@ -3548,19 +3583,18 @@ pub struct AlterTable {
/// For example: `ALTER TABLE table_name ON CLUSTER cluster_name ADD COLUMN c UInt32`
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/update)
pub on_cluster: Option<Ident>,
/// Snowflake "ICEBERG" clause for Iceberg tables
/// <https://docs.snowflake.com/en/sql-reference/sql/alter-iceberg-table>
pub iceberg: bool,
/// Table type: None for regular tables, Some(AlterTableType) for Iceberg or Dynamic tables
pub table_type: Option<AlterTableType>,
/// Token that represents the end of the statement (semicolon or EOF)
pub end_token: AttachedToken,
}

impl fmt::Display for AlterTable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.iceberg {
write!(f, "ALTER ICEBERG TABLE ")?;
} else {
write!(f, "ALTER TABLE ")?;
match &self.table_type {
Some(AlterTableType::Iceberg) => write!(f, "ALTER ICEBERG TABLE ")?,
Some(AlterTableType::Dynamic) => write!(f, "ALTER DYNAMIC TABLE ")?,
None => write!(f, "ALTER TABLE ")?,
}

if self.if_exists {
Expand Down
11 changes: 3 additions & 8 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ pub use self::dcl::{
pub use self::ddl::{
AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation, AlterPolicyOperation,
AlterSchema, AlterSchemaOperation, AlterTable, AlterTableAlgorithm, AlterTableLock,
AlterTableOperation, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition,
AlterTableOperation, AlterTableType, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition,
AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue, ClusteredBy, ColumnDef,
ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy, ColumnPolicyProperty,
ConstraintCharacteristics, CreateConnector, CreateDomain, CreateExtension, CreateFunction,
Expand Down Expand Up @@ -2787,10 +2787,11 @@ impl fmt::Display for Declare {
}

/// Sql options of a `CREATE TABLE` statement.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CreateTableOptions {
#[default]
None,
/// Options specified using the `WITH` keyword.
/// e.g. `WITH (description = "123")`
Expand Down Expand Up @@ -2819,12 +2820,6 @@ pub enum CreateTableOptions {
TableProperties(Vec<SqlOption>),
}

impl Default for CreateTableOptions {
fn default() -> Self {
Self::None
}
}

impl fmt::Display for CreateTableOptions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expand Down
3 changes: 3 additions & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,9 @@ impl Spanned for AlterTableOperation {
AlterTableOperation::DropClusteringKey => Span::empty(),
AlterTableOperation::SuspendRecluster => Span::empty(),
AlterTableOperation::ResumeRecluster => Span::empty(),
AlterTableOperation::Refresh => Span::empty(),
AlterTableOperation::Suspend => Span::empty(),
AlterTableOperation::Resume => Span::empty(),
AlterTableOperation::Algorithm { .. } => Span::empty(),
AlterTableOperation::AutoIncrement { value, .. } => value.span(),
AlterTableOperation::Lock { .. } => Span::empty(),
Expand Down
55 changes: 50 additions & 5 deletions src/dialect/snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#[cfg(not(feature = "std"))]
use crate::alloc::string::ToString;
use crate::ast::helpers::attached_token::AttachedToken;
use crate::ast::helpers::key_value_options::{
KeyValueOption, KeyValueOptionKind, KeyValueOptions, KeyValueOptionsDelimiter,
};
Expand All @@ -26,11 +27,12 @@ use crate::ast::helpers::stmt_data_loading::{
FileStagingCommand, StageLoadSelectItem, StageLoadSelectItemKind, StageParamsObject,
};
use crate::ast::{
CatalogSyncNamespaceMode, ColumnOption, ColumnPolicy, ColumnPolicyProperty, ContactEntry,
CopyIntoSnowflakeKind, CreateTableLikeKind, DollarQuotedString, Ident, IdentityParameters,
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
InitializeKind, ObjectName, ObjectNamePart, RefreshModeKind, RowAccessPolicy, ShowObjects,
SqlOption, Statement, StorageSerializationPolicy, TagsColumnOption, Value, WrappedCollection,
AlterTable, AlterTableOperation, AlterTableType, CatalogSyncNamespaceMode, ColumnOption,
ColumnPolicy, ColumnPolicyProperty, ContactEntry, CopyIntoSnowflakeKind, CreateTableLikeKind,
DollarQuotedString, Ident, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind,
IdentityPropertyKind, IdentityPropertyOrder, InitializeKind, ObjectName, ObjectNamePart,
RefreshModeKind, RowAccessPolicy, ShowObjects, SqlOption, Statement,
StorageSerializationPolicy, TagsColumnOption, Value, WrappedCollection,
};
use crate::dialect::{Dialect, Precedence};
use crate::keywords::Keyword;
Expand Down Expand Up @@ -214,6 +216,11 @@ impl Dialect for SnowflakeDialect {
return Some(parser.parse_begin_exception_end());
}

if parser.parse_keywords(&[Keyword::ALTER, Keyword::DYNAMIC, Keyword::TABLE]) {
// ALTER DYNAMIC TABLE
return Some(parse_alter_dynamic_table(parser));
}

if parser.parse_keywords(&[Keyword::ALTER, Keyword::SESSION]) {
// ALTER SESSION
let set = match parser.parse_one_of_keywords(&[Keyword::SET, Keyword::UNSET]) {
Expand Down Expand Up @@ -604,6 +611,44 @@ fn parse_file_staging_command(kw: Keyword, parser: &mut Parser) -> Result<Statem
}
}

/// Parse snowflake alter dynamic table.
/// <https://docs.snowflake.com/en/sql-reference/sql/alter-table>
fn parse_alter_dynamic_table(parser: &mut Parser) -> Result<Statement, ParserError> {
// Use parse_object_name(true) to support IDENTIFIER() function
let table_name = parser.parse_object_name(true)?;

// Parse the operation (REFRESH, SUSPEND, or RESUME)
let operation = if parser.parse_keyword(Keyword::REFRESH) {
AlterTableOperation::Refresh
} else if parser.parse_keyword(Keyword::SUSPEND) {
AlterTableOperation::Suspend
} else if parser.parse_keyword(Keyword::RESUME) {
AlterTableOperation::Resume
} else {
return parser.expected(
"REFRESH, SUSPEND, or RESUME after ALTER DYNAMIC TABLE",
parser.peek_token(),
);
};

let end_token = if parser.peek_token_ref().token == Token::SemiColon {
parser.peek_token_ref().clone()
} else {
parser.get_current_token().clone()
};

Ok(Statement::AlterTable(AlterTable {
name: table_name,
if_exists: false,
only: false,
operations: vec![operation],
location: None,
on_cluster: None,
table_type: Some(AlterTableType::Dynamic),
end_token: AttachedToken(end_token),
}))
}

/// Parse snowflake alter session.
/// <https://docs.snowflake.com/en/sql-reference/sql/alter-session>
fn parse_alter_session(parser: &mut Parser, set: bool) -> Result<Statement, ParserError> {
Expand Down
1 change: 1 addition & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,7 @@ define_keywords!(
REF,
REFERENCES,
REFERENCING,
REFRESH,
REFRESH_MODE,
REGCLASS,
REGEXP,
Expand Down
6 changes: 5 additions & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9461,7 +9461,11 @@ impl<'a> Parser<'a> {
operations,
location,
on_cluster,
iceberg,
table_type: if iceberg {
Some(AlterTableType::Iceberg)
} else {
None
},
end_token: AttachedToken(end_token),
}
.into())
Expand Down
2 changes: 1 addition & 1 deletion src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ pub fn alter_table_op_with_name(stmt: Statement, expected_name: &str) -> AlterTa
assert_eq!(alter_table.name.to_string(), expected_name);
assert!(!alter_table.if_exists);
assert!(!alter_table.only);
assert!(!alter_table.iceberg);
assert_eq!(alter_table.table_type, None);
only(alter_table.operations)
}
_ => panic!("Expected ALTER TABLE statement"),
Expand Down
4 changes: 2 additions & 2 deletions tests/sqlparser_mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2746,14 +2746,14 @@ fn parse_alter_table_add_column() {
if_exists,
only,
operations,
iceberg,
table_type,
location: _,
on_cluster: _,
end_token: _,
}) => {
assert_eq!(name.to_string(), "tab");
assert!(!if_exists);
assert!(!iceberg);
assert_eq!(table_type, None);
assert!(!only);
assert_eq!(
operations,
Expand Down
8 changes: 8 additions & 0 deletions tests/sqlparser_snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4662,3 +4662,11 @@ fn test_drop_constraints() {
snowflake().verified_stmt("ALTER TABLE tbl DROP FOREIGN KEY k1 RESTRICT");
snowflake().verified_stmt("ALTER TABLE tbl DROP CONSTRAINT c1 CASCADE");
}

#[test]
fn test_alter_dynamic_table() {
snowflake().verified_stmt("ALTER DYNAMIC TABLE MY_DYNAMIC_TABLE REFRESH");
snowflake().verified_stmt("ALTER DYNAMIC TABLE my_database.my_schema.my_dynamic_table REFRESH");
snowflake().verified_stmt("ALTER DYNAMIC TABLE my_dyn_table SUSPEND");
snowflake().verified_stmt("ALTER DYNAMIC TABLE my_dyn_table RESUME");
}
Loading