snowflake.rs - source

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