@@ -657,9 +657,15 @@ fn parse_alter_dynamic_table(parser: &mut Parser) -> Result<Statement, ParserErr
657657/// Parse snowflake alter external table.
658658/// <https://docs.snowflake.com/en/sql-reference/sql/alter-external-table>
659659fn parse_alter_external_table ( parser : & mut Parser ) -> Result < Statement , ParserError > {
660- let if_exists = parser. parse_keywords ( & [ Keyword :: IF , Keyword :: EXISTS ] ) ;
660+ // IF EXISTS can appear before the table name for most operations
661+ let mut if_exists = parser. parse_keywords ( & [ Keyword :: IF , Keyword :: EXISTS ] ) ;
661662 let table_name = parser. parse_object_name ( true ) ?;
662663
664+ // IF EXISTS can also appear after the table name for ADD/DROP PARTITION operations
665+ if !if_exists {
666+ if_exists = parser. parse_keywords ( & [ Keyword :: IF , Keyword :: EXISTS ] ) ;
667+ }
668+
663669 // Parse the operation
664670 let operation = if parser. parse_keyword ( Keyword :: REFRESH ) {
665671 // Optional subpath for refreshing specific partitions
@@ -683,6 +689,27 @@ fn parse_alter_external_table(parser: &mut Parser) -> Result<Statement, ParserEr
683689 column_name,
684690 data_type,
685691 }
692+ } else if parser. parse_keywords ( & [ Keyword :: ADD , Keyword :: PARTITION ] ) {
693+ // ADD PARTITION ( <col> = '<val>' [, ...] ) LOCATION '<path>'
694+ let partition = parse_partition_key_values ( parser) ?;
695+ parser. expect_keyword ( Keyword :: LOCATION ) ?;
696+ let location = parse_single_quoted_string ( parser) ?;
697+ AlterTableOperation :: AddPartition {
698+ partition,
699+ location,
700+ }
701+ } else if parser. parse_keywords ( & [ Keyword :: DROP , Keyword :: PARTITION , Keyword :: LOCATION ] ) {
702+ // DROP PARTITION LOCATION '<path>'
703+ let location = parse_single_quoted_string ( parser) ?;
704+ AlterTableOperation :: DropPartitionLocation { location }
705+ } else if parser. parse_keywords ( & [ Keyword :: ADD , Keyword :: FILES ] ) {
706+ // Parse ADD FILES ( '<path>' [, '<path>', ...] )
707+ let files = parse_parenthesized_file_list ( parser) ?;
708+ AlterTableOperation :: AddFiles { files }
709+ } else if parser. parse_keywords ( & [ Keyword :: REMOVE , Keyword :: FILES ] ) {
710+ // Parse REMOVE FILES ( '<path>' [, '<path>', ...] )
711+ let files = parse_parenthesized_file_list ( parser) ?;
712+ AlterTableOperation :: RemoveFiles { files }
686713 } else if parser. parse_keyword ( Keyword :: SET ) {
687714 // Parse SET key = value options (e.g., SET AUTO_REFRESH = TRUE)
688715 let mut options = vec ! [ ] ;
@@ -698,7 +725,7 @@ fn parse_alter_external_table(parser: &mut Parser) -> Result<Statement, ParserEr
698725 AlterTableOperation :: SetOptions { options }
699726 } else {
700727 return parser. expected (
701- "REFRESH, RENAME TO, ADD PARTITION COLUMN , or SET after ALTER EXTERNAL TABLE" ,
728+ "REFRESH, RENAME TO, ADD, DROP , or SET after ALTER EXTERNAL TABLE" ,
702729 parser. peek_token ( ) ,
703730 ) ;
704731 } ;
@@ -721,6 +748,50 @@ fn parse_alter_external_table(parser: &mut Parser) -> Result<Statement, ParserEr
721748 } ) )
722749}
723750
751+ /// Parse a parenthesized list of single-quoted file paths.
752+ fn parse_parenthesized_file_list ( parser : & mut Parser ) -> Result < Vec < String > , ParserError > {
753+ parser. expect_token ( & Token :: LParen ) ?;
754+ let mut files = vec ! [ ] ;
755+ loop {
756+ match parser. next_token ( ) . token {
757+ Token :: SingleQuotedString ( s) => files. push ( s) ,
758+ _ => {
759+ return parser. expected ( "a single-quoted string" , parser. peek_token ( ) ) ;
760+ }
761+ }
762+ if !parser. consume_token ( & Token :: Comma ) {
763+ break ;
764+ }
765+ }
766+ parser. expect_token ( & Token :: RParen ) ?;
767+ Ok ( files)
768+ }
769+
770+ /// Parse partition key-value pairs: ( <col> = '<val>' [, <col> = '<val>', ...] )
771+ fn parse_partition_key_values ( parser : & mut Parser ) -> Result < Vec < ( Ident , String ) > , ParserError > {
772+ parser. expect_token ( & Token :: LParen ) ?;
773+ let mut pairs = vec ! [ ] ;
774+ loop {
775+ let key = parser. parse_identifier ( ) ?;
776+ parser. expect_token ( & Token :: Eq ) ?;
777+ let value = parse_single_quoted_string ( parser) ?;
778+ pairs. push ( ( key, value) ) ;
779+ if !parser. consume_token ( & Token :: Comma ) {
780+ break ;
781+ }
782+ }
783+ parser. expect_token ( & Token :: RParen ) ?;
784+ Ok ( pairs)
785+ }
786+
787+ /// Parse a single-quoted string and return its content.
788+ fn parse_single_quoted_string ( parser : & mut Parser ) -> Result < String , ParserError > {
789+ match parser. next_token ( ) . token {
790+ Token :: SingleQuotedString ( s) => Ok ( s) ,
791+ _ => parser. expected ( "a single-quoted string" , parser. peek_token ( ) ) ,
792+ }
793+ }
794+
724795/// Parse snowflake alter session.
725796/// <https://docs.snowflake.com/en/sql-reference/sql/alter-session>
726797fn parse_alter_session ( parser : & mut Parser , set : bool ) -> Result < Statement , ParserError > {
0 commit comments