diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 653f58e4d..711e580df 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -3281,6 +3281,9 @@ pub enum Statement { /// 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) on_cluster: Option, + /// Snowflake "ICEBERG" clause for Iceberg tables + /// + iceberg: bool, }, /// ```sql /// ALTER INDEX @@ -3405,7 +3408,7 @@ pub enum Statement { purge: bool, /// MySQL-specific "TEMPORARY" keyword temporary: bool, - /// MySQL-specific drop index syntax, which requires table specification + /// MySQL-specific drop index syntax, which requires table specification /// See table: Option, }, @@ -5139,8 +5142,14 @@ impl fmt::Display for Statement { operations, location, on_cluster, + iceberg, } => { - write!(f, "ALTER TABLE ")?; + if *iceberg { + write!(f, "ALTER ICEBERG TABLE ")?; + } else { + write!(f, "ALTER TABLE ")?; + } + if *if_exists { write!(f, "IF EXISTS ")?; } diff --git a/src/ast/spans.rs b/src/ast/spans.rs index d612738c5..dd918c34b 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -431,6 +431,7 @@ impl Spanned for Statement { operations, location: _, on_cluster, + iceberg: _, } => union_spans( core::iter::once(name.span()) .chain(operations.iter().map(|i| i.span())) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index f2da659f6..38532bc20 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8893,38 +8893,15 @@ impl<'a> Parser<'a> { Keyword::ROLE, Keyword::POLICY, Keyword::CONNECTOR, + Keyword::ICEBERG, ])?; match object_type { Keyword::VIEW => self.parse_alter_view(), Keyword::TYPE => self.parse_alter_type(), - Keyword::TABLE => { - let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); - let only = self.parse_keyword(Keyword::ONLY); // [ ONLY ] - let table_name = self.parse_object_name(false)?; - let on_cluster = self.parse_optional_on_cluster()?; - let operations = self.parse_comma_separated(Parser::parse_alter_table_operation)?; - - let mut location = None; - if self.parse_keyword(Keyword::LOCATION) { - location = Some(HiveSetLocation { - has_set: false, - location: self.parse_identifier()?, - }); - } else if self.parse_keywords(&[Keyword::SET, Keyword::LOCATION]) { - location = Some(HiveSetLocation { - has_set: true, - location: self.parse_identifier()?, - }); - } - - Ok(Statement::AlterTable { - name: table_name, - if_exists, - only, - operations, - location, - on_cluster, - }) + Keyword::TABLE => self.parse_alter_table(false), + Keyword::ICEBERG => { + self.expect_keyword(Keyword::TABLE)?; + self.parse_alter_table(true) } Keyword::INDEX => { let index_name = self.parse_object_name(false)?; @@ -8952,6 +8929,38 @@ impl<'a> Parser<'a> { } } + /// Parse a [Statement::AlterTable] + pub fn parse_alter_table(&mut self, iceberg: bool) -> Result { + let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); + let only = self.parse_keyword(Keyword::ONLY); // [ ONLY ] + let table_name = self.parse_object_name(false)?; + let on_cluster = self.parse_optional_on_cluster()?; + let operations = self.parse_comma_separated(Parser::parse_alter_table_operation)?; + + let mut location = None; + if self.parse_keyword(Keyword::LOCATION) { + location = Some(HiveSetLocation { + has_set: false, + location: self.parse_identifier()?, + }); + } else if self.parse_keywords(&[Keyword::SET, Keyword::LOCATION]) { + location = Some(HiveSetLocation { + has_set: true, + location: self.parse_identifier()?, + }); + } + + Ok(Statement::AlterTable { + name: table_name, + if_exists, + only, + operations, + location, + on_cluster, + iceberg, + }) + } + pub fn parse_alter_view(&mut self) -> Result { let name = self.parse_object_name(false)?; let columns = self.parse_parenthesized_column_list(Optional, false)?; diff --git a/src/test_utils.rs b/src/test_utils.rs index 3c22fa911..24c0ca575 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -345,10 +345,12 @@ pub fn alter_table_op_with_name(stmt: Statement, expected_name: &str) -> AlterTa operations, on_cluster: _, location: _, + iceberg, } => { assert_eq!(name.to_string(), expected_name); assert!(!if_exists); assert!(!is_only); + assert!(!iceberg); only(operations) } _ => panic!("Expected ALTER TABLE statement"), diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 337707c99..0800c3297 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -2507,11 +2507,13 @@ fn parse_alter_table_add_column() { if_exists, only, operations, + iceberg, location: _, on_cluster: _, } => { assert_eq!(name.to_string(), "tab"); assert!(!if_exists); + assert!(!iceberg); assert!(!only); assert_eq!( operations, @@ -2536,8 +2538,7 @@ fn parse_alter_table_add_column() { if_exists, only, operations, - location: _, - on_cluster: _, + .. } => { assert_eq!(name.to_string(), "tab"); assert!(!if_exists); @@ -2574,8 +2575,7 @@ fn parse_alter_table_add_columns() { if_exists, only, operations, - location: _, - on_cluster: _, + .. } => { assert_eq!(name.to_string(), "tab"); assert!(!if_exists); diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 682c0d6cc..d54d4e2af 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -834,8 +834,7 @@ fn parse_alter_table_add_columns() { if_exists, only, operations, - location: _, - on_cluster: _, + .. } => { assert_eq!(name.to_string(), "tab"); assert!(if_exists); @@ -915,8 +914,7 @@ fn parse_alter_table_owner_to() { if_exists: _, only: _, operations, - location: _, - on_cluster: _, + .. } => { assert_eq!(name.to_string(), "tab"); assert_eq!( diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 52be31435..b4d62506d 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -1591,6 +1591,13 @@ fn test_alter_table_clustering() { snowflake_and_generic().verified_stmt("ALTER TABLE tbl RESUME RECLUSTER"); } +#[test] +fn test_alter_iceberg_table() { + snowflake_and_generic().verified_stmt("ALTER ICEBERG TABLE tbl DROP CLUSTERING KEY"); + snowflake_and_generic().verified_stmt("ALTER ICEBERG TABLE tbl SUSPEND RECLUSTER"); + snowflake_and_generic().verified_stmt("ALTER ICEBERG TABLE tbl RESUME RECLUSTER"); +} + #[test] fn test_drop_stage() { match snowflake_and_generic().verified_stmt("DROP STAGE s1") {