@@ -458,6 +458,9 @@ impl Dialect for SnowflakeDialect {
458458 if parser. parse_keywords ( & [ Keyword :: FILE , Keyword :: FORMATS ] ) {
459459 return Some ( parse_show_file_formats ( terse, parser) ) ;
460460 }
461+ if parser. parse_keyword ( Keyword :: STAGES ) {
462+ return Some ( parse_show_stages ( terse, parser) ) ;
463+ }
461464 //Give back Keyword::TERSE
462465 if terse {
463466 parser. prev_token ( ) ;
@@ -1333,58 +1336,91 @@ struct StageProperties {
13331336/// Parse the stage property groups (`internalStageParams`/`externalStageParams`,
13341337/// `DIRECTORY`, `FILE_FORMAT`, `COPY_OPTIONS`, `COMMENT`) shared by
13351338/// `CREATE STAGE` and `ALTER STAGE ... SET`.
1339+ ///
1340+ /// Snowflake accepts these groups in any order (e.g. `FILE_FORMAT = (...) URL =
1341+ /// '...'`), so the groups are matched in a loop until none remains rather than
1342+ /// in a fixed sequence. Each group may appear at most once.
13361343fn parse_stage_properties ( parser : & mut Parser ) -> Result < StageProperties , ParserError > {
1337- // [ internalStageParams | externalStageParams ]
1338- let stage_params = parse_stage_params ( parser) ?;
1339-
1344+ let empty_options = || KeyValueOptions {
1345+ options : vec ! [ ] ,
1346+ delimiter : KeyValueOptionsDelimiter :: Space ,
1347+ } ;
1348+ let ( mut url, mut storage_integration, mut endpoint) = ( None , None , None ) ;
1349+ let mut encryption = empty_options ( ) ;
1350+ let mut credentials = empty_options ( ) ;
13401351 let mut directory_table_params = Vec :: new ( ) ;
13411352 let mut file_format = Vec :: new ( ) ;
13421353 let mut copy_options = Vec :: new ( ) ;
13431354 let mut comment = None ;
13441355
1345- // [ directoryTableParams ]
1346- if parser. parse_keyword ( Keyword :: DIRECTORY ) {
1347- parser. expect_token ( & Token :: Eq ) ?;
1348- directory_table_params = parser. parse_key_value_options ( true , & [ ] ) ?. options ;
1349- }
1350-
1351- // [ file_format]
1352- if parser. parse_keyword ( Keyword :: FILE_FORMAT ) {
1353- parser. expect_token ( & Token :: Eq ) ?;
1354- if parser. peek_token ( ) . token == Token :: LParen {
1355- file_format = parser. parse_key_value_options ( true , & [ ] ) ?. options ;
1356+ loop {
1357+ // [ internalStageParams | externalStageParams ]
1358+ if url. is_none ( ) && parser. parse_keyword ( Keyword :: URL ) {
1359+ parser. expect_token ( & Token :: Eq ) ?;
1360+ url = Some ( match parser. next_token ( ) . token {
1361+ Token :: SingleQuotedString ( word) => Ok ( word) ,
1362+ _ => parser. expected_ref ( "a URL statement" , parser. peek_token_ref ( ) ) ,
1363+ } ?) ;
1364+ } else if storage_integration. is_none ( )
1365+ && parser. parse_keyword ( Keyword :: STORAGE_INTEGRATION )
1366+ {
1367+ parser. expect_token ( & Token :: Eq ) ?;
1368+ storage_integration = Some ( parser. next_token ( ) . token . to_string ( ) ) ;
1369+ } else if endpoint. is_none ( ) && parser. parse_keyword ( Keyword :: ENDPOINT ) {
1370+ parser. expect_token ( & Token :: Eq ) ?;
1371+ endpoint = Some ( match parser. next_token ( ) . token {
1372+ Token :: SingleQuotedString ( word) => Ok ( word) ,
1373+ _ => parser. expected_ref ( "an endpoint statement" , parser. peek_token_ref ( ) ) ,
1374+ } ?) ;
1375+ } else if credentials. options . is_empty ( ) && parser. parse_keyword ( Keyword :: CREDENTIALS ) {
1376+ parser. expect_token ( & Token :: Eq ) ?;
1377+ credentials. options = parser. parse_key_value_options ( true , & [ ] ) ?. options ;
1378+ } else if encryption. options . is_empty ( ) && parser. parse_keyword ( Keyword :: ENCRYPTION ) {
1379+ parser. expect_token ( & Token :: Eq ) ?;
1380+ encryption. options = parser. parse_key_value_options ( true , & [ ] ) ?. options ;
1381+ } else if directory_table_params. is_empty ( ) && parser. parse_keyword ( Keyword :: DIRECTORY ) {
1382+ parser. expect_token ( & Token :: Eq ) ?;
1383+ directory_table_params = parser. parse_key_value_options ( true , & [ ] ) ?. options ;
1384+ } else if file_format. is_empty ( ) && parser. parse_keyword ( Keyword :: FILE_FORMAT ) {
1385+ parser. expect_token ( & Token :: Eq ) ?;
1386+ if parser. peek_token ( ) . token == Token :: LParen {
1387+ file_format = parser. parse_key_value_options ( true , & [ ] ) ?. options ;
1388+ } else {
1389+ // Shorthand `FILE_FORMAT = '<name>'` / `FILE_FORMAT = <ident>`
1390+ // is sugar for `FILE_FORMAT = (FORMAT_NAME = <name>)` —
1391+ // normalize it.
1392+ let tok = parser. peek_token ( ) ;
1393+ let value = match tok. token {
1394+ Token :: Word ( w) => {
1395+ parser. next_token ( ) ;
1396+ Value :: Placeholder ( w. value . clone ( ) ) . with_span ( tok. span )
1397+ }
1398+ _ => parser. parse_value ( ) ?,
1399+ } ;
1400+ file_format = vec ! [ KeyValueOption {
1401+ option_name: "FORMAT_NAME" . to_string( ) ,
1402+ option_value: KeyValueOptionKind :: Single ( value) ,
1403+ } ] ;
1404+ }
1405+ } else if copy_options. is_empty ( ) && parser. parse_keyword ( Keyword :: COPY_OPTIONS ) {
1406+ parser. expect_token ( & Token :: Eq ) ?;
1407+ copy_options = parser. parse_key_value_options ( true , & [ ] ) ?. options ;
1408+ } else if comment. is_none ( ) && parser. parse_keyword ( Keyword :: COMMENT ) {
1409+ parser. expect_token ( & Token :: Eq ) ?;
1410+ comment = Some ( parser. parse_comment_value ( ) ?) ;
13561411 } else {
1357- // Shorthand `FILE_FORMAT = '<name>'` / `FILE_FORMAT = <ident>` is
1358- // sugar for `FILE_FORMAT = (FORMAT_NAME = <name>)` — normalize it.
1359- let tok = parser. peek_token ( ) ;
1360- let value = match tok. token {
1361- Token :: Word ( w) => {
1362- parser. next_token ( ) ;
1363- Value :: Placeholder ( w. value . clone ( ) ) . with_span ( tok. span )
1364- }
1365- _ => parser. parse_value ( ) ?,
1366- } ;
1367- file_format = vec ! [ KeyValueOption {
1368- option_name: "FORMAT_NAME" . to_string( ) ,
1369- option_value: KeyValueOptionKind :: Single ( value) ,
1370- } ] ;
1412+ break ;
13711413 }
13721414 }
13731415
1374- // [ copy_options ]
1375- if parser. parse_keyword ( Keyword :: COPY_OPTIONS ) {
1376- parser. expect_token ( & Token :: Eq ) ?;
1377- copy_options = parser. parse_key_value_options ( true , & [ ] ) ?. options ;
1378- }
1379-
1380- // [ comment ]
1381- if parser. parse_keyword ( Keyword :: COMMENT ) {
1382- parser. expect_token ( & Token :: Eq ) ?;
1383- comment = Some ( parser. parse_comment_value ( ) ?) ;
1384- }
1385-
13861416 Ok ( StageProperties {
1387- stage_params,
1417+ stage_params : StageParamsObject {
1418+ url,
1419+ encryption,
1420+ endpoint,
1421+ storage_integration,
1422+ credentials,
1423+ } ,
13881424 directory_table_params : KeyValueOptions {
13891425 options : directory_table_params,
13901426 delimiter : KeyValueOptionsDelimiter :: Space ,
@@ -2472,6 +2508,15 @@ fn parse_show_file_formats(terse: bool, parser: &mut Parser) -> Result<Statement
24722508 } )
24732509}
24742510
2511+ /// Parse `SHOW [TERSE] STAGES [ ... ]`
2512+ fn parse_show_stages ( terse : bool , parser : & mut Parser ) -> Result < Statement , ParserError > {
2513+ let show_options = parser. parse_show_stmt_options ( ) ?;
2514+ Ok ( Statement :: ShowStages {
2515+ terse,
2516+ show_options,
2517+ } )
2518+ }
2519+
24752520/// Parse `DESC[RIBE] WAREHOUSE <name>`
24762521fn parse_describe_warehouse ( parser : & mut Parser ) -> Result < Statement , ParserError > {
24772522 let name = parser. parse_object_name ( false ) ?;
0 commit comments