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,8 @@ 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_keywords ( & [ Keyword :: EXTERNAL , Keyword :: VOLUME ] ) {
183+ return Some ( parse_create_external_volume ( or_replace, parser) ) ;
182184 } else {
183185 // need to go back with the cursor
184186 let mut back = 1 ;
@@ -702,6 +704,146 @@ pub fn parse_create_database(
702704 Ok ( builder. build ( ) )
703705}
704706
707+ fn parse_create_external_volume (
708+ or_replace : bool ,
709+ parser : & mut Parser ,
710+ ) -> Result < Statement , ParserError > {
711+ let if_not_exists = parser. parse_keywords ( & [ Keyword :: IF , Keyword :: NOT , Keyword :: EXISTS ] ) ;
712+ let name = parser. parse_object_name ( false ) ?;
713+ let mut comment = None ;
714+ let mut allow_writes = None ;
715+ let mut storage_locations = Vec :: new ( ) ;
716+
717+ // STORAGE_LOCATIONS (...)
718+ if parser. parse_keywords ( & [ Keyword :: STORAGE_LOCATIONS ] ) {
719+ parser. expect_token ( & Token :: Eq ) ?;
720+ storage_locations = parse_storage_locations ( parser) ?;
721+ } ;
722+
723+ // ALLOW_WRITES [ = true | false ]
724+ if parser. parse_keyword ( Keyword :: ALLOW_WRITES ) {
725+ parser. expect_token ( & Token :: Eq ) ?;
726+ allow_writes = Some ( parser. parse_boolean_string ( ) ?) ;
727+ }
728+
729+ // COMMENT = '...'
730+ if parser. parse_keyword ( Keyword :: COMMENT ) {
731+ parser. expect_token ( & Token :: Eq ) ?;
732+ comment = Some ( parser. parse_literal_string ( ) ?) ;
733+ }
734+
735+ if storage_locations. is_empty ( ) {
736+ return Err ( ParserError :: ParserError (
737+ "STORAGE_LOCATIONS is required for CREATE EXTERNAL VOLUME" . to_string ( ) ,
738+ ) ) ;
739+ }
740+
741+ Ok ( Statement :: CreateExternalVolume {
742+ or_replace,
743+ if_not_exists,
744+ name,
745+ allow_writes,
746+ comment,
747+ storage_locations,
748+ } )
749+ }
750+
751+ fn parse_storage_locations ( parser : & mut Parser ) -> Result < Vec < CloudProviderParams > , ParserError > {
752+ let mut locations = Vec :: new ( ) ;
753+ parser. expect_token ( & Token :: LParen ) ?;
754+
755+ loop {
756+ parser. expect_token ( & Token :: LParen ) ?;
757+
758+ // START OF ONE CloudProviderParams BLOCK
759+ let mut name = None ;
760+ let mut provider = None ;
761+ let mut base_url = None ;
762+ let mut aws_role_arn = None ;
763+ let mut aws_access_point_arn = None ;
764+ let mut aws_external_id = None ;
765+ let mut azure_tenant_id = None ;
766+ let mut storage_endpoint = None ;
767+ let mut use_private_link_endpoint = None ;
768+ let mut encryption: KeyValueOptions = KeyValueOptions { options : vec ! [ ] } ;
769+ let mut credentials: KeyValueOptions = KeyValueOptions { options : vec ! [ ] } ;
770+
771+ loop {
772+ if parser. parse_keyword ( Keyword :: NAME ) {
773+ parser. expect_token ( & Token :: Eq ) ?;
774+ name = Some ( parser. parse_literal_string ( ) ?) ;
775+ } else if parser. parse_keyword ( Keyword :: STORAGE_PROVIDER ) {
776+ parser. expect_token ( & Token :: Eq ) ?;
777+ provider = Some ( parser. parse_literal_string ( ) ?) ;
778+ } else if parser. parse_keyword ( Keyword :: STORAGE_BASE_URL ) {
779+ parser. expect_token ( & Token :: Eq ) ?;
780+ base_url = Some ( parser. parse_literal_string ( ) ?) ;
781+ } else if parser. parse_keyword ( Keyword :: STORAGE_AWS_ROLE_ARN ) {
782+ parser. expect_token ( & Token :: Eq ) ?;
783+ aws_role_arn = Some ( parser. parse_literal_string ( ) ?) ;
784+ } else if parser. parse_keyword ( Keyword :: STORAGE_AWS_ACCESS_POINT_ARN ) {
785+ parser. expect_token ( & Token :: Eq ) ?;
786+ aws_access_point_arn = Some ( parser. parse_literal_string ( ) ?) ;
787+ } else if parser. parse_keyword ( Keyword :: STORAGE_AWS_EXTERNAL_ID ) {
788+ parser. expect_token ( & Token :: Eq ) ?;
789+ aws_external_id = Some ( parser. parse_literal_string ( ) ?) ;
790+ } else if parser. parse_keyword ( Keyword :: AZURE_TENANT_ID ) {
791+ parser. expect_token ( & Token :: Eq ) ?;
792+ azure_tenant_id = Some ( parser. parse_literal_string ( ) ?) ;
793+ } else if parser. parse_keyword ( Keyword :: STORAGE_ENDPOINT ) {
794+ parser. expect_token ( & Token :: Eq ) ?;
795+ storage_endpoint = Some ( parser. parse_literal_string ( ) ?) ;
796+ } else if parser. parse_keyword ( Keyword :: USE_PRIVATELINK_ENDPOINT ) {
797+ parser. expect_token ( & Token :: Eq ) ?;
798+ use_private_link_endpoint = Some ( parser. parse_boolean_string ( ) ?) ;
799+ } else if parser. parse_keyword ( Keyword :: ENCRYPTION ) {
800+ parser. expect_token ( & Token :: Eq ) ?;
801+ encryption = KeyValueOptions {
802+ options : parse_parentheses_options ( parser) ?,
803+ } ;
804+ } else if parser. parse_keyword ( Keyword :: CREDENTIALS ) {
805+ parser. expect_token ( & Token :: Eq ) ?;
806+ credentials = KeyValueOptions {
807+ options : parse_parentheses_options ( parser) ?,
808+ } ;
809+ } else if parser. consume_token ( & Token :: RParen ) {
810+ break ;
811+ } else {
812+ return parser. expected ( "a valid key or closing paren" , parser. peek_token ( ) ) ;
813+ }
814+ }
815+
816+ let Some ( name) = name else {
817+ return parser. expected ( "NAME = '...'" , parser. peek_token ( ) ) ;
818+ } ;
819+
820+ let Some ( provider) = provider else {
821+ return parser. expected ( "STORAGE_PROVIDER = '...'" , parser. peek_token ( ) ) ;
822+ } ;
823+
824+ locations. push ( CloudProviderParams {
825+ name,
826+ provider,
827+ base_url,
828+ aws_role_arn,
829+ aws_access_point_arn,
830+ aws_external_id,
831+ azure_tenant_id,
832+ storage_endpoint,
833+ use_private_link_endpoint,
834+ encryption,
835+ credentials,
836+ } ) ;
837+ // EXIT if next token is RParen
838+ if parser. consume_token ( & Token :: RParen ) {
839+ break ;
840+ }
841+ // Otherwise expect a comma before next object
842+ parser. expect_token ( & Token :: Comma ) ?;
843+ }
844+ Ok ( locations)
845+ }
846+
705847pub fn parse_storage_serialization_policy (
706848 parser : & mut Parser ,
707849) -> Result < StorageSerializationPolicy , ParserError > {
0 commit comments