diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index ad3191eb..e12364c0 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -3880,8 +3880,11 @@ pub enum AlterTableType { /// Iceberg, /// Dynamic table type - /// + /// Dynamic, + /// External table type + /// + External, } /// ALTER TABLE statement @@ -3911,6 +3914,7 @@ impl fmt::Display for AlterTable { match &self.table_type { Some(AlterTableType::Iceberg) => write!(f, "ALTER ICEBERG TABLE ")?, Some(AlterTableType::Dynamic) => write!(f, "ALTER DYNAMIC TABLE ")?, + Some(AlterTableType::External) => write!(f, "ALTER EXTERNAL TABLE ")?, None => write!(f, "ALTER TABLE ")?, } diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs index 4cfaddce..d2b714f2 100644 --- a/src/dialect/snowflake.rs +++ b/src/dialect/snowflake.rs @@ -221,6 +221,11 @@ impl Dialect for SnowflakeDialect { return Some(parse_alter_dynamic_table(parser)); } + if parser.parse_keywords(&[Keyword::ALTER, Keyword::EXTERNAL, Keyword::TABLE]) { + // ALTER EXTERNAL TABLE + return Some(parse_alter_external_table(parser)); + } + if parser.parse_keywords(&[Keyword::ALTER, Keyword::SESSION]) { // ALTER SESSION let set = match parser.parse_one_of_keywords(&[Keyword::SET, Keyword::UNSET]) { @@ -649,6 +654,37 @@ fn parse_alter_dynamic_table(parser: &mut Parser) -> Result +fn parse_alter_external_table(parser: &mut Parser) -> Result { + let if_exists = parser.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); + let table_name = parser.parse_object_name(true)?; + + // Parse the operation (REFRESH for now, can be extended) + let operation = if parser.parse_keyword(Keyword::REFRESH) { + AlterTableOperation::Refresh + } else { + return parser.expected("REFRESH after ALTER EXTERNAL 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, + only: false, + operations: vec![operation], + location: None, + on_cluster: None, + table_type: Some(AlterTableType::External), + end_token: AttachedToken(end_token), + })) +} + /// Parse snowflake alter session. /// fn parse_alter_session(parser: &mut Parser, set: bool) -> Result { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index b2fa3b16..fc104a5f 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9778,6 +9778,7 @@ impl<'a> Parser<'a> { Keyword::POLICY, Keyword::CONNECTOR, Keyword::ICEBERG, + Keyword::EXTERNAL, Keyword::SCHEMA, Keyword::USER, ])?; @@ -9789,10 +9790,14 @@ impl<'a> Parser<'a> { } Keyword::VIEW => self.parse_alter_view(), Keyword::TYPE => self.parse_alter_type(), - Keyword::TABLE => self.parse_alter_table(false), + Keyword::TABLE => self.parse_alter_table(None), Keyword::ICEBERG => { self.expect_keyword(Keyword::TABLE)?; - self.parse_alter_table(true) + self.parse_alter_table(Some(AlterTableType::Iceberg)) + } + Keyword::EXTERNAL => { + self.expect_keyword(Keyword::TABLE)?; + self.parse_alter_table(Some(AlterTableType::External)) } Keyword::INDEX => { let index_name = self.parse_object_name(false)?; @@ -9822,7 +9827,10 @@ impl<'a> Parser<'a> { } /// Parse a [Statement::AlterTable] - pub fn parse_alter_table(&mut self, iceberg: bool) -> Result { + pub fn parse_alter_table( + &mut self, + table_type: Option, + ) -> 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)?; @@ -9855,11 +9863,7 @@ impl<'a> Parser<'a> { operations, location, on_cluster, - table_type: if iceberg { - Some(AlterTableType::Iceberg) - } else { - None - }, + table_type, end_token: AttachedToken(end_token), } .into()) diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 22a63266..398ab763 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -4635,3 +4635,9 @@ fn test_alter_dynamic_table() { snowflake().verified_stmt("ALTER DYNAMIC TABLE my_dyn_table SUSPEND"); snowflake().verified_stmt("ALTER DYNAMIC TABLE my_dyn_table RESUME"); } + +#[test] +fn test_alter_external_table() { + snowflake().verified_stmt("ALTER EXTERNAL TABLE some_table REFRESH"); + snowflake().verified_stmt("ALTER EXTERNAL TABLE my_database.my_schema.my_external_table REFRESH"); +}