sqltk_parser/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::stmt_create_table::CreateTableBuilder;
21use crate::ast::helpers::stmt_data_loading::{
22    DataLoadingOption, DataLoadingOptionType, DataLoadingOptions, StageLoadSelectItem,
23    StageParamsObject,
24};
25use crate::ast::{
26    ColumnOption, ColumnPolicy, ColumnPolicyProperty, Ident, IdentityParameters, IdentityProperty,
27    IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, ObjectName,
28    RowAccessPolicy, Statement, TagsColumnOption, WrappedCollection,
29};
30use crate::dialect::{Dialect, Precedence};
31use crate::keywords::Keyword;
32use crate::parser::{Parser, ParserError};
33use crate::tokenizer::Token;
34#[cfg(not(feature = "std"))]
35use alloc::string::String;
36#[cfg(not(feature = "std"))]
37use alloc::vec::Vec;
38#[cfg(not(feature = "std"))]
39use alloc::{format, vec};
40
41use super::keywords::RESERVED_FOR_IDENTIFIER;
42
43/// A [`Dialect`] for [Snowflake](https://www.snowflake.com/)
44#[derive(Debug, Default)]
45pub struct SnowflakeDialect;
46
47impl Dialect for SnowflakeDialect {
48    // see https://docs.snowflake.com/en/sql-reference/identifiers-syntax.html
49    fn is_identifier_start(&self, ch: char) -> bool {
50        ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch == '_'
51    }
52
53    fn supports_projection_trailing_commas(&self) -> bool {
54        true
55    }
56
57    // Snowflake supports double-dot notation when the schema name is not specified
58    // In this case the default PUBLIC schema is used
59    //
60    // see https://docs.snowflake.com/en/sql-reference/name-resolution#resolution-when-schema-omitted-double-dot-notation
61    fn supports_object_name_double_dot_notation(&self) -> bool {
62        true
63    }
64
65    fn is_identifier_part(&self, ch: char) -> bool {
66        ch.is_ascii_lowercase()
67            || ch.is_ascii_uppercase()
68            || ch.is_ascii_digit()
69            || ch == '$'
70            || ch == '_'
71    }
72
73    // See https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#escape_sequences
74    fn supports_string_literal_backslash_escape(&self) -> bool {
75        true
76    }
77
78    fn supports_within_after_array_aggregation(&self) -> bool {
79        true
80    }
81
82    fn supports_connect_by(&self) -> bool {
83        true
84    }
85
86    fn supports_match_recognize(&self) -> bool {
87        true
88    }
89
90    // Snowflake uses this syntax for "object constants" (the values of which
91    // are not actually required to be constants).
92    //
93    // https://docs.snowflake.com/en/sql-reference/data-types-semistructured#label-object-constant
94    fn supports_dictionary_syntax(&self) -> bool {
95        true
96    }
97
98    // Snowflake doesn't document this but `FIRST_VALUE(arg, { IGNORE | RESPECT } NULLS)`
99    // works (i.e. inside the argument list instead of after).
100    fn supports_window_function_null_treatment_arg(&self) -> bool {
101        true
102    }
103
104    /// See [doc](https://docs.snowflake.com/en/sql-reference/sql/set#syntax)
105    fn supports_parenthesized_set_variables(&self) -> bool {
106        true
107    }
108
109    /// See [doc](https://docs.snowflake.com/en/sql-reference/sql/comment)
110    fn supports_comment_on(&self) -> bool {
111        true
112    }
113
114    fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
115        if parser.parse_keyword(Keyword::CREATE) {
116            // possibly CREATE STAGE
117            //[ OR  REPLACE ]
118            let or_replace = parser.parse_keywords(&[Keyword::OR, Keyword::REPLACE]);
119            // LOCAL | GLOBAL
120            let global = match parser.parse_one_of_keywords(&[Keyword::LOCAL, Keyword::GLOBAL]) {
121                Some(Keyword::LOCAL) => Some(false),
122                Some(Keyword::GLOBAL) => Some(true),
123                _ => None,
124            };
125
126            let mut temporary = false;
127            let mut volatile = false;
128            let mut transient = false;
129
130            match parser.parse_one_of_keywords(&[
131                Keyword::TEMP,
132                Keyword::TEMPORARY,
133                Keyword::VOLATILE,
134                Keyword::TRANSIENT,
135            ]) {
136                Some(Keyword::TEMP | Keyword::TEMPORARY) => temporary = true,
137                Some(Keyword::VOLATILE) => volatile = true,
138                Some(Keyword::TRANSIENT) => transient = true,
139                _ => {}
140            }
141
142            if parser.parse_keyword(Keyword::STAGE) {
143                // OK - this is CREATE STAGE statement
144                return Some(parse_create_stage(or_replace, temporary, parser));
145            } else if parser.parse_keyword(Keyword::TABLE) {
146                return Some(parse_create_table(
147                    or_replace, global, temporary, volatile, transient, parser,
148                ));
149            } else {
150                // need to go back with the cursor
151                let mut back = 1;
152                if or_replace {
153                    back += 2
154                }
155                if temporary {
156                    back += 1
157                }
158                for _i in 0..back {
159                    parser.prev_token();
160                }
161            }
162        }
163        if parser.parse_keywords(&[Keyword::COPY, Keyword::INTO]) {
164            // COPY INTO
165            return Some(parse_copy_into(parser));
166        }
167
168        None
169    }
170
171    fn parse_column_option(
172        &self,
173        parser: &mut Parser,
174    ) -> Result<Option<Result<Option<ColumnOption>, ParserError>>, ParserError> {
175        parser.maybe_parse(|parser| {
176            let with = parser.parse_keyword(Keyword::WITH);
177
178            if parser.parse_keyword(Keyword::IDENTITY) {
179                Ok(parse_identity_property(parser)
180                    .map(|p| Some(ColumnOption::Identity(IdentityPropertyKind::Identity(p)))))
181            } else if parser.parse_keyword(Keyword::AUTOINCREMENT) {
182                Ok(parse_identity_property(parser).map(|p| {
183                    Some(ColumnOption::Identity(IdentityPropertyKind::Autoincrement(
184                        p,
185                    )))
186                }))
187            } else if parser.parse_keywords(&[Keyword::MASKING, Keyword::POLICY]) {
188                Ok(parse_column_policy_property(parser, with)
189                    .map(|p| Some(ColumnOption::Policy(ColumnPolicy::MaskingPolicy(p)))))
190            } else if parser.parse_keywords(&[Keyword::PROJECTION, Keyword::POLICY]) {
191                Ok(parse_column_policy_property(parser, with)
192                    .map(|p| Some(ColumnOption::Policy(ColumnPolicy::ProjectionPolicy(p)))))
193            } else if parser.parse_keywords(&[Keyword::TAG]) {
194                Ok(parse_column_tags(parser, with).map(|p| Some(ColumnOption::Tags(p))))
195            } else {
196                Err(ParserError::ParserError("not found match".to_string()))
197            }
198        })
199    }
200
201    fn get_next_precedence(&self, parser: &Parser) -> Option<Result<u8, ParserError>> {
202        let token = parser.peek_token();
203        // Snowflake supports the `:` cast operator unlike other dialects
204        match token.token {
205            Token::Colon => Some(Ok(self.prec_value(Precedence::DoubleColon))),
206            _ => None,
207        }
208    }
209
210    fn describe_requires_table_keyword(&self) -> bool {
211        true
212    }
213
214    fn allow_extract_custom(&self) -> bool {
215        true
216    }
217
218    fn allow_extract_single_quotes(&self) -> bool {
219        true
220    }
221
222    /// Snowflake expects the `LIKE` option before the `IN` option,
223    /// for example: <https://docs.snowflake.com/en/sql-reference/sql/show-views#syntax>
224    fn supports_show_like_before_in(&self) -> bool {
225        true
226    }
227
228    fn is_reserved_for_identifier(&self, kw: Keyword) -> bool {
229        // Unreserve some keywords that Snowflake accepts as identifiers
230        // See: https://docs.snowflake.com/en/sql-reference/reserved-keywords
231        if matches!(kw, Keyword::INTERVAL) {
232            false
233        } else {
234            RESERVED_FOR_IDENTIFIER.contains(&kw)
235        }
236    }
237}
238
239/// Parse snowflake create table statement.
240/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
241pub fn parse_create_table(
242    or_replace: bool,
243    global: Option<bool>,
244    temporary: bool,
245    volatile: bool,
246    transient: bool,
247    parser: &mut Parser,
248) -> Result<Statement, ParserError> {
249    let if_not_exists = parser.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
250    let table_name = parser.parse_object_name(false)?;
251
252    let mut builder = CreateTableBuilder::new(table_name)
253        .or_replace(or_replace)
254        .if_not_exists(if_not_exists)
255        .temporary(temporary)
256        .transient(transient)
257        .volatile(volatile)
258        .global(global)
259        .hive_formats(Some(Default::default()));
260
261    // Snowflake does not enforce order of the parameters in the statement. The parser needs to
262    // parse the statement in a loop.
263    //
264    // "CREATE TABLE x COPY GRANTS (c INT)" and "CREATE TABLE x (c INT) COPY GRANTS" are both
265    // accepted by Snowflake
266
267    loop {
268        let next_token = parser.next_token();
269        match &next_token.token {
270            Token::Word(word) => match word.keyword {
271                Keyword::COPY => {
272                    parser.expect_keyword(Keyword::GRANTS)?;
273                    builder = builder.copy_grants(true);
274                }
275                Keyword::COMMENT => {
276                    // Rewind the COMMENT keyword
277                    parser.prev_token();
278                    builder = builder.comment(parser.parse_optional_inline_comment()?);
279                }
280                Keyword::AS => {
281                    let query = parser.parse_query()?;
282                    builder = builder.query(Some(query));
283                    break;
284                }
285                Keyword::CLONE => {
286                    let clone = parser.parse_object_name(false).ok();
287                    builder = builder.clone_clause(clone);
288                    break;
289                }
290                Keyword::LIKE => {
291                    let like = parser.parse_object_name(false).ok();
292                    builder = builder.like(like);
293                    break;
294                }
295                Keyword::CLUSTER => {
296                    parser.expect_keyword(Keyword::BY)?;
297                    parser.expect_token(&Token::LParen)?;
298                    let cluster_by = Some(WrappedCollection::Parentheses(
299                        parser.parse_comma_separated(|p| p.parse_identifier(false))?,
300                    ));
301                    parser.expect_token(&Token::RParen)?;
302
303                    builder = builder.cluster_by(cluster_by)
304                }
305                Keyword::ENABLE_SCHEMA_EVOLUTION => {
306                    parser.expect_token(&Token::Eq)?;
307                    let enable_schema_evolution =
308                        match parser.parse_one_of_keywords(&[Keyword::TRUE, Keyword::FALSE]) {
309                            Some(Keyword::TRUE) => true,
310                            Some(Keyword::FALSE) => false,
311                            _ => {
312                                return parser.expected("TRUE or FALSE", next_token);
313                            }
314                        };
315
316                    builder = builder.enable_schema_evolution(Some(enable_schema_evolution));
317                }
318                Keyword::CHANGE_TRACKING => {
319                    parser.expect_token(&Token::Eq)?;
320                    let change_tracking =
321                        match parser.parse_one_of_keywords(&[Keyword::TRUE, Keyword::FALSE]) {
322                            Some(Keyword::TRUE) => true,
323                            Some(Keyword::FALSE) => false,
324                            _ => {
325                                return parser.expected("TRUE or FALSE", next_token);
326                            }
327                        };
328
329                    builder = builder.change_tracking(Some(change_tracking));
330                }
331                Keyword::DATA_RETENTION_TIME_IN_DAYS => {
332                    parser.expect_token(&Token::Eq)?;
333                    let data_retention_time_in_days = parser.parse_literal_uint()?;
334                    builder =
335                        builder.data_retention_time_in_days(Some(data_retention_time_in_days));
336                }
337                Keyword::MAX_DATA_EXTENSION_TIME_IN_DAYS => {
338                    parser.expect_token(&Token::Eq)?;
339                    let max_data_extension_time_in_days = parser.parse_literal_uint()?;
340                    builder = builder
341                        .max_data_extension_time_in_days(Some(max_data_extension_time_in_days));
342                }
343                Keyword::DEFAULT_DDL_COLLATION => {
344                    parser.expect_token(&Token::Eq)?;
345                    let default_ddl_collation = parser.parse_literal_string()?;
346                    builder = builder.default_ddl_collation(Some(default_ddl_collation));
347                }
348                // WITH is optional, we just verify that next token is one of the expected ones and
349                // fallback to the default match statement
350                Keyword::WITH => {
351                    parser.expect_one_of_keywords(&[
352                        Keyword::AGGREGATION,
353                        Keyword::TAG,
354                        Keyword::ROW,
355                    ])?;
356                    parser.prev_token();
357                }
358                Keyword::AGGREGATION => {
359                    parser.expect_keyword(Keyword::POLICY)?;
360                    let aggregation_policy = parser.parse_object_name(false)?;
361                    builder = builder.with_aggregation_policy(Some(aggregation_policy));
362                }
363                Keyword::ROW => {
364                    parser.expect_keywords(&[Keyword::ACCESS, Keyword::POLICY])?;
365                    let policy = parser.parse_object_name(false)?;
366                    parser.expect_keyword(Keyword::ON)?;
367                    parser.expect_token(&Token::LParen)?;
368                    let columns = parser.parse_comma_separated(|p| p.parse_identifier(false))?;
369                    parser.expect_token(&Token::RParen)?;
370
371                    builder =
372                        builder.with_row_access_policy(Some(RowAccessPolicy::new(policy, columns)))
373                }
374                Keyword::TAG => {
375                    parser.expect_token(&Token::LParen)?;
376                    let tags = parser.parse_comma_separated(Parser::parse_tag)?;
377                    parser.expect_token(&Token::RParen)?;
378                    builder = builder.with_tags(Some(tags));
379                }
380                _ => {
381                    return parser.expected("end of statement", next_token);
382                }
383            },
384            Token::LParen => {
385                parser.prev_token();
386                let (columns, constraints) = parser.parse_columns()?;
387                builder = builder.columns(columns).constraints(constraints);
388            }
389            Token::EOF => {
390                if builder.columns.is_empty() {
391                    return Err(ParserError::ParserError(
392                        "unexpected end of input".to_string(),
393                    ));
394                }
395
396                break;
397            }
398            Token::SemiColon => {
399                if builder.columns.is_empty() {
400                    return Err(ParserError::ParserError(
401                        "unexpected end of input".to_string(),
402                    ));
403                }
404
405                parser.prev_token();
406                break;
407            }
408            _ => {
409                return parser.expected("end of statement", next_token);
410            }
411        }
412    }
413
414    Ok(builder.build())
415}
416
417pub fn parse_create_stage(
418    or_replace: bool,
419    temporary: bool,
420    parser: &mut Parser,
421) -> Result<Statement, ParserError> {
422    //[ IF NOT EXISTS ]
423    let if_not_exists = parser.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
424    let name = parser.parse_object_name(false)?;
425    let mut directory_table_params = Vec::new();
426    let mut file_format = Vec::new();
427    let mut copy_options = Vec::new();
428    let mut comment = None;
429
430    // [ internalStageParams | externalStageParams ]
431    let stage_params = parse_stage_params(parser)?;
432
433    // [ directoryTableParams ]
434    if parser.parse_keyword(Keyword::DIRECTORY) {
435        parser.expect_token(&Token::Eq)?;
436        directory_table_params = parse_parentheses_options(parser)?;
437    }
438
439    // [ file_format]
440    if parser.parse_keyword(Keyword::FILE_FORMAT) {
441        parser.expect_token(&Token::Eq)?;
442        file_format = parse_parentheses_options(parser)?;
443    }
444
445    // [ copy_options ]
446    if parser.parse_keyword(Keyword::COPY_OPTIONS) {
447        parser.expect_token(&Token::Eq)?;
448        copy_options = parse_parentheses_options(parser)?;
449    }
450
451    // [ comment ]
452    if parser.parse_keyword(Keyword::COMMENT) {
453        parser.expect_token(&Token::Eq)?;
454        comment = Some(match parser.next_token().token {
455            Token::SingleQuotedString(word) => Ok(word),
456            _ => parser.expected("a comment statement", parser.peek_token()),
457        }?)
458    }
459
460    Ok(Statement::CreateStage {
461        or_replace,
462        temporary,
463        if_not_exists,
464        name,
465        stage_params,
466        directory_table_params: DataLoadingOptions {
467            options: directory_table_params,
468        },
469        file_format: DataLoadingOptions {
470            options: file_format,
471        },
472        copy_options: DataLoadingOptions {
473            options: copy_options,
474        },
475        comment,
476    })
477}
478
479pub fn parse_stage_name_identifier(parser: &mut Parser) -> Result<Ident, ParserError> {
480    let mut ident = String::new();
481    while let Some(next_token) = parser.next_token_no_skip() {
482        match &next_token.token {
483            Token::Whitespace(_) => break,
484            Token::Period => {
485                parser.prev_token();
486                break;
487            }
488            Token::RParen => {
489                parser.prev_token();
490                break;
491            }
492            Token::AtSign => ident.push('@'),
493            Token::Tilde => ident.push('~'),
494            Token::Mod => ident.push('%'),
495            Token::Div => ident.push('/'),
496            Token::Word(w) => ident.push_str(&w.value),
497            _ => return parser.expected("stage name identifier", parser.peek_token()),
498        }
499    }
500    Ok(Ident::new(ident))
501}
502
503pub fn parse_snowflake_stage_name(parser: &mut Parser) -> Result<ObjectName, ParserError> {
504    match parser.next_token().token {
505        Token::AtSign => {
506            parser.prev_token();
507            let mut idents = vec![];
508            loop {
509                idents.push(parse_stage_name_identifier(parser)?);
510                if !parser.consume_token(&Token::Period) {
511                    break;
512                }
513            }
514            Ok(ObjectName(idents))
515        }
516        _ => {
517            parser.prev_token();
518            Ok(parser.parse_object_name(false)?)
519        }
520    }
521}
522
523pub fn parse_copy_into(parser: &mut Parser) -> Result<Statement, ParserError> {
524    let into: ObjectName = parse_snowflake_stage_name(parser)?;
525    let mut files: Vec<String> = vec![];
526    let mut from_transformations: Option<Vec<StageLoadSelectItem>> = None;
527    let from_stage_alias;
528    let from_stage: ObjectName;
529    let stage_params: StageParamsObject;
530
531    parser.expect_keyword(Keyword::FROM)?;
532    // check if data load transformations are present
533    match parser.next_token().token {
534        Token::LParen => {
535            // data load with transformations
536            parser.expect_keyword(Keyword::SELECT)?;
537            from_transformations = parse_select_items_for_data_load(parser)?;
538
539            parser.expect_keyword(Keyword::FROM)?;
540            from_stage = parse_snowflake_stage_name(parser)?;
541            stage_params = parse_stage_params(parser)?;
542
543            // as
544            from_stage_alias = if parser.parse_keyword(Keyword::AS) {
545                Some(match parser.next_token().token {
546                    Token::Word(w) => Ok(Ident::new(w.value)),
547                    _ => parser.expected("stage alias", parser.peek_token()),
548                }?)
549            } else {
550                None
551            };
552            parser.expect_token(&Token::RParen)?;
553        }
554        _ => {
555            parser.prev_token();
556            from_stage = parse_snowflake_stage_name(parser)?;
557            stage_params = parse_stage_params(parser)?;
558
559            // as
560            from_stage_alias = if parser.parse_keyword(Keyword::AS) {
561                Some(match parser.next_token().token {
562                    Token::Word(w) => Ok(Ident::new(w.value)),
563                    _ => parser.expected("stage alias", parser.peek_token()),
564                }?)
565            } else {
566                None
567            };
568        }
569    };
570
571    // [ files ]
572    if parser.parse_keyword(Keyword::FILES) {
573        parser.expect_token(&Token::Eq)?;
574        parser.expect_token(&Token::LParen)?;
575        let mut continue_loop = true;
576        while continue_loop {
577            continue_loop = false;
578            let next_token = parser.next_token();
579            match next_token.token {
580                Token::SingleQuotedString(s) => files.push(s),
581                _ => parser.expected("file token", next_token)?,
582            };
583            if parser.next_token().token.eq(&Token::Comma) {
584                continue_loop = true;
585            } else {
586                parser.prev_token(); // not a comma, need to go back
587            }
588        }
589        parser.expect_token(&Token::RParen)?;
590    }
591
592    // [ pattern ]
593    let mut pattern = None;
594    if parser.parse_keyword(Keyword::PATTERN) {
595        parser.expect_token(&Token::Eq)?;
596        let next_token = parser.next_token();
597        pattern = Some(match next_token.token {
598            Token::SingleQuotedString(s) => s,
599            _ => parser.expected("pattern", next_token)?,
600        });
601    }
602
603    // [ file_format]
604    let mut file_format = Vec::new();
605    if parser.parse_keyword(Keyword::FILE_FORMAT) {
606        parser.expect_token(&Token::Eq)?;
607        file_format = parse_parentheses_options(parser)?;
608    }
609
610    // [ copy_options ]
611    let mut copy_options = Vec::new();
612    if parser.parse_keyword(Keyword::COPY_OPTIONS) {
613        parser.expect_token(&Token::Eq)?;
614        copy_options = parse_parentheses_options(parser)?;
615    }
616
617    // [ VALIDATION_MODE ]
618    let mut validation_mode = None;
619    if parser.parse_keyword(Keyword::VALIDATION_MODE) {
620        parser.expect_token(&Token::Eq)?;
621        validation_mode = Some(parser.next_token().token.to_string());
622    }
623
624    Ok(Statement::CopyIntoSnowflake {
625        into,
626        from_stage,
627        from_stage_alias,
628        stage_params,
629        from_transformations,
630        files: if files.is_empty() { None } else { Some(files) },
631        pattern,
632        file_format: DataLoadingOptions {
633            options: file_format,
634        },
635        copy_options: DataLoadingOptions {
636            options: copy_options,
637        },
638        validation_mode,
639    })
640}
641
642fn parse_select_items_for_data_load(
643    parser: &mut Parser,
644) -> Result<Option<Vec<StageLoadSelectItem>>, ParserError> {
645    // [<alias>.]$<file_col_num>[.<element>] [ , [<alias>.]$<file_col_num>[.<element>] ... ]
646    let mut select_items: Vec<StageLoadSelectItem> = vec![];
647    loop {
648        let mut alias: Option<Ident> = None;
649        let mut file_col_num: i32 = 0;
650        let mut element: Option<Ident> = None;
651        let mut item_as: Option<Ident> = None;
652
653        let next_token = parser.next_token();
654        match next_token.token {
655            Token::Placeholder(w) => {
656                file_col_num = w.to_string().split_off(1).parse::<i32>().map_err(|e| {
657                    ParserError::ParserError(format!("Could not parse '{w}' as i32: {e}"))
658                })?;
659                Ok(())
660            }
661            Token::Word(w) => {
662                alias = Some(Ident::new(w.value));
663                Ok(())
664            }
665            _ => parser.expected("alias or file_col_num", next_token),
666        }?;
667
668        if alias.is_some() {
669            parser.expect_token(&Token::Period)?;
670            // now we get col_num token
671            let col_num_token = parser.next_token();
672            match col_num_token.token {
673                Token::Placeholder(w) => {
674                    file_col_num = w.to_string().split_off(1).parse::<i32>().map_err(|e| {
675                        ParserError::ParserError(format!("Could not parse '{w}' as i32: {e}"))
676                    })?;
677                    Ok(())
678                }
679                _ => parser.expected("file_col_num", col_num_token),
680            }?;
681        }
682
683        // try extracting optional element
684        match parser.next_token().token {
685            Token::Colon => {
686                // parse element
687                element = Some(Ident::new(match parser.next_token().token {
688                    Token::Word(w) => Ok(w.value),
689                    _ => parser.expected("file_col_num", parser.peek_token()),
690                }?));
691            }
692            _ => {
693                // element not present move back
694                parser.prev_token();
695            }
696        }
697
698        // as
699        if parser.parse_keyword(Keyword::AS) {
700            item_as = Some(match parser.next_token().token {
701                Token::Word(w) => Ok(Ident::new(w.value)),
702                _ => parser.expected("column item alias", parser.peek_token()),
703            }?);
704        }
705
706        select_items.push(StageLoadSelectItem {
707            alias,
708            file_col_num,
709            element,
710            item_as,
711        });
712
713        match parser.next_token().token {
714            Token::Comma => {
715                // continue
716            }
717            _ => {
718                parser.prev_token(); // need to move back
719                break;
720            }
721        }
722    }
723    Ok(Some(select_items))
724}
725
726fn parse_stage_params(parser: &mut Parser) -> Result<StageParamsObject, ParserError> {
727    let (mut url, mut storage_integration, mut endpoint) = (None, None, None);
728    let mut encryption: DataLoadingOptions = DataLoadingOptions { options: vec![] };
729    let mut credentials: DataLoadingOptions = DataLoadingOptions { options: vec![] };
730
731    // URL
732    if parser.parse_keyword(Keyword::URL) {
733        parser.expect_token(&Token::Eq)?;
734        url = Some(match parser.next_token().token {
735            Token::SingleQuotedString(word) => Ok(word),
736            _ => parser.expected("a URL statement", parser.peek_token()),
737        }?)
738    }
739
740    // STORAGE INTEGRATION
741    if parser.parse_keyword(Keyword::STORAGE_INTEGRATION) {
742        parser.expect_token(&Token::Eq)?;
743        storage_integration = Some(parser.next_token().token.to_string());
744    }
745
746    // ENDPOINT
747    if parser.parse_keyword(Keyword::ENDPOINT) {
748        parser.expect_token(&Token::Eq)?;
749        endpoint = Some(match parser.next_token().token {
750            Token::SingleQuotedString(word) => Ok(word),
751            _ => parser.expected("an endpoint statement", parser.peek_token()),
752        }?)
753    }
754
755    // CREDENTIALS
756    if parser.parse_keyword(Keyword::CREDENTIALS) {
757        parser.expect_token(&Token::Eq)?;
758        credentials = DataLoadingOptions {
759            options: parse_parentheses_options(parser)?,
760        };
761    }
762
763    // ENCRYPTION
764    if parser.parse_keyword(Keyword::ENCRYPTION) {
765        parser.expect_token(&Token::Eq)?;
766        encryption = DataLoadingOptions {
767            options: parse_parentheses_options(parser)?,
768        };
769    }
770
771    Ok(StageParamsObject {
772        url,
773        encryption,
774        endpoint,
775        storage_integration,
776        credentials,
777    })
778}
779
780/// Parses options provided within parentheses like:
781/// ( ENABLE = { TRUE | FALSE }
782///      [ AUTO_REFRESH = { TRUE | FALSE } ]
783///      [ REFRESH_ON_CREATE =  { TRUE | FALSE } ]
784///      [ NOTIFICATION_INTEGRATION = '<notification_integration_name>' ] )
785///
786fn parse_parentheses_options(parser: &mut Parser) -> Result<Vec<DataLoadingOption>, ParserError> {
787    let mut options: Vec<DataLoadingOption> = Vec::new();
788
789    parser.expect_token(&Token::LParen)?;
790    loop {
791        match parser.next_token().token {
792            Token::RParen => break,
793            Token::Word(key) => {
794                parser.expect_token(&Token::Eq)?;
795                if parser.parse_keyword(Keyword::TRUE) {
796                    options.push(DataLoadingOption {
797                        option_name: key.value,
798                        option_type: DataLoadingOptionType::BOOLEAN,
799                        value: "TRUE".to_string(),
800                    });
801                    Ok(())
802                } else if parser.parse_keyword(Keyword::FALSE) {
803                    options.push(DataLoadingOption {
804                        option_name: key.value,
805                        option_type: DataLoadingOptionType::BOOLEAN,
806                        value: "FALSE".to_string(),
807                    });
808                    Ok(())
809                } else {
810                    match parser.next_token().token {
811                        Token::SingleQuotedString(value) => {
812                            options.push(DataLoadingOption {
813                                option_name: key.value,
814                                option_type: DataLoadingOptionType::STRING,
815                                value,
816                            });
817                            Ok(())
818                        }
819                        Token::Word(word) => {
820                            options.push(DataLoadingOption {
821                                option_name: key.value,
822                                option_type: DataLoadingOptionType::ENUM,
823                                value: word.value,
824                            });
825                            Ok(())
826                        }
827                        _ => parser.expected("expected option value", parser.peek_token()),
828                    }
829                }
830            }
831            _ => parser.expected("another option or ')'", parser.peek_token()),
832        }?;
833    }
834    Ok(options)
835}
836
837/// Parsing a property of identity or autoincrement column option
838/// Syntax:
839/// ```sql
840/// [ (seed , increment) | START num INCREMENT num ] [ ORDER | NOORDER ]
841/// ```
842/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
843fn parse_identity_property(parser: &mut Parser) -> Result<IdentityProperty, ParserError> {
844    let parameters = if parser.consume_token(&Token::LParen) {
845        let seed = parser.parse_number()?;
846        parser.expect_token(&Token::Comma)?;
847        let increment = parser.parse_number()?;
848        parser.expect_token(&Token::RParen)?;
849
850        Some(IdentityPropertyFormatKind::FunctionCall(
851            IdentityParameters { seed, increment },
852        ))
853    } else if parser.parse_keyword(Keyword::START) {
854        let seed = parser.parse_number()?;
855        parser.expect_keyword(Keyword::INCREMENT)?;
856        let increment = parser.parse_number()?;
857
858        Some(IdentityPropertyFormatKind::StartAndIncrement(
859            IdentityParameters { seed, increment },
860        ))
861    } else {
862        None
863    };
864    let order = match parser.parse_one_of_keywords(&[Keyword::ORDER, Keyword::NOORDER]) {
865        Some(Keyword::ORDER) => Some(IdentityPropertyOrder::Order),
866        Some(Keyword::NOORDER) => Some(IdentityPropertyOrder::NoOrder),
867        _ => None,
868    };
869    Ok(IdentityProperty { parameters, order })
870}
871
872/// Parsing a policy property of column option
873/// Syntax:
874/// ```sql
875/// <policy_name> [ USING ( <col_name> , <cond_col1> , ... )
876/// ```
877/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
878fn parse_column_policy_property(
879    parser: &mut Parser,
880    with: bool,
881) -> Result<ColumnPolicyProperty, ParserError> {
882    let policy_name = parser.parse_identifier(false)?;
883    let using_columns = if parser.parse_keyword(Keyword::USING) {
884        parser.expect_token(&Token::LParen)?;
885        let columns = parser.parse_comma_separated(|p| p.parse_identifier(false))?;
886        parser.expect_token(&Token::RParen)?;
887        Some(columns)
888    } else {
889        None
890    };
891
892    Ok(ColumnPolicyProperty {
893        with,
894        policy_name,
895        using_columns,
896    })
897}
898
899/// Parsing tags list of column
900/// Syntax:
901/// ```sql
902/// ( <tag_name> = '<tag_value>' [ , <tag_name> = '<tag_value>' , ... ] )
903/// ```
904/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
905fn parse_column_tags(parser: &mut Parser, with: bool) -> Result<TagsColumnOption, ParserError> {
906    parser.expect_token(&Token::LParen)?;
907    let tags = parser.parse_comma_separated(Parser::parse_tag)?;
908    parser.expect_token(&Token::RParen)?;
909
910    Ok(TagsColumnOption { with, tags })
911}