Thanks to visit codestin.com
Credit goes to docs.rs

sqlparser/dialect/
snowflake.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18#[cfg(not(feature = "std"))]
19use crate::alloc::string::ToString;
20use crate::ast::helpers::key_value_options::{KeyValueOption, KeyValueOptionType, KeyValueOptions};
21use crate::ast::helpers::stmt_create_table::CreateTableBuilder;
22use crate::ast::helpers::stmt_data_loading::{
23    FileStagingCommand, StageLoadSelectItem, StageLoadSelectItemKind, StageParamsObject,
24};
25use crate::ast::{
26    ColumnOption, ColumnPolicy, ColumnPolicyProperty, CopyIntoSnowflakeKind, Ident,
27    IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind,
28    IdentityPropertyOrder, ObjectName, RowAccessPolicy, ShowObjects, Statement, TagsColumnOption,
29    WrappedCollection,
30};
31use crate::dialect::{Dialect, Precedence};
32use crate::keywords::Keyword;
33use crate::parser::{IsOptional, Parser, ParserError};
34use crate::tokenizer::{Token, Word};
35#[cfg(not(feature = "std"))]
36use alloc::boxed::Box;
37#[cfg(not(feature = "std"))]
38use alloc::string::String;
39#[cfg(not(feature = "std"))]
40use alloc::vec::Vec;
41#[cfg(not(feature = "std"))]
42use alloc::{format, vec};
43
44use super::keywords::RESERVED_FOR_IDENTIFIER;
45use sqlparser::ast::StorageSerializationPolicy;
46
47const RESERVED_KEYWORDS_FOR_SELECT_ITEM_OPERATOR: [Keyword; 1] = [Keyword::CONNECT_BY_ROOT];
48/// A [`Dialect`] for [Snowflake](https://www.snowflake.com/)
49#[derive(Debug, Default)]
50pub struct SnowflakeDialect;
51
52impl Dialect for SnowflakeDialect {
53    // see https://docs.snowflake.com/en/sql-reference/identifiers-syntax.html
54    fn is_identifier_start(&self, ch: char) -> bool {
55        ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch == '_'
56    }
57
58    fn supports_projection_trailing_commas(&self) -> bool {
59        true
60    }
61
62    fn supports_from_trailing_commas(&self) -> bool {
63        true
64    }
65
66    // Snowflake supports double-dot notation when the schema name is not specified
67    // In this case the default PUBLIC schema is used
68    //
69    // see https://docs.snowflake.com/en/sql-reference/name-resolution#resolution-when-schema-omitted-double-dot-notation
70    fn supports_object_name_double_dot_notation(&self) -> bool {
71        true
72    }
73
74    fn is_identifier_part(&self, ch: char) -> bool {
75        ch.is_ascii_lowercase()
76            || ch.is_ascii_uppercase()
77            || ch.is_ascii_digit()
78            || ch == '$'
79            || ch == '_'
80    }
81
82    // See https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#escape_sequences
83    fn supports_string_literal_backslash_escape(&self) -> bool {
84        true
85    }
86
87    fn supports_within_after_array_aggregation(&self) -> bool {
88        true
89    }
90
91    /// See <https://docs.snowflake.com/en/sql-reference/constructs/where#joins-in-the-where-clause>
92    fn supports_outer_join_operator(&self) -> bool {
93        true
94    }
95
96    fn supports_connect_by(&self) -> bool {
97        true
98    }
99
100    /// See <https://docs.snowflake.com/en/sql-reference/sql/execute-immediate>
101    fn supports_execute_immediate(&self) -> bool {
102        true
103    }
104
105    fn supports_match_recognize(&self) -> bool {
106        true
107    }
108
109    // Snowflake uses this syntax for "object constants" (the values of which
110    // are not actually required to be constants).
111    //
112    // https://docs.snowflake.com/en/sql-reference/data-types-semistructured#label-object-constant
113    fn supports_dictionary_syntax(&self) -> bool {
114        true
115    }
116
117    // Snowflake doesn't document this but `FIRST_VALUE(arg, { IGNORE | RESPECT } NULLS)`
118    // works (i.e. inside the argument list instead of after).
119    fn supports_window_function_null_treatment_arg(&self) -> bool {
120        true
121    }
122
123    /// See [doc](https://docs.snowflake.com/en/sql-reference/sql/set#syntax)
124    fn supports_parenthesized_set_variables(&self) -> bool {
125        true
126    }
127
128    /// See [doc](https://docs.snowflake.com/en/sql-reference/sql/comment)
129    fn supports_comment_on(&self) -> bool {
130        true
131    }
132
133    fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
134        if parser.parse_keywords(&[Keyword::ALTER, Keyword::SESSION]) {
135            // ALTER SESSION
136            let set = match parser.parse_one_of_keywords(&[Keyword::SET, Keyword::UNSET]) {
137                Some(Keyword::SET) => true,
138                Some(Keyword::UNSET) => false,
139                _ => return Some(parser.expected("SET or UNSET", parser.peek_token())),
140            };
141            return Some(parse_alter_session(parser, set));
142        }
143
144        if parser.parse_keyword(Keyword::CREATE) {
145            // possibly CREATE STAGE
146            //[ OR  REPLACE ]
147            let or_replace = parser.parse_keywords(&[Keyword::OR, Keyword::REPLACE]);
148            // LOCAL | GLOBAL
149            let global = match parser.parse_one_of_keywords(&[Keyword::LOCAL, Keyword::GLOBAL]) {
150                Some(Keyword::LOCAL) => Some(false),
151                Some(Keyword::GLOBAL) => Some(true),
152                _ => None,
153            };
154
155            let mut temporary = false;
156            let mut volatile = false;
157            let mut transient = false;
158            let mut iceberg = false;
159
160            match parser.parse_one_of_keywords(&[
161                Keyword::TEMP,
162                Keyword::TEMPORARY,
163                Keyword::VOLATILE,
164                Keyword::TRANSIENT,
165                Keyword::ICEBERG,
166            ]) {
167                Some(Keyword::TEMP | Keyword::TEMPORARY) => temporary = true,
168                Some(Keyword::VOLATILE) => volatile = true,
169                Some(Keyword::TRANSIENT) => transient = true,
170                Some(Keyword::ICEBERG) => iceberg = true,
171                _ => {}
172            }
173
174            if parser.parse_keyword(Keyword::STAGE) {
175                // OK - this is CREATE STAGE statement
176                return Some(parse_create_stage(or_replace, temporary, parser));
177            } else if parser.parse_keyword(Keyword::TABLE) {
178                return Some(parse_create_table(
179                    or_replace, global, temporary, volatile, transient, iceberg, parser,
180                ));
181            } else {
182                // need to go back with the cursor
183                let mut back = 1;
184                if or_replace {
185                    back += 2
186                }
187                if temporary {
188                    back += 1
189                }
190                for _i in 0..back {
191                    parser.prev_token();
192                }
193            }
194        }
195        if parser.parse_keywords(&[Keyword::COPY, Keyword::INTO]) {
196            // COPY INTO
197            return Some(parse_copy_into(parser));
198        }
199
200        if let Some(kw) = parser.parse_one_of_keywords(&[
201            Keyword::LIST,
202            Keyword::LS,
203            Keyword::REMOVE,
204            Keyword::RM,
205        ]) {
206            return Some(parse_file_staging_command(kw, parser));
207        }
208
209        if parser.parse_keyword(Keyword::SHOW) {
210            let terse = parser.parse_keyword(Keyword::TERSE);
211            if parser.parse_keyword(Keyword::OBJECTS) {
212                return Some(parse_show_objects(terse, parser));
213            }
214            //Give back Keyword::TERSE
215            if terse {
216                parser.prev_token();
217            }
218            //Give back Keyword::SHOW
219            parser.prev_token();
220        }
221
222        None
223    }
224
225    fn parse_column_option(
226        &self,
227        parser: &mut Parser,
228    ) -> Result<Option<Result<Option<ColumnOption>, ParserError>>, ParserError> {
229        parser.maybe_parse(|parser| {
230            let with = parser.parse_keyword(Keyword::WITH);
231
232            if parser.parse_keyword(Keyword::IDENTITY) {
233                Ok(parse_identity_property(parser)
234                    .map(|p| Some(ColumnOption::Identity(IdentityPropertyKind::Identity(p)))))
235            } else if parser.parse_keyword(Keyword::AUTOINCREMENT) {
236                Ok(parse_identity_property(parser).map(|p| {
237                    Some(ColumnOption::Identity(IdentityPropertyKind::Autoincrement(
238                        p,
239                    )))
240                }))
241            } else if parser.parse_keywords(&[Keyword::MASKING, Keyword::POLICY]) {
242                Ok(parse_column_policy_property(parser, with)
243                    .map(|p| Some(ColumnOption::Policy(ColumnPolicy::MaskingPolicy(p)))))
244            } else if parser.parse_keywords(&[Keyword::PROJECTION, Keyword::POLICY]) {
245                Ok(parse_column_policy_property(parser, with)
246                    .map(|p| Some(ColumnOption::Policy(ColumnPolicy::ProjectionPolicy(p)))))
247            } else if parser.parse_keywords(&[Keyword::TAG]) {
248                Ok(parse_column_tags(parser, with).map(|p| Some(ColumnOption::Tags(p))))
249            } else {
250                Err(ParserError::ParserError("not found match".to_string()))
251            }
252        })
253    }
254
255    fn get_next_precedence(&self, parser: &Parser) -> Option<Result<u8, ParserError>> {
256        let token = parser.peek_token();
257        // Snowflake supports the `:` cast operator unlike other dialects
258        match token.token {
259            Token::Colon => Some(Ok(self.prec_value(Precedence::DoubleColon))),
260            _ => None,
261        }
262    }
263
264    fn describe_requires_table_keyword(&self) -> bool {
265        true
266    }
267
268    fn allow_extract_custom(&self) -> bool {
269        true
270    }
271
272    fn allow_extract_single_quotes(&self) -> bool {
273        true
274    }
275
276    /// Snowflake expects the `LIKE` option before the `IN` option,
277    /// for example: <https://docs.snowflake.com/en/sql-reference/sql/show-views#syntax>
278    fn supports_show_like_before_in(&self) -> bool {
279        true
280    }
281
282    fn is_reserved_for_identifier(&self, kw: Keyword) -> bool {
283        // Unreserve some keywords that Snowflake accepts as identifiers
284        // See: https://docs.snowflake.com/en/sql-reference/reserved-keywords
285        if matches!(kw, Keyword::INTERVAL) {
286            false
287        } else {
288            RESERVED_FOR_IDENTIFIER.contains(&kw)
289        }
290    }
291
292    fn supports_partiql(&self) -> bool {
293        true
294    }
295
296    fn is_select_item_alias(&self, explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool {
297        explicit
298            || match kw {
299            // The following keywords can be considered an alias as long as 
300            // they are not followed by other tokens that may change their meaning
301            // e.g. `SELECT * EXCEPT (col1) FROM tbl`
302            Keyword::EXCEPT
303            // e.g. `SELECT 1 LIMIT 5`
304            | Keyword::LIMIT
305            // e.g. `SELECT 1 OFFSET 5 ROWS`
306            | Keyword::OFFSET
307            // e.g. `INSERT INTO t SELECT 1 RETURNING *`
308            | Keyword::RETURNING if !matches!(parser.peek_token_ref().token, Token::Comma | Token::EOF) =>
309            {
310                false
311            }
312
313            // `FETCH` can be considered an alias as long as it's not followed by `FIRST`` or `NEXT`
314            // which would give it a different meanings, for example: `SELECT 1 FETCH FIRST 10 ROWS` - not an alias
315            Keyword::FETCH
316                if parser.peek_keyword(Keyword::FIRST) || parser.peek_keyword(Keyword::NEXT) =>
317            {
318                false
319            }
320
321            // Reserved keywords by the Snowflake dialect, which seem to be less strictive 
322            // than what is listed in `keywords::RESERVED_FOR_COLUMN_ALIAS`. The following 
323            // keywords were tested with the this statement: `SELECT 1 <KW>`.
324            Keyword::FROM
325            | Keyword::GROUP
326            | Keyword::HAVING
327            | Keyword::INTERSECT
328            | Keyword::INTO
329            | Keyword::MINUS
330            | Keyword::ORDER
331            | Keyword::SELECT
332            | Keyword::UNION
333            | Keyword::WHERE
334            | Keyword::WITH => false,
335
336            // Any other word is considered an alias
337            _ => true,
338        }
339    }
340
341    /// See: <https://docs.snowflake.com/en/sql-reference/constructs/at-before>
342    fn supports_timestamp_versioning(&self) -> bool {
343        true
344    }
345
346    /// See: <https://docs.snowflake.com/en/sql-reference/constructs/group-by>
347    fn supports_group_by_expr(&self) -> bool {
348        true
349    }
350
351    /// See: <https://docs.snowflake.com/en/sql-reference/constructs/connect-by>
352    fn get_reserved_keywords_for_select_item_operator(&self) -> &[Keyword] {
353        &RESERVED_KEYWORDS_FOR_SELECT_ITEM_OPERATOR
354    }
355}
356
357fn parse_file_staging_command(kw: Keyword, parser: &mut Parser) -> Result<Statement, ParserError> {
358    let stage = parse_snowflake_stage_name(parser)?;
359    let pattern = if parser.parse_keyword(Keyword::PATTERN) {
360        parser.expect_token(&Token::Eq)?;
361        Some(parser.parse_literal_string()?)
362    } else {
363        None
364    };
365
366    match kw {
367        Keyword::LIST | Keyword::LS => Ok(Statement::List(FileStagingCommand { stage, pattern })),
368        Keyword::REMOVE | Keyword::RM => {
369            Ok(Statement::Remove(FileStagingCommand { stage, pattern }))
370        }
371        _ => Err(ParserError::ParserError(
372            "unexpected stage command, expecting LIST, LS, REMOVE or RM".to_string(),
373        )),
374    }
375}
376
377/// Parse snowflake alter session.
378/// <https://docs.snowflake.com/en/sql-reference/sql/alter-session>
379fn parse_alter_session(parser: &mut Parser, set: bool) -> Result<Statement, ParserError> {
380    let session_options = parse_session_options(parser, set)?;
381    Ok(Statement::AlterSession {
382        set,
383        session_params: KeyValueOptions {
384            options: session_options,
385        },
386    })
387}
388
389/// Parse snowflake create table statement.
390/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
391/// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
392pub fn parse_create_table(
393    or_replace: bool,
394    global: Option<bool>,
395    temporary: bool,
396    volatile: bool,
397    transient: bool,
398    iceberg: bool,
399    parser: &mut Parser,
400) -> Result<Statement, ParserError> {
401    let if_not_exists = parser.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
402    let table_name = parser.parse_object_name(false)?;
403
404    let mut builder = CreateTableBuilder::new(table_name)
405        .or_replace(or_replace)
406        .if_not_exists(if_not_exists)
407        .temporary(temporary)
408        .transient(transient)
409        .volatile(volatile)
410        .iceberg(iceberg)
411        .global(global)
412        .hive_formats(Some(Default::default()));
413
414    // Snowflake does not enforce order of the parameters in the statement. The parser needs to
415    // parse the statement in a loop.
416    //
417    // "CREATE TABLE x COPY GRANTS (c INT)" and "CREATE TABLE x (c INT) COPY GRANTS" are both
418    // accepted by Snowflake
419
420    loop {
421        let next_token = parser.next_token();
422        match &next_token.token {
423            Token::Word(word) => match word.keyword {
424                Keyword::COPY => {
425                    parser.expect_keyword_is(Keyword::GRANTS)?;
426                    builder = builder.copy_grants(true);
427                }
428                Keyword::COMMENT => {
429                    // Rewind the COMMENT keyword
430                    parser.prev_token();
431                    builder = builder.comment(parser.parse_optional_inline_comment()?);
432                }
433                Keyword::AS => {
434                    let query = parser.parse_query()?;
435                    builder = builder.query(Some(query));
436                    break;
437                }
438                Keyword::CLONE => {
439                    let clone = parser.parse_object_name(false).ok();
440                    builder = builder.clone_clause(clone);
441                    break;
442                }
443                Keyword::LIKE => {
444                    let like = parser.parse_object_name(false).ok();
445                    builder = builder.like(like);
446                    break;
447                }
448                Keyword::CLUSTER => {
449                    parser.expect_keyword_is(Keyword::BY)?;
450                    parser.expect_token(&Token::LParen)?;
451                    let cluster_by = Some(WrappedCollection::Parentheses(
452                        parser.parse_comma_separated(|p| p.parse_identifier())?,
453                    ));
454                    parser.expect_token(&Token::RParen)?;
455
456                    builder = builder.cluster_by(cluster_by)
457                }
458                Keyword::ENABLE_SCHEMA_EVOLUTION => {
459                    parser.expect_token(&Token::Eq)?;
460                    let enable_schema_evolution =
461                        match parser.parse_one_of_keywords(&[Keyword::TRUE, Keyword::FALSE]) {
462                            Some(Keyword::TRUE) => true,
463                            Some(Keyword::FALSE) => false,
464                            _ => {
465                                return parser.expected("TRUE or FALSE", next_token);
466                            }
467                        };
468
469                    builder = builder.enable_schema_evolution(Some(enable_schema_evolution));
470                }
471                Keyword::CHANGE_TRACKING => {
472                    parser.expect_token(&Token::Eq)?;
473                    let change_tracking =
474                        match parser.parse_one_of_keywords(&[Keyword::TRUE, Keyword::FALSE]) {
475                            Some(Keyword::TRUE) => true,
476                            Some(Keyword::FALSE) => false,
477                            _ => {
478                                return parser.expected("TRUE or FALSE", next_token);
479                            }
480                        };
481
482                    builder = builder.change_tracking(Some(change_tracking));
483                }
484                Keyword::DATA_RETENTION_TIME_IN_DAYS => {
485                    parser.expect_token(&Token::Eq)?;
486                    let data_retention_time_in_days = parser.parse_literal_uint()?;
487                    builder =
488                        builder.data_retention_time_in_days(Some(data_retention_time_in_days));
489                }
490                Keyword::MAX_DATA_EXTENSION_TIME_IN_DAYS => {
491                    parser.expect_token(&Token::Eq)?;
492                    let max_data_extension_time_in_days = parser.parse_literal_uint()?;
493                    builder = builder
494                        .max_data_extension_time_in_days(Some(max_data_extension_time_in_days));
495                }
496                Keyword::DEFAULT_DDL_COLLATION => {
497                    parser.expect_token(&Token::Eq)?;
498                    let default_ddl_collation = parser.parse_literal_string()?;
499                    builder = builder.default_ddl_collation(Some(default_ddl_collation));
500                }
501                // WITH is optional, we just verify that next token is one of the expected ones and
502                // fallback to the default match statement
503                Keyword::WITH => {
504                    parser.expect_one_of_keywords(&[
505                        Keyword::AGGREGATION,
506                        Keyword::TAG,
507                        Keyword::ROW,
508                    ])?;
509                    parser.prev_token();
510                }
511                Keyword::AGGREGATION => {
512                    parser.expect_keyword_is(Keyword::POLICY)?;
513                    let aggregation_policy = parser.parse_object_name(false)?;
514                    builder = builder.with_aggregation_policy(Some(aggregation_policy));
515                }
516                Keyword::ROW => {
517                    parser.expect_keywords(&[Keyword::ACCESS, Keyword::POLICY])?;
518                    let policy = parser.parse_object_name(false)?;
519                    parser.expect_keyword_is(Keyword::ON)?;
520                    parser.expect_token(&Token::LParen)?;
521                    let columns = parser.parse_comma_separated(|p| p.parse_identifier())?;
522                    parser.expect_token(&Token::RParen)?;
523
524                    builder =
525                        builder.with_row_access_policy(Some(RowAccessPolicy::new(policy, columns)))
526                }
527                Keyword::TAG => {
528                    parser.expect_token(&Token::LParen)?;
529                    let tags = parser.parse_comma_separated(Parser::parse_tag)?;
530                    parser.expect_token(&Token::RParen)?;
531                    builder = builder.with_tags(Some(tags));
532                }
533                Keyword::ON if parser.parse_keyword(Keyword::COMMIT) => {
534                    let on_commit = Some(parser.parse_create_table_on_commit()?);
535                    builder = builder.on_commit(on_commit);
536                }
537                Keyword::EXTERNAL_VOLUME => {
538                    parser.expect_token(&Token::Eq)?;
539                    builder.external_volume = Some(parser.parse_literal_string()?);
540                }
541                Keyword::CATALOG => {
542                    parser.expect_token(&Token::Eq)?;
543                    builder.catalog = Some(parser.parse_literal_string()?);
544                }
545                Keyword::BASE_LOCATION => {
546                    parser.expect_token(&Token::Eq)?;
547                    builder.base_location = Some(parser.parse_literal_string()?);
548                }
549                Keyword::CATALOG_SYNC => {
550                    parser.expect_token(&Token::Eq)?;
551                    builder.catalog_sync = Some(parser.parse_literal_string()?);
552                }
553                Keyword::STORAGE_SERIALIZATION_POLICY => {
554                    parser.expect_token(&Token::Eq)?;
555
556                    builder.storage_serialization_policy =
557                        Some(parse_storage_serialization_policy(parser)?);
558                }
559                _ => {
560                    return parser.expected("end of statement", next_token);
561                }
562            },
563            Token::LParen => {
564                parser.prev_token();
565                let (columns, constraints) = parser.parse_columns()?;
566                builder = builder.columns(columns).constraints(constraints);
567            }
568            Token::EOF => {
569                if builder.columns.is_empty() {
570                    return Err(ParserError::ParserError(
571                        "unexpected end of input".to_string(),
572                    ));
573                }
574
575                break;
576            }
577            Token::SemiColon => {
578                if builder.columns.is_empty() {
579                    return Err(ParserError::ParserError(
580                        "unexpected end of input".to_string(),
581                    ));
582                }
583
584                parser.prev_token();
585                break;
586            }
587            _ => {
588                return parser.expected("end of statement", next_token);
589            }
590        }
591    }
592
593    if iceberg && builder.base_location.is_none() {
594        return Err(ParserError::ParserError(
595            "BASE_LOCATION is required for ICEBERG tables".to_string(),
596        ));
597    }
598
599    Ok(builder.build())
600}
601
602pub fn parse_storage_serialization_policy(
603    parser: &mut Parser,
604) -> Result<StorageSerializationPolicy, ParserError> {
605    let next_token = parser.next_token();
606    match &next_token.token {
607        Token::Word(w) => match w.keyword {
608            Keyword::COMPATIBLE => Ok(StorageSerializationPolicy::Compatible),
609            Keyword::OPTIMIZED => Ok(StorageSerializationPolicy::Optimized),
610            _ => parser.expected("storage_serialization_policy", next_token),
611        },
612        _ => parser.expected("storage_serialization_policy", next_token),
613    }
614}
615
616pub fn parse_create_stage(
617    or_replace: bool,
618    temporary: bool,
619    parser: &mut Parser,
620) -> Result<Statement, ParserError> {
621    //[ IF NOT EXISTS ]
622    let if_not_exists = parser.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
623    let name = parser.parse_object_name(false)?;
624    let mut directory_table_params = Vec::new();
625    let mut file_format = Vec::new();
626    let mut copy_options = Vec::new();
627    let mut comment = None;
628
629    // [ internalStageParams | externalStageParams ]
630    let stage_params = parse_stage_params(parser)?;
631
632    // [ directoryTableParams ]
633    if parser.parse_keyword(Keyword::DIRECTORY) {
634        parser.expect_token(&Token::Eq)?;
635        directory_table_params = parse_parentheses_options(parser)?;
636    }
637
638    // [ file_format]
639    if parser.parse_keyword(Keyword::FILE_FORMAT) {
640        parser.expect_token(&Token::Eq)?;
641        file_format = parse_parentheses_options(parser)?;
642    }
643
644    // [ copy_options ]
645    if parser.parse_keyword(Keyword::COPY_OPTIONS) {
646        parser.expect_token(&Token::Eq)?;
647        copy_options = parse_parentheses_options(parser)?;
648    }
649
650    // [ comment ]
651    if parser.parse_keyword(Keyword::COMMENT) {
652        parser.expect_token(&Token::Eq)?;
653        comment = Some(parser.parse_comment_value()?);
654    }
655
656    Ok(Statement::CreateStage {
657        or_replace,
658        temporary,
659        if_not_exists,
660        name,
661        stage_params,
662        directory_table_params: KeyValueOptions {
663            options: directory_table_params,
664        },
665        file_format: KeyValueOptions {
666            options: file_format,
667        },
668        copy_options: KeyValueOptions {
669            options: copy_options,
670        },
671        comment,
672    })
673}
674
675pub fn parse_stage_name_identifier(parser: &mut Parser) -> Result<Ident, ParserError> {
676    let mut ident = String::new();
677    while let Some(next_token) = parser.next_token_no_skip() {
678        match &next_token.token {
679            Token::Whitespace(_) | Token::SemiColon => break,
680            Token::Period => {
681                parser.prev_token();
682                break;
683            }
684            Token::RParen => {
685                parser.prev_token();
686                break;
687            }
688            Token::AtSign => ident.push('@'),
689            Token::Tilde => ident.push('~'),
690            Token::Mod => ident.push('%'),
691            Token::Div => ident.push('/'),
692            Token::Word(w) => ident.push_str(&w.to_string()),
693            _ => return parser.expected("stage name identifier", parser.peek_token()),
694        }
695    }
696    Ok(Ident::new(ident))
697}
698
699pub fn parse_snowflake_stage_name(parser: &mut Parser) -> Result<ObjectName, ParserError> {
700    match parser.next_token().token {
701        Token::AtSign => {
702            parser.prev_token();
703            let mut idents = vec![];
704            loop {
705                idents.push(parse_stage_name_identifier(parser)?);
706                if !parser.consume_token(&Token::Period) {
707                    break;
708                }
709            }
710            Ok(ObjectName::from(idents))
711        }
712        _ => {
713            parser.prev_token();
714            Ok(parser.parse_object_name(false)?)
715        }
716    }
717}
718
719/// Parses a `COPY INTO` statement. Snowflake has two variants, `COPY INTO <table>`
720/// and `COPY INTO <location>` which have different syntax.
721pub fn parse_copy_into(parser: &mut Parser) -> Result<Statement, ParserError> {
722    let kind = match parser.peek_token().token {
723        // Indicates an internal stage
724        Token::AtSign => CopyIntoSnowflakeKind::Location,
725        // Indicates an external stage, i.e. s3://, gcs:// or azure://
726        Token::SingleQuotedString(s) if s.contains("://") => CopyIntoSnowflakeKind::Location,
727        _ => CopyIntoSnowflakeKind::Table,
728    };
729
730    let mut files: Vec<String> = vec![];
731    let mut from_transformations: Option<Vec<StageLoadSelectItemKind>> = None;
732    let mut from_stage_alias = None;
733    let mut from_stage = None;
734    let mut stage_params = StageParamsObject {
735        url: None,
736        encryption: KeyValueOptions { options: vec![] },
737        endpoint: None,
738        storage_integration: None,
739        credentials: KeyValueOptions { options: vec![] },
740    };
741    let mut from_query = None;
742    let mut partition = None;
743    let mut file_format = Vec::new();
744    let mut pattern = None;
745    let mut validation_mode = None;
746    let mut copy_options = Vec::new();
747
748    let into: ObjectName = parse_snowflake_stage_name(parser)?;
749    if kind == CopyIntoSnowflakeKind::Location {
750        stage_params = parse_stage_params(parser)?;
751    }
752
753    let into_columns = match &parser.peek_token().token {
754        Token::LParen => Some(parser.parse_parenthesized_column_list(IsOptional::Optional, true)?),
755        _ => None,
756    };
757
758    parser.expect_keyword_is(Keyword::FROM)?;
759    match parser.next_token().token {
760        Token::LParen if kind == CopyIntoSnowflakeKind::Table => {
761            // Data load with transformations
762            parser.expect_keyword_is(Keyword::SELECT)?;
763            from_transformations = parse_select_items_for_data_load(parser)?;
764
765            parser.expect_keyword_is(Keyword::FROM)?;
766            from_stage = Some(parse_snowflake_stage_name(parser)?);
767            stage_params = parse_stage_params(parser)?;
768
769            // Parse an optional alias
770            from_stage_alias = parser
771                .maybe_parse_table_alias()?
772                .map(|table_alias| table_alias.name);
773            parser.expect_token(&Token::RParen)?;
774        }
775        Token::LParen if kind == CopyIntoSnowflakeKind::Location => {
776            // Data unload with a query
777            from_query = Some(parser.parse_query()?);
778            parser.expect_token(&Token::RParen)?;
779        }
780        _ => {
781            parser.prev_token();
782            from_stage = Some(parse_snowflake_stage_name(parser)?);
783            stage_params = parse_stage_params(parser)?;
784
785            // as
786            from_stage_alias = if parser.parse_keyword(Keyword::AS) {
787                Some(match parser.next_token().token {
788                    Token::Word(w) => Ok(Ident::new(w.value)),
789                    _ => parser.expected("stage alias", parser.peek_token()),
790                }?)
791            } else {
792                None
793            };
794        }
795    }
796
797    loop {
798        // FILE_FORMAT
799        if parser.parse_keyword(Keyword::FILE_FORMAT) {
800            parser.expect_token(&Token::Eq)?;
801            file_format = parse_parentheses_options(parser)?;
802        // PARTITION BY
803        } else if parser.parse_keywords(&[Keyword::PARTITION, Keyword::BY]) {
804            partition = Some(Box::new(parser.parse_expr()?))
805        // FILES
806        } else if parser.parse_keyword(Keyword::FILES) {
807            parser.expect_token(&Token::Eq)?;
808            parser.expect_token(&Token::LParen)?;
809            let mut continue_loop = true;
810            while continue_loop {
811                continue_loop = false;
812                let next_token = parser.next_token();
813                match next_token.token {
814                    Token::SingleQuotedString(s) => files.push(s),
815                    _ => parser.expected("file token", next_token)?,
816                };
817                if parser.next_token().token.eq(&Token::Comma) {
818                    continue_loop = true;
819                } else {
820                    parser.prev_token(); // not a comma, need to go back
821                }
822            }
823            parser.expect_token(&Token::RParen)?;
824        // PATTERN
825        } else if parser.parse_keyword(Keyword::PATTERN) {
826            parser.expect_token(&Token::Eq)?;
827            let next_token = parser.next_token();
828            pattern = Some(match next_token.token {
829                Token::SingleQuotedString(s) => s,
830                _ => parser.expected("pattern", next_token)?,
831            });
832        // VALIDATION MODE
833        } else if parser.parse_keyword(Keyword::VALIDATION_MODE) {
834            parser.expect_token(&Token::Eq)?;
835            validation_mode = Some(parser.next_token().token.to_string());
836        // COPY OPTIONS
837        } else if parser.parse_keyword(Keyword::COPY_OPTIONS) {
838            parser.expect_token(&Token::Eq)?;
839            copy_options = parse_parentheses_options(parser)?;
840        } else {
841            match parser.next_token().token {
842                Token::SemiColon | Token::EOF => break,
843                Token::Comma => continue,
844                // In `COPY INTO <location>` the copy options do not have a shared key
845                // like in `COPY INTO <table>`
846                Token::Word(key) => copy_options.push(parse_option(parser, key)?),
847                _ => return parser.expected("another copy option, ; or EOF'", parser.peek_token()),
848            }
849        }
850    }
851
852    Ok(Statement::CopyIntoSnowflake {
853        kind,
854        into,
855        into_columns,
856        from_obj: from_stage,
857        from_obj_alias: from_stage_alias,
858        stage_params,
859        from_transformations,
860        from_query,
861        files: if files.is_empty() { None } else { Some(files) },
862        pattern,
863        file_format: KeyValueOptions {
864            options: file_format,
865        },
866        copy_options: KeyValueOptions {
867            options: copy_options,
868        },
869        validation_mode,
870        partition,
871    })
872}
873
874fn parse_select_items_for_data_load(
875    parser: &mut Parser,
876) -> Result<Option<Vec<StageLoadSelectItemKind>>, ParserError> {
877    let mut select_items: Vec<StageLoadSelectItemKind> = vec![];
878    loop {
879        match parser.maybe_parse(parse_select_item_for_data_load)? {
880            // [<alias>.]$<file_col_num>[.<element>] [ , [<alias>.]$<file_col_num>[.<element>] ... ]
881            Some(item) => select_items.push(StageLoadSelectItemKind::StageLoadSelectItem(item)),
882            // Fallback, try to parse a standard SQL select item
883            None => select_items.push(StageLoadSelectItemKind::SelectItem(
884                parser.parse_select_item()?,
885            )),
886        }
887        if matches!(parser.peek_token_ref().token, Token::Comma) {
888            parser.advance_token();
889        } else {
890            break;
891        }
892    }
893    Ok(Some(select_items))
894}
895
896fn parse_select_item_for_data_load(
897    parser: &mut Parser,
898) -> Result<StageLoadSelectItem, ParserError> {
899    let mut alias: Option<Ident> = None;
900    let mut file_col_num: i32 = 0;
901    let mut element: Option<Ident> = None;
902    let mut item_as: Option<Ident> = None;
903
904    let next_token = parser.next_token();
905    match next_token.token {
906        Token::Placeholder(w) => {
907            file_col_num = w.to_string().split_off(1).parse::<i32>().map_err(|e| {
908                ParserError::ParserError(format!("Could not parse '{w}' as i32: {e}"))
909            })?;
910            Ok(())
911        }
912        Token::Word(w) => {
913            alias = Some(Ident::new(w.value));
914            Ok(())
915        }
916        _ => parser.expected("alias or file_col_num", next_token),
917    }?;
918
919    if alias.is_some() {
920        parser.expect_token(&Token::Period)?;
921        // now we get col_num token
922        let col_num_token = parser.next_token();
923        match col_num_token.token {
924            Token::Placeholder(w) => {
925                file_col_num = w.to_string().split_off(1).parse::<i32>().map_err(|e| {
926                    ParserError::ParserError(format!("Could not parse '{w}' as i32: {e}"))
927                })?;
928                Ok(())
929            }
930            _ => parser.expected("file_col_num", col_num_token),
931        }?;
932    }
933
934    // try extracting optional element
935    match parser.next_token().token {
936        Token::Colon => {
937            // parse element
938            element = Some(Ident::new(match parser.next_token().token {
939                Token::Word(w) => Ok(w.value),
940                _ => parser.expected("file_col_num", parser.peek_token()),
941            }?));
942        }
943        _ => {
944            // element not present move back
945            parser.prev_token();
946        }
947    }
948
949    // as
950    if parser.parse_keyword(Keyword::AS) {
951        item_as = Some(match parser.next_token().token {
952            Token::Word(w) => Ok(Ident::new(w.value)),
953            _ => parser.expected("column item alias", parser.peek_token()),
954        }?);
955    }
956
957    Ok(StageLoadSelectItem {
958        alias,
959        file_col_num,
960        element,
961        item_as,
962    })
963}
964
965fn parse_stage_params(parser: &mut Parser) -> Result<StageParamsObject, ParserError> {
966    let (mut url, mut storage_integration, mut endpoint) = (None, None, None);
967    let mut encryption: KeyValueOptions = KeyValueOptions { options: vec![] };
968    let mut credentials: KeyValueOptions = KeyValueOptions { options: vec![] };
969
970    // URL
971    if parser.parse_keyword(Keyword::URL) {
972        parser.expect_token(&Token::Eq)?;
973        url = Some(match parser.next_token().token {
974            Token::SingleQuotedString(word) => Ok(word),
975            _ => parser.expected("a URL statement", parser.peek_token()),
976        }?)
977    }
978
979    // STORAGE INTEGRATION
980    if parser.parse_keyword(Keyword::STORAGE_INTEGRATION) {
981        parser.expect_token(&Token::Eq)?;
982        storage_integration = Some(parser.next_token().token.to_string());
983    }
984
985    // ENDPOINT
986    if parser.parse_keyword(Keyword::ENDPOINT) {
987        parser.expect_token(&Token::Eq)?;
988        endpoint = Some(match parser.next_token().token {
989            Token::SingleQuotedString(word) => Ok(word),
990            _ => parser.expected("an endpoint statement", parser.peek_token()),
991        }?)
992    }
993
994    // CREDENTIALS
995    if parser.parse_keyword(Keyword::CREDENTIALS) {
996        parser.expect_token(&Token::Eq)?;
997        credentials = KeyValueOptions {
998            options: parse_parentheses_options(parser)?,
999        };
1000    }
1001
1002    // ENCRYPTION
1003    if parser.parse_keyword(Keyword::ENCRYPTION) {
1004        parser.expect_token(&Token::Eq)?;
1005        encryption = KeyValueOptions {
1006            options: parse_parentheses_options(parser)?,
1007        };
1008    }
1009
1010    Ok(StageParamsObject {
1011        url,
1012        encryption,
1013        endpoint,
1014        storage_integration,
1015        credentials,
1016    })
1017}
1018
1019/// Parses options separated by blank spaces, commas, or new lines like:
1020/// ABORT_DETACHED_QUERY = { TRUE | FALSE }
1021///      [ ACTIVE_PYTHON_PROFILER = { 'LINE' | 'MEMORY' } ]
1022///      [ BINARY_INPUT_FORMAT = '\<string\>' ]
1023fn parse_session_options(
1024    parser: &mut Parser,
1025    set: bool,
1026) -> Result<Vec<KeyValueOption>, ParserError> {
1027    let mut options: Vec<KeyValueOption> = Vec::new();
1028    let empty = String::new;
1029    loop {
1030        let next_token = parser.peek_token();
1031        match next_token.token {
1032            Token::SemiColon | Token::EOF => break,
1033            Token::Comma => {
1034                parser.advance_token();
1035                continue;
1036            }
1037            Token::Word(key) => {
1038                parser.advance_token();
1039                if set {
1040                    let option = parse_option(parser, key)?;
1041                    options.push(option);
1042                } else {
1043                    options.push(KeyValueOption {
1044                        option_name: key.value,
1045                        option_type: KeyValueOptionType::STRING,
1046                        value: empty(),
1047                    });
1048                }
1049            }
1050            _ => {
1051                return parser.expected("another option or end of statement", next_token);
1052            }
1053        }
1054    }
1055    if options.is_empty() {
1056        Err(ParserError::ParserError(
1057            "expected at least one option".to_string(),
1058        ))
1059    } else {
1060        Ok(options)
1061    }
1062}
1063
1064/// Parses options provided within parentheses like:
1065/// ( ENABLE = { TRUE | FALSE }
1066///      [ AUTO_REFRESH = { TRUE | FALSE } ]
1067///      [ REFRESH_ON_CREATE =  { TRUE | FALSE } ]
1068///      [ NOTIFICATION_INTEGRATION = '<notification_integration_name>' ] )
1069///
1070fn parse_parentheses_options(parser: &mut Parser) -> Result<Vec<KeyValueOption>, ParserError> {
1071    let mut options: Vec<KeyValueOption> = Vec::new();
1072    parser.expect_token(&Token::LParen)?;
1073    loop {
1074        match parser.next_token().token {
1075            Token::RParen => break,
1076            Token::Comma => continue,
1077            Token::Word(key) => options.push(parse_option(parser, key)?),
1078            _ => return parser.expected("another option or ')'", parser.peek_token()),
1079        };
1080    }
1081    Ok(options)
1082}
1083
1084/// Parses a `KEY = VALUE` construct based on the specified key
1085fn parse_option(parser: &mut Parser, key: Word) -> Result<KeyValueOption, ParserError> {
1086    parser.expect_token(&Token::Eq)?;
1087    if parser.parse_keyword(Keyword::TRUE) {
1088        Ok(KeyValueOption {
1089            option_name: key.value,
1090            option_type: KeyValueOptionType::BOOLEAN,
1091            value: "TRUE".to_string(),
1092        })
1093    } else if parser.parse_keyword(Keyword::FALSE) {
1094        Ok(KeyValueOption {
1095            option_name: key.value,
1096            option_type: KeyValueOptionType::BOOLEAN,
1097            value: "FALSE".to_string(),
1098        })
1099    } else {
1100        match parser.next_token().token {
1101            Token::SingleQuotedString(value) => Ok(KeyValueOption {
1102                option_name: key.value,
1103                option_type: KeyValueOptionType::STRING,
1104                value,
1105            }),
1106            Token::Word(word) => Ok(KeyValueOption {
1107                option_name: key.value,
1108                option_type: KeyValueOptionType::ENUM,
1109                value: word.value,
1110            }),
1111            Token::Number(n, _) => Ok(KeyValueOption {
1112                option_name: key.value,
1113                option_type: KeyValueOptionType::NUMBER,
1114                value: n,
1115            }),
1116            _ => parser.expected("expected option value", parser.peek_token()),
1117        }
1118    }
1119}
1120
1121/// Parsing a property of identity or autoincrement column option
1122/// Syntax:
1123/// ```sql
1124/// [ (seed , increment) | START num INCREMENT num ] [ ORDER | NOORDER ]
1125/// ```
1126/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1127fn parse_identity_property(parser: &mut Parser) -> Result<IdentityProperty, ParserError> {
1128    let parameters = if parser.consume_token(&Token::LParen) {
1129        let seed = parser.parse_number()?;
1130        parser.expect_token(&Token::Comma)?;
1131        let increment = parser.parse_number()?;
1132        parser.expect_token(&Token::RParen)?;
1133
1134        Some(IdentityPropertyFormatKind::FunctionCall(
1135            IdentityParameters { seed, increment },
1136        ))
1137    } else if parser.parse_keyword(Keyword::START) {
1138        let seed = parser.parse_number()?;
1139        parser.expect_keyword_is(Keyword::INCREMENT)?;
1140        let increment = parser.parse_number()?;
1141
1142        Some(IdentityPropertyFormatKind::StartAndIncrement(
1143            IdentityParameters { seed, increment },
1144        ))
1145    } else {
1146        None
1147    };
1148    let order = match parser.parse_one_of_keywords(&[Keyword::ORDER, Keyword::NOORDER]) {
1149        Some(Keyword::ORDER) => Some(IdentityPropertyOrder::Order),
1150        Some(Keyword::NOORDER) => Some(IdentityPropertyOrder::NoOrder),
1151        _ => None,
1152    };
1153    Ok(IdentityProperty { parameters, order })
1154}
1155
1156/// Parsing a policy property of column option
1157/// Syntax:
1158/// ```sql
1159/// <policy_name> [ USING ( <col_name> , <cond_col1> , ... )
1160/// ```
1161/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1162fn parse_column_policy_property(
1163    parser: &mut Parser,
1164    with: bool,
1165) -> Result<ColumnPolicyProperty, ParserError> {
1166    let policy_name = parser.parse_identifier()?;
1167    let using_columns = if parser.parse_keyword(Keyword::USING) {
1168        parser.expect_token(&Token::LParen)?;
1169        let columns = parser.parse_comma_separated(|p| p.parse_identifier())?;
1170        parser.expect_token(&Token::RParen)?;
1171        Some(columns)
1172    } else {
1173        None
1174    };
1175
1176    Ok(ColumnPolicyProperty {
1177        with,
1178        policy_name,
1179        using_columns,
1180    })
1181}
1182
1183/// Parsing tags list of column
1184/// Syntax:
1185/// ```sql
1186/// ( <tag_name> = '<tag_value>' [ , <tag_name> = '<tag_value>' , ... ] )
1187/// ```
1188/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1189fn parse_column_tags(parser: &mut Parser, with: bool) -> Result<TagsColumnOption, ParserError> {
1190    parser.expect_token(&Token::LParen)?;
1191    let tags = parser.parse_comma_separated(Parser::parse_tag)?;
1192    parser.expect_token(&Token::RParen)?;
1193
1194    Ok(TagsColumnOption { with, tags })
1195}
1196
1197/// Parse snowflake show objects.
1198/// <https://docs.snowflake.com/en/sql-reference/sql/show-objects>
1199fn parse_show_objects(terse: bool, parser: &mut Parser) -> Result<Statement, ParserError> {
1200    let show_options = parser.parse_show_stmt_options()?;
1201    Ok(Statement::ShowObjects(ShowObjects {
1202        terse,
1203        show_options,
1204    }))
1205}