@@ -29,14 +29,13 @@ use crate::ast::helpers::stmt_data_loading::{
2929use crate :: ast:: {
3030 AlterExternalVolumeOperation , AlterTable , AlterTableOperation , AlterTableType ,
3131 CatalogSyncNamespaceMode , ColumnOption , ColumnPolicy , ColumnPolicyProperty , ContactEntry ,
32- CopyIntoSnowflakeKind , CreateTable , CreateTableLikeKind , DollarQuotedString ,
33- ExternalVolumeEncryption , ExternalVolumeStorageLocation , Ident , IdentityParameters ,
34- IdentityProperty , IdentityPropertyFormatKind , IdentityPropertyKind , IdentityPropertyOrder ,
35- InitializeKind , Insert , MultiTableInsertIntoClause , MultiTableInsertType ,
36- MultiTableInsertValue , MultiTableInsertValues , MultiTableInsertWhenClause , ObjectName ,
37- ObjectNamePart , RefreshModeKind , RowAccessPolicy , ShowObjects , SqlOption , Statement ,
38- StorageLifecyclePolicy , StorageSerializationPolicy , TableObject , TagsColumnOption , Value ,
39- WrappedCollection ,
32+ CopyIntoSnowflakeKind , CreateTable , CreateTableLikeKind , DollarQuotedString , Ident ,
33+ IdentityParameters , IdentityProperty , IdentityPropertyFormatKind , IdentityPropertyKind ,
34+ IdentityPropertyOrder , InitializeKind , Insert , MultiTableInsertIntoClause ,
35+ MultiTableInsertType , MultiTableInsertValue , MultiTableInsertValues ,
36+ MultiTableInsertWhenClause , ObjectName , ObjectNamePart , RefreshModeKind , RowAccessPolicy ,
37+ ShowObjects , SqlOption , Statement , StorageLifecyclePolicy , StorageSerializationPolicy ,
38+ TableObject , TagsColumnOption , Value , WrappedCollection ,
4039} ;
4140use crate :: dialect:: { Dialect , Precedence } ;
4241use crate :: keywords:: Keyword ;
@@ -288,11 +287,6 @@ impl Dialect for SnowflakeDialect {
288287 return Some ( parse_alter_session ( parser, set) ) ;
289288 }
290289
291- if parser. parse_keywords ( & [ Keyword :: DROP , Keyword :: EXTERNAL , Keyword :: VOLUME ] ) {
292- // DROP EXTERNAL VOLUME
293- return Some ( parse_drop_external_volume ( parser) ) ;
294- }
295-
296290 if parser
297291 . parse_one_of_keywords ( & [ Keyword :: DESC , Keyword :: DESCRIBE ] )
298292 . is_some ( )
@@ -2018,22 +2012,25 @@ fn parse_multi_table_insert_when_clauses(
20182012}
20192013
20202014/// Parse `CREATE [OR REPLACE] EXTERNAL VOLUME [IF NOT EXISTS] <name> ...`
2015+ ///
2016+ /// Each storage location is a parenthesized `KEY = value` option list; the
2017+ /// options (and the `ENCRYPTION = (...)` sub-list) are parsed generically via
2018+ /// [`Parser::parse_key_value_options`], so field order and the exact option
2019+ /// set are left to the caller rather than validated here.
20212020fn parse_create_external_volume (
20222021 or_replace : bool ,
20232022 parser : & mut Parser ,
20242023) -> Result < Statement , ParserError > {
20252024 let if_not_exists = parser. parse_keywords ( & [ Keyword :: IF , Keyword :: NOT , Keyword :: EXISTS ] ) ;
20262025 let name = parser. parse_object_name ( false ) ?;
20272026
2028- parser. expect_keyword ( Keyword :: STORAGE_LOCATIONS ) ?;
2027+ parser. expect_keyword_is ( Keyword :: STORAGE_LOCATIONS ) ?;
20292028 parser. expect_token ( & Token :: Eq ) ?;
20302029 parser. expect_token ( & Token :: LParen ) ?;
20312030
20322031 let mut storage_locations = vec ! [ ] ;
20332032 loop {
2034- parser. expect_token ( & Token :: LParen ) ?;
2035- storage_locations. push ( parse_external_volume_storage_location ( parser) ?) ;
2036- parser. expect_token ( & Token :: RParen ) ?;
2033+ storage_locations. push ( parser. parse_key_value_options ( true , & [ ] ) ?) ;
20372034 if !parser. consume_token ( & Token :: Comma ) {
20382035 break ;
20392036 }
@@ -2063,126 +2060,21 @@ fn parse_create_external_volume(
20632060 } )
20642061}
20652062
2066- /// Parse a single storage location: `NAME = '...' STORAGE_PROVIDER = '...' ...`
2067- ///
2068- /// `NAME` and `STORAGE_PROVIDER` must appear first (in that order). After
2069- /// those two, `STORAGE_BASE_URL` (required) and the optional fields
2070- /// (`STORAGE_AWS_ROLE_ARN`, `STORAGE_AWS_EXTERNAL_ID`, `ENCRYPTION`) may
2071- /// appear in any order, separated by commas or whitespace. Real Snowflake
2072- /// accepts the flexible ordering — mirror that here so fixtures like
2073- /// `NAME = '…' STORAGE_PROVIDER = '…' STORAGE_AWS_ROLE_ARN = '…'
2074- /// STORAGE_BASE_URL = '…'` parse correctly.
2075- fn parse_external_volume_storage_location (
2076- parser : & mut Parser ,
2077- ) -> Result < ExternalVolumeStorageLocation , ParserError > {
2078- parser. expect_keyword ( Keyword :: NAME ) ?;
2079- parser. expect_token ( & Token :: Eq ) ?;
2080- let name = parser. parse_literal_string ( ) ?;
2081-
2082- // separators may be commas or whitespace — consume optional comma
2083- let _ = parser. consume_token ( & Token :: Comma ) ;
2084-
2085- parser. expect_keyword ( Keyword :: STORAGE_PROVIDER ) ?;
2086- parser. expect_token ( & Token :: Eq ) ?;
2087- let storage_provider = parser. parse_literal_string ( ) ?;
2088-
2089- let mut storage_base_url: Option < String > = None ;
2090- let mut storage_aws_role_arn = None ;
2091- let mut storage_aws_external_id = None ;
2092- let mut encryption = None ;
2093-
2094- // Parse the remaining fields (STORAGE_BASE_URL + optionals) in any order
2095- // (comma-or-whitespace separated). Duplicates are rejected.
2096- loop {
2097- let _ = parser. consume_token ( & Token :: Comma ) ;
2098- if parser. parse_keyword ( Keyword :: STORAGE_BASE_URL ) {
2099- if storage_base_url. is_some ( ) {
2100- return Err ( ParserError :: ParserError (
2101- "duplicate STORAGE_BASE_URL in STORAGE_LOCATION" . to_string ( ) ,
2102- ) ) ;
2103- }
2104- parser. expect_token ( & Token :: Eq ) ?;
2105- storage_base_url = Some ( parser. parse_literal_string ( ) ?) ;
2106- } else if parser. parse_keyword ( Keyword :: STORAGE_AWS_ROLE_ARN ) {
2107- if storage_aws_role_arn. is_some ( ) {
2108- return Err ( ParserError :: ParserError (
2109- "duplicate STORAGE_AWS_ROLE_ARN in STORAGE_LOCATION" . to_string ( ) ,
2110- ) ) ;
2111- }
2112- parser. expect_token ( & Token :: Eq ) ?;
2113- storage_aws_role_arn = Some ( parser. parse_literal_string ( ) ?) ;
2114- } else if parser. parse_keyword ( Keyword :: STORAGE_AWS_EXTERNAL_ID ) {
2115- if storage_aws_external_id. is_some ( ) {
2116- return Err ( ParserError :: ParserError (
2117- "duplicate STORAGE_AWS_EXTERNAL_ID in STORAGE_LOCATION" . to_string ( ) ,
2118- ) ) ;
2119- }
2120- parser. expect_token ( & Token :: Eq ) ?;
2121- storage_aws_external_id = Some ( parser. parse_literal_string ( ) ?) ;
2122- } else if parser. parse_keyword ( Keyword :: ENCRYPTION ) {
2123- if encryption. is_some ( ) {
2124- return Err ( ParserError :: ParserError (
2125- "duplicate ENCRYPTION in STORAGE_LOCATION" . to_string ( ) ,
2126- ) ) ;
2127- }
2128- parser. expect_token ( & Token :: Eq ) ?;
2129- parser. expect_token ( & Token :: LParen ) ?;
2130- encryption = Some ( parse_external_volume_encryption ( parser) ?) ;
2131- parser. expect_token ( & Token :: RParen ) ?;
2132- } else {
2133- break ;
2134- }
2135- }
2136-
2137- let storage_base_url = storage_base_url. ok_or_else ( || {
2138- ParserError :: ParserError ( "STORAGE_BASE_URL is required in STORAGE_LOCATION" . to_string ( ) )
2139- } ) ?;
2140-
2141- Ok ( ExternalVolumeStorageLocation {
2142- name,
2143- storage_provider,
2144- storage_base_url,
2145- storage_aws_role_arn,
2146- storage_aws_external_id,
2147- encryption,
2148- } )
2149- }
2150-
2151- /// Parse encryption options: `TYPE = '...' [KMS_KEY_ID = '...']`
2152- fn parse_external_volume_encryption (
2153- parser : & mut Parser ,
2154- ) -> Result < ExternalVolumeEncryption , ParserError > {
2155- parser. expect_keyword ( Keyword :: TYPE ) ?;
2156- parser. expect_token ( & Token :: Eq ) ?;
2157- let kind = parser. parse_literal_string ( ) ?;
2158-
2159- let mut kms_key_id = None ;
2160- if parser. parse_keyword ( Keyword :: KMS_KEY_ID ) {
2161- parser. expect_token ( & Token :: Eq ) ?;
2162- kms_key_id = Some ( parser. parse_literal_string ( ) ?) ;
2163- }
2164-
2165- Ok ( ExternalVolumeEncryption { kind, kms_key_id } )
2166- }
2167-
21682063/// Parse `ALTER EXTERNAL VOLUME [IF EXISTS] <name> ...`
21692064fn parse_alter_external_volume ( parser : & mut Parser ) -> Result < Statement , ParserError > {
21702065 let if_exists = parser. parse_keywords ( & [ Keyword :: IF , Keyword :: EXISTS ] ) ;
21712066 let name = parser. parse_object_name ( false ) ?;
21722067
21732068 let operation = if parser. parse_keyword ( Keyword :: ADD ) {
2174- parser. expect_keyword ( Keyword :: STORAGE_LOCATION ) ?;
2069+ parser. expect_keyword_is ( Keyword :: STORAGE_LOCATION ) ?;
21752070 parser. expect_token ( & Token :: Eq ) ?;
2176- parser. expect_token ( & Token :: LParen ) ?;
2177- let loc = parse_external_volume_storage_location ( parser) ?;
2178- parser. expect_token ( & Token :: RParen ) ?;
2179- AlterExternalVolumeOperation :: AddStorageLocation ( loc)
2071+ AlterExternalVolumeOperation :: AddStorageLocation ( parser. parse_key_value_options ( true , & [ ] ) ?)
21802072 } else if parser. parse_keyword ( Keyword :: SET ) {
2181- parser. expect_keyword ( Keyword :: ALLOW_WRITES ) ?;
2073+ parser. expect_keyword_is ( Keyword :: ALLOW_WRITES ) ?;
21822074 parser. expect_token ( & Token :: Eq ) ?;
21832075 AlterExternalVolumeOperation :: SetAllowWrites ( parser. parse_boolean_string ( ) ?)
21842076 } else if parser. parse_keyword ( Keyword :: REMOVE ) {
2185- parser. expect_keyword ( Keyword :: STORAGE_LOCATION ) ?;
2077+ parser. expect_keyword_is ( Keyword :: STORAGE_LOCATION ) ?;
21862078 let loc_name = parser. parse_literal_string ( ) ?;
21872079 AlterExternalVolumeOperation :: RemoveStorageLocation ( loc_name)
21882080 } else {
@@ -2199,13 +2091,6 @@ fn parse_alter_external_volume(parser: &mut Parser) -> Result<Statement, ParserE
21992091 } )
22002092}
22012093
2202- /// Parse `DROP EXTERNAL VOLUME [IF EXISTS] <name>`
2203- fn parse_drop_external_volume ( parser : & mut Parser ) -> Result < Statement , ParserError > {
2204- let if_exists = parser. parse_keywords ( & [ Keyword :: IF , Keyword :: EXISTS ] ) ;
2205- let name = parser. parse_object_name ( false ) ?;
2206- Ok ( Statement :: DropExternalVolume { name, if_exists } )
2207- }
2208-
22092094/// Parse `DESC[RIBE] EXTERNAL VOLUME <name>`
22102095fn parse_describe_external_volume ( parser : & mut Parser ) -> Result < Statement , ParserError > {
22112096 let name = parser. parse_object_name ( false ) ?;
0 commit comments