sqlparser/dialect/
snowflake.rs

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