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 ;
2021use crate :: ast:: helpers:: key_value_options:: { KeyValueOption , KeyValueOptionType , KeyValueOptions } ;
@@ -24,10 +25,11 @@ use crate::ast::helpers::stmt_data_loading::{
2425 FileStagingCommand , StageLoadSelectItem , StageParamsObject ,
2526} ;
2627use crate :: ast:: {
27- CatalogSyncNamespaceMode , ColumnOption , ColumnPolicy , ColumnPolicyProperty , ContactEntry ,
28- CopyIntoSnowflakeKind , Ident , IdentityParameters , IdentityProperty , IdentityPropertyFormatKind ,
29- IdentityPropertyKind , IdentityPropertyOrder , ObjectName , RowAccessPolicy , ShowObjects ,
30- Statement , StorageSerializationPolicy , TagsColumnOption , WrappedCollection ,
28+ CatalogSyncNamespaceMode , CloudProviderParams , ColumnOption , ColumnPolicy ,
29+ ColumnPolicyProperty , ContactEntry , CopyIntoSnowflakeKind , Ident , IdentityParameters ,
30+ IdentityProperty , IdentityPropertyFormatKind , IdentityPropertyKind , IdentityPropertyOrder ,
31+ ObjectName , RowAccessPolicy , ShowObjects , Statement , StorageSerializationPolicy ,
32+ TagsColumnOption , WrappedCollection ,
3133} ;
3234use crate :: dialect:: { Dialect , Precedence } ;
3335use crate :: keywords:: Keyword ;
@@ -42,8 +44,6 @@ use alloc::vec::Vec;
4244#[ cfg( not( feature = "std" ) ) ]
4345use alloc:: { format, vec} ;
4446
45- use super :: keywords:: RESERVED_FOR_IDENTIFIER ;
46-
4747/// A [`Dialect`] for [Snowflake](https://www.snowflake.com/)
4848#[ derive( Debug , Default ) ]
4949pub struct SnowflakeDialect ;
@@ -179,6 +179,12 @@ impl Dialect for SnowflakeDialect {
179179 ) ) ;
180180 } else if parser. parse_keyword ( Keyword :: DATABASE ) {
181181 return Some ( parse_create_database ( or_replace, transient, parser) ) ;
182+ } else if parser. parse_keyword ( Keyword :: EXTERNAL ) {
183+ if parser. parse_keyword ( Keyword :: VOLUME ) {
184+ return Some ( parse_create_external_volume ( or_replace, parser) ) ;
185+ } else {
186+ parser. prev_token ( ) ;
187+ }
182188 } else {
183189 // need to go back with the cursor
184190 let mut back = 1 ;
@@ -702,6 +708,146 @@ pub fn parse_create_database(
702708 Ok ( builder. build ( ) )
703709}
704710
711+ fn parse_create_external_volume (
712+ or_replace : bool ,
713+ parser : & mut Parser ,
714+ ) -> Result < Statement , ParserError > {
715+ let if_not_exists = parser. parse_keywords ( & [ Keyword :: IF , Keyword :: NOT , Keyword :: EXISTS ] ) ;
716+ let name = parser. parse_object_name ( false ) ?;
717+ let mut comment = None ;
718+ let mut allow_writes = None ;
719+ let mut storage_locations = Vec :: new ( ) ;
720+
721+ // STORAGE_LOCATIONS (...)
722+ if parser. parse_keywords ( & [ Keyword :: STORAGE_LOCATIONS ] ) {
723+ parser. expect_token ( & Token :: Eq ) ?;
724+ storage_locations = parse_storage_locations ( parser) ?;
725+ } ;
726+
727+ // ALLOW_WRITES [ = true | false ]
728+ if parser. parse_keyword ( Keyword :: ALLOW_WRITES ) {
729+ parser. expect_token ( & Token :: Eq ) ?;
730+ allow_writes = Some ( parser. parse_boolean_string ( ) ?) ;
731+ }
732+
733+ // COMMENT = '...'
734+ if parser. parse_keyword ( Keyword :: COMMENT ) {
735+ parser. expect_token ( & Token :: Eq ) ?;
736+ comment = Some ( parser. parse_literal_string ( ) ?) ;
737+ }
738+
739+ if storage_locations. is_empty ( ) {
740+ return Err ( ParserError :: ParserError (
741+ "STORAGE_LOCATIONS is required for CREATE EXTERNAL VOLUME" . to_string ( ) ,
742+ ) ) ;
743+ }
744+
745+ Ok ( Statement :: CreateExternalVolume {
746+ or_replace,
747+ if_not_exists,
748+ name,
749+ allow_writes,
750+ comment,
751+ storage_locations,
752+ } )
753+ }
754+
755+ fn parse_storage_locations ( parser : & mut Parser ) -> Result < Vec < CloudProviderParams > , ParserError > {
756+ let mut locations = Vec :: new ( ) ;
757+ parser. expect_token ( & Token :: LParen ) ?;
758+
759+ loop {
760+ parser. expect_token ( & Token :: LParen ) ?;
761+
762+ // START OF ONE CloudProviderParams BLOCK
763+ let mut name = None ;
764+ let mut provider = None ;
765+ let mut base_url = None ;
766+ let mut aws_role_arn = None ;
767+ let mut aws_access_point_arn = None ;
768+ let mut aws_external_id = None ;
769+ let mut azure_tenant_id = None ;
770+ let mut storage_endpoint = None ;
771+ let mut use_private_link_endpoint = None ;
772+ let mut encryption: KeyValueOptions = KeyValueOptions { options : vec ! [ ] } ;
773+ let mut credentials: KeyValueOptions = KeyValueOptions { options : vec ! [ ] } ;
774+
775+ loop {
776+ if parser. parse_keyword ( Keyword :: NAME ) {
777+ parser. expect_token ( & Token :: Eq ) ?;
778+ name = Some ( parser. parse_literal_string ( ) ?) ;
779+ } else if parser. parse_keyword ( Keyword :: STORAGE_PROVIDER ) {
780+ parser. expect_token ( & Token :: Eq ) ?;
781+ provider = Some ( parser. parse_literal_string ( ) ?) ;
782+ } else if parser. parse_keyword ( Keyword :: STORAGE_BASE_URL ) {
783+ parser. expect_token ( & Token :: Eq ) ?;
784+ base_url = Some ( parser. parse_literal_string ( ) ?) ;
785+ } else if parser. parse_keyword ( Keyword :: STORAGE_AWS_ROLE_ARN ) {
786+ parser. expect_token ( & Token :: Eq ) ?;
787+ aws_role_arn = Some ( parser. parse_literal_string ( ) ?) ;
788+ } else if parser. parse_keyword ( Keyword :: STORAGE_AWS_ACCESS_POINT_ARN ) {
789+ parser. expect_token ( & Token :: Eq ) ?;
790+ aws_access_point_arn = Some ( parser. parse_literal_string ( ) ?) ;
791+ } else if parser. parse_keyword ( Keyword :: STORAGE_AWS_EXTERNAL_ID ) {
792+ parser. expect_token ( & Token :: Eq ) ?;
793+ aws_external_id = Some ( parser. parse_literal_string ( ) ?) ;
794+ } else if parser. parse_keyword ( Keyword :: AZURE_TENANT_ID ) {
795+ parser. expect_token ( & Token :: Eq ) ?;
796+ azure_tenant_id = Some ( parser. parse_literal_string ( ) ?) ;
797+ } else if parser. parse_keyword ( Keyword :: STORAGE_ENDPOINT ) {
798+ parser. expect_token ( & Token :: Eq ) ?;
799+ storage_endpoint = Some ( parser. parse_literal_string ( ) ?) ;
800+ } else if parser. parse_keyword ( Keyword :: USE_PRIVATELINK_ENDPOINT ) {
801+ parser. expect_token ( & Token :: Eq ) ?;
802+ use_private_link_endpoint = Some ( parser. parse_boolean_string ( ) ?) ;
803+ } else if parser. parse_keyword ( Keyword :: ENCRYPTION ) {
804+ parser. expect_token ( & Token :: Eq ) ?;
805+ encryption = KeyValueOptions {
806+ options : parse_parentheses_options ( parser) ?,
807+ } ;
808+ } else if parser. parse_keyword ( Keyword :: CREDENTIALS ) {
809+ parser. expect_token ( & Token :: Eq ) ?;
810+ credentials = KeyValueOptions {
811+ options : parse_parentheses_options ( parser) ?,
812+ } ;
813+ } else if parser. consume_token ( & Token :: RParen ) {
814+ break ;
815+ } else {
816+ return parser. expected ( "a valid key or closing paren" , parser. peek_token ( ) ) ;
817+ }
818+ }
819+
820+ let Some ( name) = name else {
821+ return parser. expected ( "NAME = '...'" , parser. peek_token ( ) ) ;
822+ } ;
823+
824+ let Some ( provider) = provider else {
825+ return parser. expected ( "STORAGE_PROVIDER = '...'" , parser. peek_token ( ) ) ;
826+ } ;
827+
828+ locations. push ( CloudProviderParams {
829+ name,
830+ provider,
831+ base_url,
832+ aws_role_arn,
833+ aws_access_point_arn,
834+ aws_external_id,
835+ azure_tenant_id,
836+ storage_endpoint,
837+ use_private_link_endpoint,
838+ encryption,
839+ credentials,
840+ } ) ;
841+ // EXIT if next token is RParen
842+ if parser. consume_token ( & Token :: RParen ) {
843+ break ;
844+ }
845+ // Otherwise expect a comma before next object
846+ parser. expect_token ( & Token :: Comma ) ?;
847+ }
848+ Ok ( locations)
849+ }
850+
705851pub fn parse_storage_serialization_policy (
706852 parser : & mut Parser ,
707853) -> Result < StorageSerializationPolicy , ParserError > {
0 commit comments