diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index d207f5766..c0254951d 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -87,6 +87,20 @@ pub enum AlterTableOperation { // See `AttachPartition` for more details partition: Partition, }, + /// `FREEZE PARTITION ` + /// Note: this is a ClickHouse-specific operation, please refer to + /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/partition#freeze-partition) + FreezePartition { + partition: Partition, + with_name: Option, + }, + /// `UNFREEZE PARTITION ` + /// Note: this is a ClickHouse-specific operation, please refer to + /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/partition#unfreeze-partition) + UnfreezePartition { + partition: Partition, + with_name: Option, + }, /// `DROP PRIMARY KEY` /// /// Note: this is a MySQL-specific operation. @@ -379,6 +393,26 @@ impl fmt::Display for AlterTableOperation { display_comma_separated(table_properties) ) } + AlterTableOperation::FreezePartition { + partition, + with_name, + } => { + write!(f, "FREEZE {partition}")?; + if let Some(name) = with_name { + write!(f, " WITH NAME {name}")?; + } + Ok(()) + } + AlterTableOperation::UnfreezePartition { + partition, + with_name, + } => { + write!(f, "UNFREEZE {partition}")?; + if let Some(name) = with_name { + write!(f, " WITH NAME {name}")?; + } + Ok(()) + } } } } diff --git a/src/keywords.rs b/src/keywords.rs index c175da874..0b6b038e9 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -760,6 +760,7 @@ define_keywords!( UNBOUNDED, UNCACHE, UNCOMMITTED, + UNFREEZE, UNION, UNIQUE, UNKNOWN, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 60a7b4d0b..90c45aa7a 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -6479,6 +6479,34 @@ impl<'a> Parser<'a> { AlterTableOperation::DetachPartition { partition: self.parse_part_or_partition()?, } + } else if dialect_of!(self is ClickHouseDialect|GenericDialect) + && self.parse_keyword(Keyword::FREEZE) + { + let partition = self.parse_part_or_partition()?; + let with_name = if self.parse_keyword(Keyword::WITH) { + self.expect_keyword(Keyword::NAME)?; + Some(self.parse_identifier(false)?) + } else { + None + }; + AlterTableOperation::FreezePartition { + partition, + with_name, + } + } else if dialect_of!(self is ClickHouseDialect|GenericDialect) + && self.parse_keyword(Keyword::UNFREEZE) + { + let partition = self.parse_part_or_partition()?; + let with_name = if self.parse_keyword(Keyword::WITH) { + self.expect_keyword(Keyword::NAME)?; + Some(self.parse_identifier(false)?) + } else { + None + }; + AlterTableOperation::UnfreezePartition { + partition, + with_name, + } } else { let options: Vec = self.parse_options_with_keywords(&[Keyword::SET, Keyword::TBLPROPERTIES])?; diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index 4676e6e50..4c96febab 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -1216,6 +1216,89 @@ fn parse_create_table_on_commit_and_as_query() { } } +#[test] +fn parse_freeze_and_unfreeze_partition() { + // test cases without `WITH NAME` + for operation_name in &["FREEZE", "UNFREEZE"] { + let sql = format!("ALTER TABLE t {operation_name} PARTITION '2024-08-14'"); + + let expected_partition = Partition::Expr(Expr::Value(Value::SingleQuotedString( + "2024-08-14".to_string(), + ))); + match clickhouse_and_generic().verified_stmt(&sql) { + Statement::AlterTable { operations, .. } => { + assert_eq!(operations.len(), 1); + let expected_operation = if operation_name == &"FREEZE" { + AlterTableOperation::FreezePartition { + partition: expected_partition, + with_name: None, + } + } else { + AlterTableOperation::UnfreezePartition { + partition: expected_partition, + with_name: None, + } + }; + assert_eq!(operations[0], expected_operation); + } + _ => unreachable!(), + } + } + + // test case with `WITH NAME` + for operation_name in &["FREEZE", "UNFREEZE"] { + let sql = + format!("ALTER TABLE t {operation_name} PARTITION '2024-08-14' WITH NAME 'hello'"); + match clickhouse_and_generic().verified_stmt(&sql) { + Statement::AlterTable { operations, .. } => { + assert_eq!(operations.len(), 1); + let expected_partition = Partition::Expr(Expr::Value(Value::SingleQuotedString( + "2024-08-14".to_string(), + ))); + let expected_operation = if operation_name == &"FREEZE" { + AlterTableOperation::FreezePartition { + partition: expected_partition, + with_name: Some(Ident::with_quote('\'', "hello")), + } + } else { + AlterTableOperation::UnfreezePartition { + partition: expected_partition, + with_name: Some(Ident::with_quote('\'', "hello")), + } + }; + assert_eq!(operations[0], expected_operation); + } + _ => unreachable!(), + } + } + + // negative cases + for operation_name in &["FREEZE", "UNFREEZE"] { + assert_eq!( + clickhouse_and_generic() + .parse_sql_statements(format!("ALTER TABLE t0 {operation_name} PARTITION").as_str()) + .unwrap_err(), + ParserError("Expected: an expression:, found: EOF".to_string()) + ); + assert_eq!( + clickhouse_and_generic() + .parse_sql_statements( + format!("ALTER TABLE t0 {operation_name} PARTITION p0 WITH").as_str() + ) + .unwrap_err(), + ParserError("Expected: NAME, found: EOF".to_string()) + ); + assert_eq!( + clickhouse_and_generic() + .parse_sql_statements( + format!("ALTER TABLE t0 {operation_name} PARTITION p0 WITH NAME").as_str() + ) + .unwrap_err(), + ParserError("Expected: identifier, found: EOF".to_string()) + ); + } +} + #[test] fn parse_select_table_function_settings() { fn check_settings(sql: &str, expected: &TableFunctionArgs) {