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::attached_token::AttachedToken;
21use crate::ast::helpers::key_value_options::{
22    KeyValueOption, KeyValueOptionKind, KeyValueOptions, KeyValueOptionsDelimiter,
23};
24use crate::ast::helpers::stmt_create_database::CreateDatabaseBuilder;
25use crate::ast::helpers::stmt_create_table::CreateTableBuilder;
26use crate::ast::helpers::stmt_data_loading::{
27    FileStagingCommand, StageLoadSelectItem, StageLoadSelectItemKind, StageParamsObject,
28};
29use crate::ast::{
30    AlterTable, AlterTableOperation, AlterTableType, CatalogSyncNamespaceMode, ColumnOption,
31    ColumnPolicy, ColumnPolicyProperty, ContactEntry, CopyIntoSnowflakeKind, CreateTableLikeKind,
32    DollarQuotedString, Ident, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind,
33    IdentityPropertyKind, IdentityPropertyOrder, InitializeKind, ObjectName, ObjectNamePart,
34    RefreshModeKind, RowAccessPolicy, ShowObjects, SqlOption, Statement,
35    StorageSerializationPolicy, TagsColumnOption, Value, WrappedCollection,
36};
37use crate::dialect::{Dialect, Precedence};
38use crate::keywords::Keyword;
39use crate::parser::{IsOptional, Parser, ParserError};
40use crate::tokenizer::Token;
41#[cfg(not(feature = "std"))]
42use alloc::boxed::Box;
43#[cfg(not(feature = "std"))]
44use alloc::string::String;
45#[cfg(not(feature = "std"))]
46use alloc::vec::Vec;
47#[cfg(not(feature = "std"))]
48use alloc::{format, vec};
49
50use super::keywords::RESERVED_FOR_IDENTIFIER;
51
52const RESERVED_KEYWORDS_FOR_SELECT_ITEM_OPERATOR: [Keyword; 1] = [Keyword::CONNECT_BY_ROOT];
53
54// See: <https://docs.snowflake.com/en/sql-reference/reserved-keywords>
55const RESERVED_KEYWORDS_FOR_TABLE_FACTOR: &[Keyword] = &[
56    Keyword::ALL,
57    Keyword::ALTER,
58    Keyword::AND,
59    Keyword::ANY,
60    Keyword::AS,
61    Keyword::BETWEEN,
62    Keyword::BY,
63    Keyword::CHECK,
64    Keyword::COLUMN,
65    Keyword::CONNECT,
66    Keyword::CREATE,
67    Keyword::CROSS,
68    Keyword::CURRENT,
69    Keyword::DELETE,
70    Keyword::DISTINCT,
71    Keyword::DROP,
72    Keyword::ELSE,
73    Keyword::EXISTS,
74    Keyword::FOLLOWING,
75    Keyword::FOR,
76    Keyword::FROM,
77    Keyword::FULL,
78    Keyword::GRANT,
79    Keyword::GROUP,
80    Keyword::HAVING,
81    Keyword::ILIKE,
82    Keyword::IN,
83    Keyword::INCREMENT,
84    Keyword::INNER,
85    Keyword::INSERT,
86    Keyword::INTERSECT,
87    Keyword::INTO,
88    Keyword::IS,
89    Keyword::JOIN,
90    Keyword::LEFT,
91    Keyword::LIKE,
92    Keyword::MINUS,
93    Keyword::NATURAL,
94    Keyword::NOT,
95    Keyword::NULL,
96    Keyword::OF,
97    Keyword::ON,
98    Keyword::OR,
99    Keyword::ORDER,
100    Keyword::QUALIFY,
101    Keyword::REGEXP,
102    Keyword::REVOKE,
103    Keyword::RIGHT,
104    Keyword::RLIKE,
105    Keyword::ROW,
106    Keyword::ROWS,
107    Keyword::SAMPLE,
108    Keyword::SELECT,
109    Keyword::SET,
110    Keyword::SOME,
111    Keyword::START,
112    Keyword::TABLE,
113    Keyword::TABLESAMPLE,
114    Keyword::THEN,
115    Keyword::TO,
116    Keyword::TRIGGER,
117    Keyword::UNION,
118    Keyword::UNIQUE,
119    Keyword::UPDATE,
120    Keyword::USING,
121    Keyword::VALUES,
122    Keyword::WHEN,
123    Keyword::WHENEVER,
124    Keyword::WHERE,
125    Keyword::WINDOW,
126    Keyword::WITH,
127];
128
129/// A [`Dialect`] for [Snowflake](https://www.snowflake.com/)
130#[derive(Debug, Default)]
131pub struct SnowflakeDialect;
132
133impl Dialect for SnowflakeDialect {
134    // see https://docs.snowflake.com/en/sql-reference/identifiers-syntax.html
135    fn is_identifier_start(&self, ch: char) -> bool {
136        ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch == '_'
137    }
138
139    fn supports_projection_trailing_commas(&self) -> bool {
140        true
141    }
142
143    fn supports_from_trailing_commas(&self) -> bool {
144        true
145    }
146
147    // Snowflake supports double-dot notation when the schema name is not specified
148    // In this case the default PUBLIC schema is used
149    //
150    // see https://docs.snowflake.com/en/sql-reference/name-resolution#resolution-when-schema-omitted-double-dot-notation
151    fn supports_object_name_double_dot_notation(&self) -> bool {
152        true
153    }
154
155    fn is_identifier_part(&self, ch: char) -> bool {
156        ch.is_ascii_lowercase()
157            || ch.is_ascii_uppercase()
158            || ch.is_ascii_digit()
159            || ch == '$'
160            || ch == '_'
161    }
162
163    // See https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#escape_sequences
164    fn supports_string_literal_backslash_escape(&self) -> bool {
165        true
166    }
167
168    fn supports_within_after_array_aggregation(&self) -> bool {
169        true
170    }
171
172    /// See <https://docs.snowflake.com/en/sql-reference/constructs/where#joins-in-the-where-clause>
173    fn supports_outer_join_operator(&self) -> bool {
174        true
175    }
176
177    fn supports_connect_by(&self) -> bool {
178        true
179    }
180
181    /// See <https://docs.snowflake.com/en/sql-reference/sql/execute-immediate>
182    fn supports_execute_immediate(&self) -> bool {
183        true
184    }
185
186    fn supports_match_recognize(&self) -> bool {
187        true
188    }
189
190    // Snowflake uses this syntax for "object constants" (the values of which
191    // are not actually required to be constants).
192    //
193    // https://docs.snowflake.com/en/sql-reference/data-types-semistructured#label-object-constant
194    fn supports_dictionary_syntax(&self) -> bool {
195        true
196    }
197
198    // Snowflake doesn't document this but `FIRST_VALUE(arg, { IGNORE | RESPECT } NULLS)`
199    // works (i.e. inside the argument list instead of after).
200    fn supports_window_function_null_treatment_arg(&self) -> bool {
201        true
202    }
203
204    /// See [doc](https://docs.snowflake.com/en/sql-reference/sql/set#syntax)
205    fn supports_parenthesized_set_variables(&self) -> bool {
206        true
207    }
208
209    /// See [doc](https://docs.snowflake.com/en/sql-reference/sql/comment)
210    fn supports_comment_on(&self) -> bool {
211        true
212    }
213
214    fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
215        if parser.parse_keyword(Keyword::BEGIN) {
216            return Some(parser.parse_begin_exception_end());
217        }
218
219        if parser.parse_keywords(&[Keyword::ALTER, Keyword::DYNAMIC, Keyword::TABLE]) {
220            // ALTER DYNAMIC TABLE
221            return Some(parse_alter_dynamic_table(parser));
222        }
223
224        if parser.parse_keywords(&[Keyword::ALTER, Keyword::SESSION]) {
225            // ALTER SESSION
226            let set = match parser.parse_one_of_keywords(&[Keyword::SET, Keyword::UNSET]) {
227                Some(Keyword::SET) => true,
228                Some(Keyword::UNSET) => false,
229                _ => return Some(parser.expected("SET or UNSET", parser.peek_token())),
230            };
231            return Some(parse_alter_session(parser, set));
232        }
233
234        if parser.parse_keyword(Keyword::CREATE) {
235            // possibly CREATE STAGE
236            //[ OR  REPLACE ]
237            let or_replace = parser.parse_keywords(&[Keyword::OR, Keyword::REPLACE]);
238            // LOCAL | GLOBAL
239            let global = match parser.parse_one_of_keywords(&[Keyword::LOCAL, Keyword::GLOBAL]) {
240                Some(Keyword::LOCAL) => Some(false),
241                Some(Keyword::GLOBAL) => Some(true),
242                _ => None,
243            };
244
245            let dynamic = parser.parse_keyword(Keyword::DYNAMIC);
246
247            let mut temporary = false;
248            let mut volatile = false;
249            let mut transient = false;
250            let mut iceberg = false;
251
252            match parser.parse_one_of_keywords(&[
253                Keyword::TEMP,
254                Keyword::TEMPORARY,
255                Keyword::VOLATILE,
256                Keyword::TRANSIENT,
257                Keyword::ICEBERG,
258            ]) {
259                Some(Keyword::TEMP | Keyword::TEMPORARY) => temporary = true,
260                Some(Keyword::VOLATILE) => volatile = true,
261                Some(Keyword::TRANSIENT) => transient = true,
262                Some(Keyword::ICEBERG) => iceberg = true,
263                _ => {}
264            }
265
266            if parser.parse_keyword(Keyword::STAGE) {
267                // OK - this is CREATE STAGE statement
268                return Some(parse_create_stage(or_replace, temporary, parser));
269            } else if parser.parse_keyword(Keyword::TABLE) {
270                return Some(parse_create_table(
271                    or_replace, global, temporary, volatile, transient, iceberg, dynamic, parser,
272                ));
273            } else if parser.parse_keyword(Keyword::DATABASE) {
274                return Some(parse_create_database(or_replace, transient, parser));
275            } else {
276                // need to go back with the cursor
277                let mut back = 1;
278                if or_replace {
279                    back += 2
280                }
281                if temporary {
282                    back += 1
283                }
284                for _i in 0..back {
285                    parser.prev_token();
286                }
287            }
288        }
289        if parser.parse_keywords(&[Keyword::COPY, Keyword::INTO]) {
290            // COPY INTO
291            return Some(parse_copy_into(parser));
292        }
293
294        if let Some(kw) = parser.parse_one_of_keywords(&[
295            Keyword::LIST,
296            Keyword::LS,
297            Keyword::REMOVE,
298            Keyword::RM,
299        ]) {
300            return Some(parse_file_staging_command(kw, parser));
301        }
302
303        if parser.parse_keyword(Keyword::SHOW) {
304            let terse = parser.parse_keyword(Keyword::TERSE);
305            if parser.parse_keyword(Keyword::OBJECTS) {
306                return Some(parse_show_objects(terse, parser));
307            }
308            //Give back Keyword::TERSE
309            if terse {
310                parser.prev_token();
311            }
312            //Give back Keyword::SHOW
313            parser.prev_token();
314        }
315
316        None
317    }
318
319    fn parse_column_option(
320        &self,
321        parser: &mut Parser,
322    ) -> Result<Option<Result<Option<ColumnOption>, ParserError>>, ParserError> {
323        parser.maybe_parse(|parser| {
324            let with = parser.parse_keyword(Keyword::WITH);
325
326            if parser.parse_keyword(Keyword::IDENTITY) {
327                Ok(parse_identity_property(parser)
328                    .map(|p| Some(ColumnOption::Identity(IdentityPropertyKind::Identity(p)))))
329            } else if parser.parse_keyword(Keyword::AUTOINCREMENT) {
330                Ok(parse_identity_property(parser).map(|p| {
331                    Some(ColumnOption::Identity(IdentityPropertyKind::Autoincrement(
332                        p,
333                    )))
334                }))
335            } else if parser.parse_keywords(&[Keyword::MASKING, Keyword::POLICY]) {
336                Ok(parse_column_policy_property(parser, with)
337                    .map(|p| Some(ColumnOption::Policy(ColumnPolicy::MaskingPolicy(p)))))
338            } else if parser.parse_keywords(&[Keyword::PROJECTION, Keyword::POLICY]) {
339                Ok(parse_column_policy_property(parser, with)
340                    .map(|p| Some(ColumnOption::Policy(ColumnPolicy::ProjectionPolicy(p)))))
341            } else if parser.parse_keywords(&[Keyword::TAG]) {
342                Ok(parse_column_tags(parser, with).map(|p| Some(ColumnOption::Tags(p))))
343            } else {
344                Err(ParserError::ParserError("not found match".to_string()))
345            }
346        })
347    }
348
349    fn get_next_precedence(&self, parser: &Parser) -> Option<Result<u8, ParserError>> {
350        let token = parser.peek_token();
351        // Snowflake supports the `:` cast operator unlike other dialects
352        match token.token {
353            Token::Colon => Some(Ok(self.prec_value(Precedence::DoubleColon))),
354            _ => None,
355        }
356    }
357
358    fn describe_requires_table_keyword(&self) -> bool {
359        true
360    }
361
362    fn allow_extract_custom(&self) -> bool {
363        true
364    }
365
366    fn allow_extract_single_quotes(&self) -> bool {
367        true
368    }
369
370    /// Snowflake expects the `LIKE` option before the `IN` option,
371    /// for example: <https://docs.snowflake.com/en/sql-reference/sql/show-views#syntax>
372    fn supports_show_like_before_in(&self) -> bool {
373        true
374    }
375
376    fn supports_left_associative_joins_without_parens(&self) -> bool {
377        false
378    }
379
380    fn is_reserved_for_identifier(&self, kw: Keyword) -> bool {
381        // Unreserve some keywords that Snowflake accepts as identifiers
382        // See: https://docs.snowflake.com/en/sql-reference/reserved-keywords
383        if matches!(kw, Keyword::INTERVAL) {
384            false
385        } else {
386            RESERVED_FOR_IDENTIFIER.contains(&kw)
387        }
388    }
389
390    fn supports_partiql(&self) -> bool {
391        true
392    }
393
394    fn is_column_alias(&self, kw: &Keyword, parser: &mut Parser) -> bool {
395        match kw {
396            // The following keywords can be considered an alias as long as
397            // they are not followed by other tokens that may change their meaning
398            // e.g. `SELECT * EXCEPT (col1) FROM tbl`
399            Keyword::EXCEPT
400            // e.g. `INSERT INTO t SELECT 1 RETURNING *`
401            | Keyword::RETURNING if !matches!(parser.peek_token_ref().token, Token::Comma | Token::EOF) =>
402            {
403                false
404            }
405
406            // e.g. `SELECT 1 LIMIT 5` - not an alias
407            // e.g. `SELECT 1 OFFSET 5 ROWS` - not an alias
408            Keyword::LIMIT | Keyword::OFFSET if peek_for_limit_options(parser) => false,
409
410            // `FETCH` can be considered an alias as long as it's not followed by `FIRST`` or `NEXT`
411            // which would give it a different meanings, for example:
412            // `SELECT 1 FETCH FIRST 10 ROWS` - not an alias
413            // `SELECT 1 FETCH 10` - not an alias
414            Keyword::FETCH if parser.peek_one_of_keywords(&[Keyword::FIRST, Keyword::NEXT]).is_some()
415                    || peek_for_limit_options(parser) =>
416            {
417                false
418            }
419
420            // Reserved keywords by the Snowflake dialect, which seem to be less strictive
421            // than what is listed in `keywords::RESERVED_FOR_COLUMN_ALIAS`. The following
422            // keywords were tested with the this statement: `SELECT 1 <KW>`.
423            Keyword::FROM
424            | Keyword::GROUP
425            | Keyword::HAVING
426            | Keyword::INTERSECT
427            | Keyword::INTO
428            | Keyword::MINUS
429            | Keyword::ORDER
430            | Keyword::SELECT
431            | Keyword::UNION
432            | Keyword::WHERE
433            | Keyword::WITH => false,
434
435            // Any other word is considered an alias
436            _ => true,
437        }
438    }
439
440    fn is_table_alias(&self, kw: &Keyword, parser: &mut Parser) -> bool {
441        match kw {
442            // The following keywords can be considered an alias as long as
443            // they are not followed by other tokens that may change their meaning
444            Keyword::RETURNING
445            | Keyword::INNER
446            | Keyword::USING
447            | Keyword::PIVOT
448            | Keyword::UNPIVOT
449            | Keyword::EXCEPT
450            | Keyword::MATCH_RECOGNIZE
451                if !matches!(parser.peek_token_ref().token, Token::SemiColon | Token::EOF) =>
452            {
453                false
454            }
455
456            // `LIMIT` can be considered an alias as long as it's not followed by a value. For example:
457            // `SELECT * FROM tbl LIMIT WHERE 1=1` - alias
458            // `SELECT * FROM tbl LIMIT 3` - not an alias
459            Keyword::LIMIT | Keyword::OFFSET if peek_for_limit_options(parser) => false,
460
461            // `FETCH` can be considered an alias as long as it's not followed by `FIRST`` or `NEXT`
462            // which would give it a different meanings, for example:
463            // `SELECT * FROM tbl FETCH FIRST 10 ROWS` - not an alias
464            // `SELECT * FROM tbl FETCH 10` - not an alias
465            Keyword::FETCH
466                if parser
467                    .peek_one_of_keywords(&[Keyword::FIRST, Keyword::NEXT])
468                    .is_some()
469                    || peek_for_limit_options(parser) =>
470            {
471                false
472            }
473
474            // All sorts of join-related keywords can be considered aliases unless additional
475            // keywords change their meaning.
476            Keyword::RIGHT | Keyword::LEFT | Keyword::SEMI | Keyword::ANTI
477                if parser
478                    .peek_one_of_keywords(&[Keyword::JOIN, Keyword::OUTER])
479                    .is_some() =>
480            {
481                false
482            }
483
484            Keyword::GLOBAL if parser.peek_keyword(Keyword::FULL) => false,
485
486            // Reserved keywords by the Snowflake dialect, which seem to be less strictive
487            // than what is listed in `keywords::RESERVED_FOR_TABLE_ALIAS`. The following
488            // keywords were tested with the this statement: `SELECT <KW>.* FROM tbl <KW>`.
489            Keyword::WITH
490            | Keyword::ORDER
491            | Keyword::SELECT
492            | Keyword::WHERE
493            | Keyword::GROUP
494            | Keyword::HAVING
495            | Keyword::LATERAL
496            | Keyword::UNION
497            | Keyword::INTERSECT
498            | Keyword::MINUS
499            | Keyword::ON
500            | Keyword::JOIN
501            | Keyword::INNER
502            | Keyword::CROSS
503            | Keyword::FULL
504            | Keyword::LEFT
505            | Keyword::RIGHT
506            | Keyword::NATURAL
507            | Keyword::USING
508            | Keyword::ASOF
509            | Keyword::MATCH_CONDITION
510            | Keyword::SET
511            | Keyword::QUALIFY
512            | Keyword::FOR
513            | Keyword::START
514            | Keyword::CONNECT
515            | Keyword::SAMPLE
516            | Keyword::TABLESAMPLE
517            | Keyword::FROM => false,
518
519            // Any other word is considered an alias
520            _ => true,
521        }
522    }
523
524    fn is_table_factor(&self, kw: &Keyword, parser: &mut Parser) -> bool {
525        match kw {
526            Keyword::LIMIT if peek_for_limit_options(parser) => false,
527            // Table function
528            Keyword::TABLE if matches!(parser.peek_token_ref().token, Token::LParen) => true,
529            _ => !RESERVED_KEYWORDS_FOR_TABLE_FACTOR.contains(kw),
530        }
531    }
532
533    /// See: <https://docs.snowflake.com/en/sql-reference/constructs/at-before>
534    fn supports_timestamp_versioning(&self) -> bool {
535        true
536    }
537
538    /// See: <https://docs.snowflake.com/en/sql-reference/constructs/group-by>
539    fn supports_group_by_expr(&self) -> bool {
540        true
541    }
542
543    /// See: <https://docs.snowflake.com/en/sql-reference/constructs/connect-by>
544    fn get_reserved_keywords_for_select_item_operator(&self) -> &[Keyword] {
545        &RESERVED_KEYWORDS_FOR_SELECT_ITEM_OPERATOR
546    }
547
548    fn supports_space_separated_column_options(&self) -> bool {
549        true
550    }
551
552    fn supports_comma_separated_drop_column_list(&self) -> bool {
553        true
554    }
555
556    fn is_identifier_generating_function_name(
557        &self,
558        ident: &Ident,
559        name_parts: &[ObjectNamePart],
560    ) -> bool {
561        ident.quote_style.is_none()
562            && ident.value.to_lowercase() == "identifier"
563            && !name_parts
564                .iter()
565                .any(|p| matches!(p, ObjectNamePart::Function(_)))
566    }
567
568    // For example: `SELECT IDENTIFIER('alias1').* FROM tbl AS alias1`
569    fn supports_select_expr_star(&self) -> bool {
570        true
571    }
572
573    fn supports_select_wildcard_exclude(&self) -> bool {
574        true
575    }
576
577    fn supports_semantic_view_table_factor(&self) -> bool {
578        true
579    }
580}
581
582// Peeks ahead to identify tokens that are expected after
583// a LIMIT/FETCH keyword.
584fn peek_for_limit_options(parser: &Parser) -> bool {
585    match &parser.peek_token_ref().token {
586        Token::Number(_, _) | Token::Placeholder(_) => true,
587        Token::SingleQuotedString(val) if val.is_empty() => true,
588        Token::DollarQuotedString(DollarQuotedString { value, .. }) if value.is_empty() => true,
589        Token::Word(w) if w.keyword == Keyword::NULL => true,
590        _ => false,
591    }
592}
593
594fn parse_file_staging_command(kw: Keyword, parser: &mut Parser) -> Result<Statement, ParserError> {
595    let stage = parse_snowflake_stage_name(parser)?;
596    let pattern = if parser.parse_keyword(Keyword::PATTERN) {
597        parser.expect_token(&Token::Eq)?;
598        Some(parser.parse_literal_string()?)
599    } else {
600        None
601    };
602
603    match kw {
604        Keyword::LIST | Keyword::LS => Ok(Statement::List(FileStagingCommand { stage, pattern })),
605        Keyword::REMOVE | Keyword::RM => {
606            Ok(Statement::Remove(FileStagingCommand { stage, pattern }))
607        }
608        _ => Err(ParserError::ParserError(
609            "unexpected stage command, expecting LIST, LS, REMOVE or RM".to_string(),
610        )),
611    }
612}
613
614/// Parse snowflake alter dynamic table.
615/// <https://docs.snowflake.com/en/sql-reference/sql/alter-table>
616fn parse_alter_dynamic_table(parser: &mut Parser) -> Result<Statement, ParserError> {
617    // Use parse_object_name(true) to support IDENTIFIER() function
618    let table_name = parser.parse_object_name(true)?;
619
620    // Parse the operation (REFRESH, SUSPEND, or RESUME)
621    let operation = if parser.parse_keyword(Keyword::REFRESH) {
622        AlterTableOperation::Refresh
623    } else if parser.parse_keyword(Keyword::SUSPEND) {
624        AlterTableOperation::Suspend
625    } else if parser.parse_keyword(Keyword::RESUME) {
626        AlterTableOperation::Resume
627    } else {
628        return parser.expected(
629            "REFRESH, SUSPEND, or RESUME after ALTER DYNAMIC TABLE",
630            parser.peek_token(),
631        );
632    };
633
634    let end_token = if parser.peek_token_ref().token == Token::SemiColon {
635        parser.peek_token_ref().clone()
636    } else {
637        parser.get_current_token().clone()
638    };
639
640    Ok(Statement::AlterTable(AlterTable {
641        name: table_name,
642        if_exists: false,
643        only: false,
644        operations: vec![operation],
645        location: None,
646        on_cluster: None,
647        table_type: Some(AlterTableType::Dynamic),
648        end_token: AttachedToken(end_token),
649    }))
650}
651
652/// Parse snowflake alter session.
653/// <https://docs.snowflake.com/en/sql-reference/sql/alter-session>
654fn parse_alter_session(parser: &mut Parser, set: bool) -> Result<Statement, ParserError> {
655    let session_options = parse_session_options(parser, set)?;
656    Ok(Statement::AlterSession {
657        set,
658        session_params: KeyValueOptions {
659            options: session_options,
660            delimiter: KeyValueOptionsDelimiter::Space,
661        },
662    })
663}
664
665/// Parse snowflake create table statement.
666/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
667/// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
668#[allow(clippy::too_many_arguments)]
669pub fn parse_create_table(
670    or_replace: bool,
671    global: Option<bool>,
672    temporary: bool,
673    volatile: bool,
674    transient: bool,
675    iceberg: bool,
676    dynamic: bool,
677    parser: &mut Parser,
678) -> Result<Statement, ParserError> {
679    let if_not_exists = parser.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
680    let table_name = parser.parse_object_name(false)?;
681
682    let mut builder = CreateTableBuilder::new(table_name)
683        .or_replace(or_replace)
684        .if_not_exists(if_not_exists)
685        .temporary(temporary)
686        .transient(transient)
687        .volatile(volatile)
688        .iceberg(iceberg)
689        .global(global)
690        .dynamic(dynamic)
691        .hive_formats(None);
692
693    // Snowflake does not enforce order of the parameters in the statement. The parser needs to
694    // parse the statement in a loop.
695    //
696    // "CREATE TABLE x COPY GRANTS (c INT)" and "CREATE TABLE x (c INT) COPY GRANTS" are both
697    // accepted by Snowflake
698
699    let mut plain_options = vec![];
700
701    loop {
702        let next_token = parser.next_token();
703        match &next_token.token {
704            Token::Word(word) => match word.keyword {
705                Keyword::COPY => {
706                    parser.expect_keyword_is(Keyword::GRANTS)?;
707                    builder = builder.copy_grants(true);
708                }
709                Keyword::COMMENT => {
710                    // Rewind the COMMENT keyword
711                    parser.prev_token();
712                    if let Some(comment_def) = parser.parse_optional_inline_comment()? {
713                        plain_options.push(SqlOption::Comment(comment_def))
714                    }
715                }
716                Keyword::AS => {
717                    let query = parser.parse_query()?;
718                    builder = builder.query(Some(query));
719                }
720                Keyword::CLONE => {
721                    let clone = parser.parse_object_name(false).ok();
722                    builder = builder.clone_clause(clone);
723                }
724                Keyword::LIKE => {
725                    let name = parser.parse_object_name(false)?;
726                    builder = builder.like(Some(CreateTableLikeKind::Plain(
727                        crate::ast::CreateTableLike {
728                            name,
729                            defaults: None,
730                        },
731                    )));
732                }
733                Keyword::CLUSTER => {
734                    parser.expect_keyword_is(Keyword::BY)?;
735                    parser.expect_token(&Token::LParen)?;
736                    let cluster_by = Some(WrappedCollection::Parentheses(
737                        parser.parse_comma_separated(|p| p.parse_expr())?,
738                    ));
739                    parser.expect_token(&Token::RParen)?;
740
741                    builder = builder.cluster_by(cluster_by)
742                }
743                Keyword::ENABLE_SCHEMA_EVOLUTION => {
744                    parser.expect_token(&Token::Eq)?;
745                    builder = builder.enable_schema_evolution(Some(parser.parse_boolean_string()?));
746                }
747                Keyword::CHANGE_TRACKING => {
748                    parser.expect_token(&Token::Eq)?;
749                    builder = builder.change_tracking(Some(parser.parse_boolean_string()?));
750                }
751                Keyword::DATA_RETENTION_TIME_IN_DAYS => {
752                    parser.expect_token(&Token::Eq)?;
753                    let data_retention_time_in_days = parser.parse_literal_uint()?;
754                    builder =
755                        builder.data_retention_time_in_days(Some(data_retention_time_in_days));
756                }
757                Keyword::MAX_DATA_EXTENSION_TIME_IN_DAYS => {
758                    parser.expect_token(&Token::Eq)?;
759                    let max_data_extension_time_in_days = parser.parse_literal_uint()?;
760                    builder = builder
761                        .max_data_extension_time_in_days(Some(max_data_extension_time_in_days));
762                }
763                Keyword::DEFAULT_DDL_COLLATION => {
764                    parser.expect_token(&Token::Eq)?;
765                    let default_ddl_collation = parser.parse_literal_string()?;
766                    builder = builder.default_ddl_collation(Some(default_ddl_collation));
767                }
768                // WITH is optional, we just verify that next token is one of the expected ones and
769                // fallback to the default match statement
770                Keyword::WITH => {
771                    parser.expect_one_of_keywords(&[
772                        Keyword::AGGREGATION,
773                        Keyword::TAG,
774                        Keyword::ROW,
775                    ])?;
776                    parser.prev_token();
777                }
778                Keyword::AGGREGATION => {
779                    parser.expect_keyword_is(Keyword::POLICY)?;
780                    let aggregation_policy = parser.parse_object_name(false)?;
781                    builder = builder.with_aggregation_policy(Some(aggregation_policy));
782                }
783                Keyword::ROW => {
784                    parser.expect_keywords(&[Keyword::ACCESS, Keyword::POLICY])?;
785                    let policy = parser.parse_object_name(false)?;
786                    parser.expect_keyword_is(Keyword::ON)?;
787                    parser.expect_token(&Token::LParen)?;
788                    let columns = parser.parse_comma_separated(|p| p.parse_identifier())?;
789                    parser.expect_token(&Token::RParen)?;
790
791                    builder =
792                        builder.with_row_access_policy(Some(RowAccessPolicy::new(policy, columns)))
793                }
794                Keyword::TAG => {
795                    parser.expect_token(&Token::LParen)?;
796                    let tags = parser.parse_comma_separated(Parser::parse_tag)?;
797                    parser.expect_token(&Token::RParen)?;
798                    builder = builder.with_tags(Some(tags));
799                }
800                Keyword::ON if parser.parse_keyword(Keyword::COMMIT) => {
801                    let on_commit = Some(parser.parse_create_table_on_commit()?);
802                    builder = builder.on_commit(on_commit);
803                }
804                Keyword::EXTERNAL_VOLUME => {
805                    parser.expect_token(&Token::Eq)?;
806                    builder.external_volume = Some(parser.parse_literal_string()?);
807                }
808                Keyword::CATALOG => {
809                    parser.expect_token(&Token::Eq)?;
810                    builder.catalog = Some(parser.parse_literal_string()?);
811                }
812                Keyword::BASE_LOCATION => {
813                    parser.expect_token(&Token::Eq)?;
814                    builder.base_location = Some(parser.parse_literal_string()?);
815                }
816                Keyword::CATALOG_SYNC => {
817                    parser.expect_token(&Token::Eq)?;
818                    builder.catalog_sync = Some(parser.parse_literal_string()?);
819                }
820                Keyword::STORAGE_SERIALIZATION_POLICY => {
821                    parser.expect_token(&Token::Eq)?;
822
823                    builder.storage_serialization_policy =
824                        Some(parse_storage_serialization_policy(parser)?);
825                }
826                Keyword::IF if parser.parse_keywords(&[Keyword::NOT, Keyword::EXISTS]) => {
827                    builder = builder.if_not_exists(true);
828                }
829                Keyword::TARGET_LAG => {
830                    parser.expect_token(&Token::Eq)?;
831                    let target_lag = parser.parse_literal_string()?;
832                    builder = builder.target_lag(Some(target_lag));
833                }
834                Keyword::WAREHOUSE => {
835                    parser.expect_token(&Token::Eq)?;
836                    let warehouse = parser.parse_identifier()?;
837                    builder = builder.warehouse(Some(warehouse));
838                }
839                Keyword::AT | Keyword::BEFORE => {
840                    parser.prev_token();
841                    let version = parser.maybe_parse_table_version()?;
842                    builder = builder.version(version);
843                }
844                Keyword::REFRESH_MODE => {
845                    parser.expect_token(&Token::Eq)?;
846                    let refresh_mode = match parser.parse_one_of_keywords(&[
847                        Keyword::AUTO,
848                        Keyword::FULL,
849                        Keyword::INCREMENTAL,
850                    ]) {
851                        Some(Keyword::AUTO) => Some(RefreshModeKind::Auto),
852                        Some(Keyword::FULL) => Some(RefreshModeKind::Full),
853                        Some(Keyword::INCREMENTAL) => Some(RefreshModeKind::Incremental),
854                        _ => return parser.expected("AUTO, FULL or INCREMENTAL", next_token),
855                    };
856                    builder = builder.refresh_mode(refresh_mode);
857                }
858                Keyword::INITIALIZE => {
859                    parser.expect_token(&Token::Eq)?;
860                    let initialize = match parser
861                        .parse_one_of_keywords(&[Keyword::ON_CREATE, Keyword::ON_SCHEDULE])
862                    {
863                        Some(Keyword::ON_CREATE) => Some(InitializeKind::OnCreate),
864                        Some(Keyword::ON_SCHEDULE) => Some(InitializeKind::OnSchedule),
865                        _ => return parser.expected("ON_CREATE or ON_SCHEDULE", next_token),
866                    };
867                    builder = builder.initialize(initialize);
868                }
869                Keyword::REQUIRE if parser.parse_keyword(Keyword::USER) => {
870                    builder = builder.require_user(true);
871                }
872                _ => {
873                    return parser.expected("end of statement", next_token);
874                }
875            },
876            Token::LParen => {
877                parser.prev_token();
878                let (columns, constraints) = parser.parse_columns()?;
879                builder = builder.columns(columns).constraints(constraints);
880            }
881            Token::EOF => {
882                break;
883            }
884            Token::SemiColon => {
885                parser.prev_token();
886                break;
887            }
888            _ => {
889                return parser.expected("end of statement", next_token);
890            }
891        }
892    }
893    let table_options = if !plain_options.is_empty() {
894        crate::ast::CreateTableOptions::Plain(plain_options)
895    } else {
896        crate::ast::CreateTableOptions::None
897    };
898
899    builder = builder.table_options(table_options);
900
901    if iceberg && builder.base_location.is_none() {
902        return Err(ParserError::ParserError(
903            "BASE_LOCATION is required for ICEBERG tables".to_string(),
904        ));
905    }
906
907    Ok(builder.build())
908}
909
910/// Parse snowflake create database statement.
911/// <https://docs.snowflake.com/en/sql-reference/sql/create-database>
912pub fn parse_create_database(
913    or_replace: bool,
914    transient: bool,
915    parser: &mut Parser,
916) -> Result<Statement, ParserError> {
917    let if_not_exists = parser.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
918    let name = parser.parse_object_name(false)?;
919
920    let mut builder = CreateDatabaseBuilder::new(name)
921        .or_replace(or_replace)
922        .transient(transient)
923        .if_not_exists(if_not_exists);
924
925    loop {
926        let next_token = parser.next_token();
927        match &next_token.token {
928            Token::Word(word) => match word.keyword {
929                Keyword::CLONE => {
930                    builder = builder.clone_clause(Some(parser.parse_object_name(false)?));
931                }
932                Keyword::DATA_RETENTION_TIME_IN_DAYS => {
933                    parser.expect_token(&Token::Eq)?;
934                    builder =
935                        builder.data_retention_time_in_days(Some(parser.parse_literal_uint()?));
936                }
937                Keyword::MAX_DATA_EXTENSION_TIME_IN_DAYS => {
938                    parser.expect_token(&Token::Eq)?;
939                    builder =
940                        builder.max_data_extension_time_in_days(Some(parser.parse_literal_uint()?));
941                }
942                Keyword::EXTERNAL_VOLUME => {
943                    parser.expect_token(&Token::Eq)?;
944                    builder = builder.external_volume(Some(parser.parse_literal_string()?));
945                }
946                Keyword::CATALOG => {
947                    parser.expect_token(&Token::Eq)?;
948                    builder = builder.catalog(Some(parser.parse_literal_string()?));
949                }
950                Keyword::REPLACE_INVALID_CHARACTERS => {
951                    parser.expect_token(&Token::Eq)?;
952                    builder =
953                        builder.replace_invalid_characters(Some(parser.parse_boolean_string()?));
954                }
955                Keyword::DEFAULT_DDL_COLLATION => {
956                    parser.expect_token(&Token::Eq)?;
957                    builder = builder.default_ddl_collation(Some(parser.parse_literal_string()?));
958                }
959                Keyword::STORAGE_SERIALIZATION_POLICY => {
960                    parser.expect_token(&Token::Eq)?;
961                    let policy = parse_storage_serialization_policy(parser)?;
962                    builder = builder.storage_serialization_policy(Some(policy));
963                }
964                Keyword::COMMENT => {
965                    parser.expect_token(&Token::Eq)?;
966                    builder = builder.comment(Some(parser.parse_literal_string()?));
967                }
968                Keyword::CATALOG_SYNC => {
969                    parser.expect_token(&Token::Eq)?;
970                    builder = builder.catalog_sync(Some(parser.parse_literal_string()?));
971                }
972                Keyword::CATALOG_SYNC_NAMESPACE_FLATTEN_DELIMITER => {
973                    parser.expect_token(&Token::Eq)?;
974                    builder = builder.catalog_sync_namespace_flatten_delimiter(Some(
975                        parser.parse_literal_string()?,
976                    ));
977                }
978                Keyword::CATALOG_SYNC_NAMESPACE_MODE => {
979                    parser.expect_token(&Token::Eq)?;
980                    let mode =
981                        match parser.parse_one_of_keywords(&[Keyword::NEST, Keyword::FLATTEN]) {
982                            Some(Keyword::NEST) => CatalogSyncNamespaceMode::Nest,
983                            Some(Keyword::FLATTEN) => CatalogSyncNamespaceMode::Flatten,
984                            _ => {
985                                return parser.expected("NEST or FLATTEN", next_token);
986                            }
987                        };
988                    builder = builder.catalog_sync_namespace_mode(Some(mode));
989                }
990                Keyword::WITH => {
991                    if parser.parse_keyword(Keyword::TAG) {
992                        parser.expect_token(&Token::LParen)?;
993                        let tags = parser.parse_comma_separated(Parser::parse_tag)?;
994                        parser.expect_token(&Token::RParen)?;
995                        builder = builder.with_tags(Some(tags));
996                    } else if parser.parse_keyword(Keyword::CONTACT) {
997                        parser.expect_token(&Token::LParen)?;
998                        let contacts = parser.parse_comma_separated(|p| {
999                            let purpose = p.parse_identifier()?.value;
1000                            p.expect_token(&Token::Eq)?;
1001                            let contact = p.parse_identifier()?.value;
1002                            Ok(ContactEntry { purpose, contact })
1003                        })?;
1004                        parser.expect_token(&Token::RParen)?;
1005                        builder = builder.with_contacts(Some(contacts));
1006                    } else {
1007                        return parser.expected("TAG or CONTACT", next_token);
1008                    }
1009                }
1010                _ => return parser.expected("end of statement", next_token),
1011            },
1012            Token::SemiColon | Token::EOF => break,
1013            _ => return parser.expected("end of statement", next_token),
1014        }
1015    }
1016    Ok(builder.build())
1017}
1018
1019pub fn parse_storage_serialization_policy(
1020    parser: &mut Parser,
1021) -> Result<StorageSerializationPolicy, ParserError> {
1022    let next_token = parser.next_token();
1023    match &next_token.token {
1024        Token::Word(w) => match w.keyword {
1025            Keyword::COMPATIBLE => Ok(StorageSerializationPolicy::Compatible),
1026            Keyword::OPTIMIZED => Ok(StorageSerializationPolicy::Optimized),
1027            _ => parser.expected("storage_serialization_policy", next_token),
1028        },
1029        _ => parser.expected("storage_serialization_policy", next_token),
1030    }
1031}
1032
1033pub fn parse_create_stage(
1034    or_replace: bool,
1035    temporary: bool,
1036    parser: &mut Parser,
1037) -> Result<Statement, ParserError> {
1038    //[ IF NOT EXISTS ]
1039    let if_not_exists = parser.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
1040    let name = parser.parse_object_name(false)?;
1041    let mut directory_table_params = Vec::new();
1042    let mut file_format = Vec::new();
1043    let mut copy_options = Vec::new();
1044    let mut comment = None;
1045
1046    // [ internalStageParams | externalStageParams ]
1047    let stage_params = parse_stage_params(parser)?;
1048
1049    // [ directoryTableParams ]
1050    if parser.parse_keyword(Keyword::DIRECTORY) {
1051        parser.expect_token(&Token::Eq)?;
1052        directory_table_params = parser.parse_key_value_options(true, &[])?.options;
1053    }
1054
1055    // [ file_format]
1056    if parser.parse_keyword(Keyword::FILE_FORMAT) {
1057        parser.expect_token(&Token::Eq)?;
1058        file_format = parser.parse_key_value_options(true, &[])?.options;
1059    }
1060
1061    // [ copy_options ]
1062    if parser.parse_keyword(Keyword::COPY_OPTIONS) {
1063        parser.expect_token(&Token::Eq)?;
1064        copy_options = parser.parse_key_value_options(true, &[])?.options;
1065    }
1066
1067    // [ comment ]
1068    if parser.parse_keyword(Keyword::COMMENT) {
1069        parser.expect_token(&Token::Eq)?;
1070        comment = Some(parser.parse_comment_value()?);
1071    }
1072
1073    Ok(Statement::CreateStage {
1074        or_replace,
1075        temporary,
1076        if_not_exists,
1077        name,
1078        stage_params,
1079        directory_table_params: KeyValueOptions {
1080            options: directory_table_params,
1081            delimiter: KeyValueOptionsDelimiter::Space,
1082        },
1083        file_format: KeyValueOptions {
1084            options: file_format,
1085            delimiter: KeyValueOptionsDelimiter::Space,
1086        },
1087        copy_options: KeyValueOptions {
1088            options: copy_options,
1089            delimiter: KeyValueOptionsDelimiter::Space,
1090        },
1091        comment,
1092    })
1093}
1094
1095pub fn parse_stage_name_identifier(parser: &mut Parser) -> Result<Ident, ParserError> {
1096    let mut ident = String::new();
1097    while let Some(next_token) = parser.next_token_no_skip() {
1098        match &next_token.token {
1099            Token::Whitespace(_) | Token::SemiColon => break,
1100            Token::Period => {
1101                parser.prev_token();
1102                break;
1103            }
1104            Token::RParen => {
1105                parser.prev_token();
1106                break;
1107            }
1108            Token::AtSign => ident.push('@'),
1109            Token::Tilde => ident.push('~'),
1110            Token::Mod => ident.push('%'),
1111            Token::Div => ident.push('/'),
1112            Token::Plus => ident.push('+'),
1113            Token::Minus => ident.push('-'),
1114            Token::Number(n, _) => ident.push_str(n),
1115            Token::Word(w) => ident.push_str(&w.to_string()),
1116            _ => return parser.expected("stage name identifier", parser.peek_token()),
1117        }
1118    }
1119    Ok(Ident::new(ident))
1120}
1121
1122pub fn parse_snowflake_stage_name(parser: &mut Parser) -> Result<ObjectName, ParserError> {
1123    match parser.next_token().token {
1124        Token::AtSign => {
1125            parser.prev_token();
1126            let mut idents = vec![];
1127            loop {
1128                idents.push(parse_stage_name_identifier(parser)?);
1129                if !parser.consume_token(&Token::Period) {
1130                    break;
1131                }
1132            }
1133            Ok(ObjectName::from(idents))
1134        }
1135        _ => {
1136            parser.prev_token();
1137            Ok(parser.parse_object_name(false)?)
1138        }
1139    }
1140}
1141
1142/// Parses a `COPY INTO` statement. Snowflake has two variants, `COPY INTO <table>`
1143/// and `COPY INTO <location>` which have different syntax.
1144pub fn parse_copy_into(parser: &mut Parser) -> Result<Statement, ParserError> {
1145    let kind = match parser.peek_token().token {
1146        // Indicates an internal stage
1147        Token::AtSign => CopyIntoSnowflakeKind::Location,
1148        // Indicates an external stage, i.e. s3://, gcs:// or azure://
1149        Token::SingleQuotedString(s) if s.contains("://") => CopyIntoSnowflakeKind::Location,
1150        _ => CopyIntoSnowflakeKind::Table,
1151    };
1152
1153    let mut files: Vec<String> = vec![];
1154    let mut from_transformations: Option<Vec<StageLoadSelectItemKind>> = None;
1155    let mut from_stage_alias = None;
1156    let mut from_stage = None;
1157    let mut stage_params = StageParamsObject {
1158        url: None,
1159        encryption: KeyValueOptions {
1160            options: vec![],
1161            delimiter: KeyValueOptionsDelimiter::Space,
1162        },
1163        endpoint: None,
1164        storage_integration: None,
1165        credentials: KeyValueOptions {
1166            options: vec![],
1167            delimiter: KeyValueOptionsDelimiter::Space,
1168        },
1169    };
1170    let mut from_query = None;
1171    let mut partition = None;
1172    let mut file_format = Vec::new();
1173    let mut pattern = None;
1174    let mut validation_mode = None;
1175    let mut copy_options = Vec::new();
1176
1177    let into: ObjectName = parse_snowflake_stage_name(parser)?;
1178    if kind == CopyIntoSnowflakeKind::Location {
1179        stage_params = parse_stage_params(parser)?;
1180    }
1181
1182    let into_columns = match &parser.peek_token().token {
1183        Token::LParen => Some(parser.parse_parenthesized_column_list(IsOptional::Optional, true)?),
1184        _ => None,
1185    };
1186
1187    parser.expect_keyword_is(Keyword::FROM)?;
1188    match parser.next_token().token {
1189        Token::LParen if kind == CopyIntoSnowflakeKind::Table => {
1190            // Data load with transformations
1191            parser.expect_keyword_is(Keyword::SELECT)?;
1192            from_transformations = parse_select_items_for_data_load(parser)?;
1193
1194            parser.expect_keyword_is(Keyword::FROM)?;
1195            from_stage = Some(parse_snowflake_stage_name(parser)?);
1196            stage_params = parse_stage_params(parser)?;
1197
1198            // Parse an optional alias
1199            from_stage_alias = parser
1200                .maybe_parse_table_alias()?
1201                .map(|table_alias| table_alias.name);
1202            parser.expect_token(&Token::RParen)?;
1203        }
1204        Token::LParen if kind == CopyIntoSnowflakeKind::Location => {
1205            // Data unload with a query
1206            from_query = Some(parser.parse_query()?);
1207            parser.expect_token(&Token::RParen)?;
1208        }
1209        _ => {
1210            parser.prev_token();
1211            from_stage = Some(parse_snowflake_stage_name(parser)?);
1212            stage_params = parse_stage_params(parser)?;
1213
1214            // as
1215            from_stage_alias = if parser.parse_keyword(Keyword::AS) {
1216                Some(match parser.next_token().token {
1217                    Token::Word(w) => Ok(Ident::new(w.value)),
1218                    _ => parser.expected("stage alias", parser.peek_token()),
1219                }?)
1220            } else {
1221                None
1222            };
1223        }
1224    }
1225
1226    loop {
1227        // FILE_FORMAT
1228        if parser.parse_keyword(Keyword::FILE_FORMAT) {
1229            parser.expect_token(&Token::Eq)?;
1230            file_format = parser.parse_key_value_options(true, &[])?.options;
1231        // PARTITION BY
1232        } else if parser.parse_keywords(&[Keyword::PARTITION, Keyword::BY]) {
1233            partition = Some(Box::new(parser.parse_expr()?))
1234        // FILES
1235        } else if parser.parse_keyword(Keyword::FILES) {
1236            parser.expect_token(&Token::Eq)?;
1237            parser.expect_token(&Token::LParen)?;
1238            let mut continue_loop = true;
1239            while continue_loop {
1240                continue_loop = false;
1241                let next_token = parser.next_token();
1242                match next_token.token {
1243                    Token::SingleQuotedString(s) => files.push(s),
1244                    _ => parser.expected("file token", next_token)?,
1245                };
1246                if parser.next_token().token.eq(&Token::Comma) {
1247                    continue_loop = true;
1248                } else {
1249                    parser.prev_token(); // not a comma, need to go back
1250                }
1251            }
1252            parser.expect_token(&Token::RParen)?;
1253        // PATTERN
1254        } else if parser.parse_keyword(Keyword::PATTERN) {
1255            parser.expect_token(&Token::Eq)?;
1256            let next_token = parser.next_token();
1257            pattern = Some(match next_token.token {
1258                Token::SingleQuotedString(s) => s,
1259                _ => parser.expected("pattern", next_token)?,
1260            });
1261        // VALIDATION MODE
1262        } else if parser.parse_keyword(Keyword::VALIDATION_MODE) {
1263            parser.expect_token(&Token::Eq)?;
1264            validation_mode = Some(parser.next_token().token.to_string());
1265        // COPY OPTIONS
1266        } else if parser.parse_keyword(Keyword::COPY_OPTIONS) {
1267            parser.expect_token(&Token::Eq)?;
1268            copy_options = parser.parse_key_value_options(true, &[])?.options;
1269        } else {
1270            match parser.next_token().token {
1271                Token::SemiColon | Token::EOF => break,
1272                Token::Comma => continue,
1273                // In `COPY INTO <location>` the copy options do not have a shared key
1274                // like in `COPY INTO <table>`
1275                Token::Word(key) => copy_options.push(parser.parse_key_value_option(&key)?),
1276                _ => return parser.expected("another copy option, ; or EOF'", parser.peek_token()),
1277            }
1278        }
1279    }
1280
1281    Ok(Statement::CopyIntoSnowflake {
1282        kind,
1283        into,
1284        into_columns,
1285        from_obj: from_stage,
1286        from_obj_alias: from_stage_alias,
1287        stage_params,
1288        from_transformations,
1289        from_query,
1290        files: if files.is_empty() { None } else { Some(files) },
1291        pattern,
1292        file_format: KeyValueOptions {
1293            options: file_format,
1294            delimiter: KeyValueOptionsDelimiter::Space,
1295        },
1296        copy_options: KeyValueOptions {
1297            options: copy_options,
1298            delimiter: KeyValueOptionsDelimiter::Space,
1299        },
1300        validation_mode,
1301        partition,
1302    })
1303}
1304
1305fn parse_select_items_for_data_load(
1306    parser: &mut Parser,
1307) -> Result<Option<Vec<StageLoadSelectItemKind>>, ParserError> {
1308    let mut select_items: Vec<StageLoadSelectItemKind> = vec![];
1309    loop {
1310        match parser.maybe_parse(parse_select_item_for_data_load)? {
1311            // [<alias>.]$<file_col_num>[.<element>] [ , [<alias>.]$<file_col_num>[.<element>] ... ]
1312            Some(item) => select_items.push(StageLoadSelectItemKind::StageLoadSelectItem(item)),
1313            // Fallback, try to parse a standard SQL select item
1314            None => select_items.push(StageLoadSelectItemKind::SelectItem(
1315                parser.parse_select_item()?,
1316            )),
1317        }
1318        if matches!(parser.peek_token_ref().token, Token::Comma) {
1319            parser.advance_token();
1320        } else {
1321            break;
1322        }
1323    }
1324    Ok(Some(select_items))
1325}
1326
1327fn parse_select_item_for_data_load(
1328    parser: &mut Parser,
1329) -> Result<StageLoadSelectItem, ParserError> {
1330    let mut alias: Option<Ident> = None;
1331    let mut file_col_num: i32 = 0;
1332    let mut element: Option<Ident> = None;
1333    let mut item_as: Option<Ident> = None;
1334
1335    let next_token = parser.next_token();
1336    match next_token.token {
1337        Token::Placeholder(w) => {
1338            file_col_num = w.to_string().split_off(1).parse::<i32>().map_err(|e| {
1339                ParserError::ParserError(format!("Could not parse '{w}' as i32: {e}"))
1340            })?;
1341            Ok(())
1342        }
1343        Token::Word(w) => {
1344            alias = Some(Ident::new(w.value));
1345            Ok(())
1346        }
1347        _ => parser.expected("alias or file_col_num", next_token),
1348    }?;
1349
1350    if alias.is_some() {
1351        parser.expect_token(&Token::Period)?;
1352        // now we get col_num token
1353        let col_num_token = parser.next_token();
1354        match col_num_token.token {
1355            Token::Placeholder(w) => {
1356                file_col_num = w.to_string().split_off(1).parse::<i32>().map_err(|e| {
1357                    ParserError::ParserError(format!("Could not parse '{w}' as i32: {e}"))
1358                })?;
1359                Ok(())
1360            }
1361            _ => parser.expected("file_col_num", col_num_token),
1362        }?;
1363    }
1364
1365    // try extracting optional element
1366    match parser.next_token().token {
1367        Token::Colon => {
1368            // parse element
1369            element = Some(Ident::new(match parser.next_token().token {
1370                Token::Word(w) => Ok(w.value),
1371                _ => parser.expected("file_col_num", parser.peek_token()),
1372            }?));
1373        }
1374        _ => {
1375            // element not present move back
1376            parser.prev_token();
1377        }
1378    }
1379
1380    // as
1381    if parser.parse_keyword(Keyword::AS) {
1382        item_as = Some(match parser.next_token().token {
1383            Token::Word(w) => Ok(Ident::new(w.value)),
1384            _ => parser.expected("column item alias", parser.peek_token()),
1385        }?);
1386    }
1387
1388    Ok(StageLoadSelectItem {
1389        alias,
1390        file_col_num,
1391        element,
1392        item_as,
1393    })
1394}
1395
1396fn parse_stage_params(parser: &mut Parser) -> Result<StageParamsObject, ParserError> {
1397    let (mut url, mut storage_integration, mut endpoint) = (None, None, None);
1398    let mut encryption: KeyValueOptions = KeyValueOptions {
1399        options: vec![],
1400        delimiter: KeyValueOptionsDelimiter::Space,
1401    };
1402    let mut credentials: KeyValueOptions = KeyValueOptions {
1403        options: vec![],
1404        delimiter: KeyValueOptionsDelimiter::Space,
1405    };
1406
1407    // URL
1408    if parser.parse_keyword(Keyword::URL) {
1409        parser.expect_token(&Token::Eq)?;
1410        url = Some(match parser.next_token().token {
1411            Token::SingleQuotedString(word) => Ok(word),
1412            _ => parser.expected("a URL statement", parser.peek_token()),
1413        }?)
1414    }
1415
1416    // STORAGE INTEGRATION
1417    if parser.parse_keyword(Keyword::STORAGE_INTEGRATION) {
1418        parser.expect_token(&Token::Eq)?;
1419        storage_integration = Some(parser.next_token().token.to_string());
1420    }
1421
1422    // ENDPOINT
1423    if parser.parse_keyword(Keyword::ENDPOINT) {
1424        parser.expect_token(&Token::Eq)?;
1425        endpoint = Some(match parser.next_token().token {
1426            Token::SingleQuotedString(word) => Ok(word),
1427            _ => parser.expected("an endpoint statement", parser.peek_token()),
1428        }?)
1429    }
1430
1431    // CREDENTIALS
1432    if parser.parse_keyword(Keyword::CREDENTIALS) {
1433        parser.expect_token(&Token::Eq)?;
1434        credentials = KeyValueOptions {
1435            options: parser.parse_key_value_options(true, &[])?.options,
1436            delimiter: KeyValueOptionsDelimiter::Space,
1437        };
1438    }
1439
1440    // ENCRYPTION
1441    if parser.parse_keyword(Keyword::ENCRYPTION) {
1442        parser.expect_token(&Token::Eq)?;
1443        encryption = KeyValueOptions {
1444            options: parser.parse_key_value_options(true, &[])?.options,
1445            delimiter: KeyValueOptionsDelimiter::Space,
1446        };
1447    }
1448
1449    Ok(StageParamsObject {
1450        url,
1451        encryption,
1452        endpoint,
1453        storage_integration,
1454        credentials,
1455    })
1456}
1457
1458/// Parses options separated by blank spaces, commas, or new lines like:
1459/// ABORT_DETACHED_QUERY = { TRUE | FALSE }
1460///      [ ACTIVE_PYTHON_PROFILER = { 'LINE' | 'MEMORY' } ]
1461///      [ BINARY_INPUT_FORMAT = '\<string\>' ]
1462fn parse_session_options(
1463    parser: &mut Parser,
1464    set: bool,
1465) -> Result<Vec<KeyValueOption>, ParserError> {
1466    let mut options: Vec<KeyValueOption> = Vec::new();
1467    let empty = String::new;
1468    loop {
1469        let next_token = parser.peek_token();
1470        match next_token.token {
1471            Token::SemiColon | Token::EOF => break,
1472            Token::Comma => {
1473                parser.advance_token();
1474                continue;
1475            }
1476            Token::Word(key) => {
1477                parser.advance_token();
1478                if set {
1479                    let option = parser.parse_key_value_option(&key)?;
1480                    options.push(option);
1481                } else {
1482                    options.push(KeyValueOption {
1483                        option_name: key.value,
1484                        option_value: KeyValueOptionKind::Single(Value::Placeholder(empty())),
1485                    });
1486                }
1487            }
1488            _ => {
1489                return parser.expected("another option or end of statement", next_token);
1490            }
1491        }
1492    }
1493    if options.is_empty() {
1494        Err(ParserError::ParserError(
1495            "expected at least one option".to_string(),
1496        ))
1497    } else {
1498        Ok(options)
1499    }
1500}
1501
1502/// Parsing a property of identity or autoincrement column option
1503/// Syntax:
1504/// ```sql
1505/// [ (seed , increment) | START num INCREMENT num ] [ ORDER | NOORDER ]
1506/// ```
1507/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1508fn parse_identity_property(parser: &mut Parser) -> Result<IdentityProperty, ParserError> {
1509    let parameters = if parser.consume_token(&Token::LParen) {
1510        let seed = parser.parse_number()?;
1511        parser.expect_token(&Token::Comma)?;
1512        let increment = parser.parse_number()?;
1513        parser.expect_token(&Token::RParen)?;
1514
1515        Some(IdentityPropertyFormatKind::FunctionCall(
1516            IdentityParameters { seed, increment },
1517        ))
1518    } else if parser.parse_keyword(Keyword::START) {
1519        let seed = parser.parse_number()?;
1520        parser.expect_keyword_is(Keyword::INCREMENT)?;
1521        let increment = parser.parse_number()?;
1522
1523        Some(IdentityPropertyFormatKind::StartAndIncrement(
1524            IdentityParameters { seed, increment },
1525        ))
1526    } else {
1527        None
1528    };
1529    let order = match parser.parse_one_of_keywords(&[Keyword::ORDER, Keyword::NOORDER]) {
1530        Some(Keyword::ORDER) => Some(IdentityPropertyOrder::Order),
1531        Some(Keyword::NOORDER) => Some(IdentityPropertyOrder::NoOrder),
1532        _ => None,
1533    };
1534    Ok(IdentityProperty { parameters, order })
1535}
1536
1537/// Parsing a policy property of column option
1538/// Syntax:
1539/// ```sql
1540/// <policy_name> [ USING ( <col_name> , <cond_col1> , ... )
1541/// ```
1542/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1543fn parse_column_policy_property(
1544    parser: &mut Parser,
1545    with: bool,
1546) -> Result<ColumnPolicyProperty, ParserError> {
1547    let policy_name = parser.parse_object_name(false)?;
1548    let using_columns = if parser.parse_keyword(Keyword::USING) {
1549        parser.expect_token(&Token::LParen)?;
1550        let columns = parser.parse_comma_separated(|p| p.parse_identifier())?;
1551        parser.expect_token(&Token::RParen)?;
1552        Some(columns)
1553    } else {
1554        None
1555    };
1556
1557    Ok(ColumnPolicyProperty {
1558        with,
1559        policy_name,
1560        using_columns,
1561    })
1562}
1563
1564/// Parsing tags list of column
1565/// Syntax:
1566/// ```sql
1567/// ( <tag_name> = '<tag_value>' [ , <tag_name> = '<tag_value>' , ... ] )
1568/// ```
1569/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1570fn parse_column_tags(parser: &mut Parser, with: bool) -> Result<TagsColumnOption, ParserError> {
1571    parser.expect_token(&Token::LParen)?;
1572    let tags = parser.parse_comma_separated(Parser::parse_tag)?;
1573    parser.expect_token(&Token::RParen)?;
1574
1575    Ok(TagsColumnOption { with, tags })
1576}
1577
1578/// Parse snowflake show objects.
1579/// <https://docs.snowflake.com/en/sql-reference/sql/show-objects>
1580fn parse_show_objects(terse: bool, parser: &mut Parser) -> Result<Statement, ParserError> {
1581    let show_options = parser.parse_show_stmt_options()?;
1582    Ok(Statement::ShowObjects(ShowObjects {
1583        terse,
1584        show_options,
1585    }))
1586}