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