1515// specific language governing permissions and limitations
1616// under the License.
1717
18+ use super :: keywords:: RESERVED_FOR_IDENTIFIER ;
1819#[ cfg( not( feature = "std" ) ) ]
1920use crate :: alloc:: string:: ToString ;
20- use crate :: ast:: helpers:: key_value_options:: {
21- KeyValueOption , KeyValueOptionType , KeyValueOptions ,
22- } ;
21+ use crate :: ast:: helpers:: key_value_options:: { KeyValueOption , KeyValueOptionType , KeyValueOptions } ;
2322use crate :: ast:: helpers:: stmt_create_database:: CreateDatabaseBuilder ;
2423use crate :: ast:: helpers:: stmt_create_table:: CreateTableBuilder ;
2524use crate :: ast:: helpers:: stmt_data_loading:: {
2625 FileStagingCommand , StageLoadSelectItem , StageLoadSelectItemKind , StageParamsObject ,
2726} ;
2827use crate :: ast:: {
29- CatalogSyncNamespaceMode , ColumnOption , ColumnPolicy , ColumnPolicyProperty , ContactEntry ,
30- CopyIntoSnowflakeKind , DollarQuotedString , Ident , IdentityParameters , IdentityProperty ,
31- IdentityPropertyFormatKind , IdentityPropertyKind , IdentityPropertyOrder , ObjectName ,
32- ObjectNamePart , RowAccessPolicy , ShowObjects , SqlOption , Statement , StorageSerializationPolicy ,
33- TagsColumnOption , WrappedCollection ,
28+ CatalogSyncNamespaceMode , CloudProviderParams , ColumnOption , ColumnPolicy ,
29+ ColumnPolicyProperty , ContactEntry , CopyIntoSnowflakeKind , DollarQuotedString , Ident ,
30+ IdentityParameters , IdentityProperty , IdentityPropertyFormatKind , IdentityPropertyKind ,
31+ IdentityPropertyOrder , ObjectName , ObjectNamePart , RowAccessPolicy , ShowObjects , SqlOption ,
32+ Statement , StorageSerializationPolicy , TagsColumnOption , WrappedCollection ,
3433} ;
3534use crate :: dialect:: { Dialect , Precedence } ;
3635use crate :: keywords:: Keyword ;
@@ -45,8 +44,6 @@ use alloc::vec::Vec;
4544#[ cfg( not( feature = "std" ) ) ]
4645use alloc:: { format, vec} ;
4746
48- use super :: keywords:: RESERVED_FOR_IDENTIFIER ;
49-
5047const RESERVED_KEYWORDS_FOR_SELECT_ITEM_OPERATOR : [ Keyword ; 1 ] = [ Keyword :: CONNECT_BY_ROOT ] ;
5148/// A [`Dialect`] for [Snowflake](https://www.snowflake.com/)
5249#[ derive( Debug , Default ) ]
@@ -187,6 +184,8 @@ impl Dialect for SnowflakeDialect {
187184 ) ) ;
188185 } else if parser. parse_keyword ( Keyword :: DATABASE ) {
189186 return Some ( parse_create_database ( or_replace, transient, parser) ) ;
187+ } else if parser. parse_keywords ( & [ Keyword :: EXTERNAL , Keyword :: VOLUME ] ) {
188+ return Some ( parse_create_external_volume ( or_replace, parser) ) ;
190189 } else {
191190 // need to go back with the cursor
192191 let mut back = 1 ;
@@ -323,7 +322,7 @@ impl Dialect for SnowflakeDialect {
323322 Keyword :: LIMIT | Keyword :: OFFSET if peek_for_limit_options ( parser) => false ,
324323
325324 // `FETCH` can be considered an alias as long as it's not followed by `FIRST`` or `NEXT`
326- // which would give it a different meanings, for example:
325+ // which would give it a different meanings, for example:
327326 // `SELECT 1 FETCH FIRST 10 ROWS` - not an alias
328327 // `SELECT 1 FETCH 10` - not an alias
329328 Keyword :: FETCH if parser. peek_one_of_keywords ( & [ Keyword :: FIRST , Keyword :: NEXT ] ) . is_some ( )
@@ -840,6 +839,146 @@ pub fn parse_create_database(
840839 Ok ( builder. build ( ) )
841840}
842841
842+ fn parse_create_external_volume (
843+ or_replace : bool ,
844+ parser : & mut Parser ,
845+ ) -> Result < Statement , ParserError > {
846+ let if_not_exists = parser. parse_keywords ( & [ Keyword :: IF , Keyword :: NOT , Keyword :: EXISTS ] ) ;
847+ let name = parser. parse_object_name ( false ) ?;
848+ let mut comment = None ;
849+ let mut allow_writes = None ;
850+ let mut storage_locations = Vec :: new ( ) ;
851+
852+ // STORAGE_LOCATIONS (...)
853+ if parser. parse_keywords ( & [ Keyword :: STORAGE_LOCATIONS ] ) {
854+ parser. expect_token ( & Token :: Eq ) ?;
855+ storage_locations = parse_storage_locations ( parser) ?;
856+ } ;
857+
858+ // ALLOW_WRITES [ = true | false ]
859+ if parser. parse_keyword ( Keyword :: ALLOW_WRITES ) {
860+ parser. expect_token ( & Token :: Eq ) ?;
861+ allow_writes = Some ( parser. parse_boolean_string ( ) ?) ;
862+ }
863+
864+ // COMMENT = '...'
865+ if parser. parse_keyword ( Keyword :: COMMENT ) {
866+ parser. expect_token ( & Token :: Eq ) ?;
867+ comment = Some ( parser. parse_literal_string ( ) ?) ;
868+ }
869+
870+ if storage_locations. is_empty ( ) {
871+ return Err ( ParserError :: ParserError (
872+ "STORAGE_LOCATIONS is required for CREATE EXTERNAL VOLUME" . to_string ( ) ,
873+ ) ) ;
874+ }
875+
876+ Ok ( Statement :: CreateExternalVolume {
877+ or_replace,
878+ if_not_exists,
879+ name,
880+ allow_writes,
881+ comment,
882+ storage_locations,
883+ } )
884+ }
885+
886+ fn parse_storage_locations ( parser : & mut Parser ) -> Result < Vec < CloudProviderParams > , ParserError > {
887+ let mut locations = Vec :: new ( ) ;
888+ parser. expect_token ( & Token :: LParen ) ?;
889+
890+ loop {
891+ parser. expect_token ( & Token :: LParen ) ?;
892+
893+ // START OF ONE CloudProviderParams BLOCK
894+ let mut name = None ;
895+ let mut provider = None ;
896+ let mut base_url = None ;
897+ let mut aws_role_arn = None ;
898+ let mut aws_access_point_arn = None ;
899+ let mut aws_external_id = None ;
900+ let mut azure_tenant_id = None ;
901+ let mut storage_endpoint = None ;
902+ let mut use_private_link_endpoint = None ;
903+ let mut encryption: KeyValueOptions = KeyValueOptions { options : vec ! [ ] } ;
904+ let mut credentials: KeyValueOptions = KeyValueOptions { options : vec ! [ ] } ;
905+
906+ loop {
907+ if parser. parse_keyword ( Keyword :: NAME ) {
908+ parser. expect_token ( & Token :: Eq ) ?;
909+ name = Some ( parser. parse_literal_string ( ) ?) ;
910+ } else if parser. parse_keyword ( Keyword :: STORAGE_PROVIDER ) {
911+ parser. expect_token ( & Token :: Eq ) ?;
912+ provider = Some ( parser. parse_literal_string ( ) ?) ;
913+ } else if parser. parse_keyword ( Keyword :: STORAGE_BASE_URL ) {
914+ parser. expect_token ( & Token :: Eq ) ?;
915+ base_url = Some ( parser. parse_literal_string ( ) ?) ;
916+ } else if parser. parse_keyword ( Keyword :: STORAGE_AWS_ROLE_ARN ) {
917+ parser. expect_token ( & Token :: Eq ) ?;
918+ aws_role_arn = Some ( parser. parse_literal_string ( ) ?) ;
919+ } else if parser. parse_keyword ( Keyword :: STORAGE_AWS_ACCESS_POINT_ARN ) {
920+ parser. expect_token ( & Token :: Eq ) ?;
921+ aws_access_point_arn = Some ( parser. parse_literal_string ( ) ?) ;
922+ } else if parser. parse_keyword ( Keyword :: STORAGE_AWS_EXTERNAL_ID ) {
923+ parser. expect_token ( & Token :: Eq ) ?;
924+ aws_external_id = Some ( parser. parse_literal_string ( ) ?) ;
925+ } else if parser. parse_keyword ( Keyword :: AZURE_TENANT_ID ) {
926+ parser. expect_token ( & Token :: Eq ) ?;
927+ azure_tenant_id = Some ( parser. parse_literal_string ( ) ?) ;
928+ } else if parser. parse_keyword ( Keyword :: STORAGE_ENDPOINT ) {
929+ parser. expect_token ( & Token :: Eq ) ?;
930+ storage_endpoint = Some ( parser. parse_literal_string ( ) ?) ;
931+ } else if parser. parse_keyword ( Keyword :: USE_PRIVATELINK_ENDPOINT ) {
932+ parser. expect_token ( & Token :: Eq ) ?;
933+ use_private_link_endpoint = Some ( parser. parse_boolean_string ( ) ?) ;
934+ } else if parser. parse_keyword ( Keyword :: ENCRYPTION ) {
935+ parser. expect_token ( & Token :: Eq ) ?;
936+ encryption = KeyValueOptions {
937+ options : parse_parentheses_options ( parser) ?,
938+ } ;
939+ } else if parser. parse_keyword ( Keyword :: CREDENTIALS ) {
940+ parser. expect_token ( & Token :: Eq ) ?;
941+ credentials = KeyValueOptions {
942+ options : parse_parentheses_options ( parser) ?,
943+ } ;
944+ } else if parser. consume_token ( & Token :: RParen ) {
945+ break ;
946+ } else {
947+ return parser. expected ( "a valid key or closing paren" , parser. peek_token ( ) ) ;
948+ }
949+ }
950+
951+ let Some ( name) = name else {
952+ return parser. expected ( "NAME = '...'" , parser. peek_token ( ) ) ;
953+ } ;
954+
955+ let Some ( provider) = provider else {
956+ return parser. expected ( "STORAGE_PROVIDER = '...'" , parser. peek_token ( ) ) ;
957+ } ;
958+
959+ locations. push ( CloudProviderParams {
960+ name,
961+ provider,
962+ base_url,
963+ aws_role_arn,
964+ aws_access_point_arn,
965+ aws_external_id,
966+ azure_tenant_id,
967+ storage_endpoint,
968+ use_private_link_endpoint,
969+ encryption,
970+ credentials,
971+ } ) ;
972+ // EXIT if next token is RParen
973+ if parser. consume_token ( & Token :: RParen ) {
974+ break ;
975+ }
976+ // Otherwise expect a comma before next object
977+ parser. expect_token ( & Token :: Comma ) ?;
978+ }
979+ Ok ( locations)
980+ }
981+
843982pub fn parse_storage_serialization_policy (
844983 parser : & mut Parser ,
845984) -> Result < StorageSerializationPolicy , ParserError > {
0 commit comments