Skip to main content

datafusion_sql/
parser.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//! [`DFParser`]: DataFusion SQL Parser based on [`sqlparser`]
19//!
20//! This parser implements DataFusion specific statements such as
21//! `CREATE EXTERNAL TABLE`
22
23use datafusion_common::DataFusionError;
24use datafusion_common::config::{ConfigNonZeroUsize, SqlParserOptions};
25use datafusion_common::format::{ExplainFormat, ExplainStatementOptions};
26use datafusion_common::{Diagnostic, Span, sql_err};
27use sqlparser::ast::{ExprWithAlias, Ident, OrderByOptions};
28use sqlparser::tokenizer::TokenWithSpan;
29use sqlparser::{
30    ast::{
31        ColumnDef, ColumnOptionDef, ObjectName, OrderByExpr, Query,
32        Statement as SQLStatement, TableConstraint, Value,
33    },
34    dialect::{Dialect, GenericDialect, keywords::Keyword},
35    parser::{Parser, ParserError},
36    tokenizer::{Token, Tokenizer, Word},
37};
38use std::collections::VecDeque;
39use std::fmt;
40use std::str::FromStr;
41
42// Use `Parser::expected` instead, if possible
43macro_rules! parser_err {
44    ($MSG:expr $(; diagnostic = $DIAG:expr)?) => {{
45
46        let err = DataFusionError::from(ParserError::ParserError($MSG.to_string()));
47        $(
48            let err = err.with_diagnostic($DIAG);
49        )?
50        Err(err)
51    }};
52}
53
54fn parse_file_type(s: &str) -> Result<String, DataFusionError> {
55    Ok(s.to_uppercase())
56}
57
58/// DataFusion specific `EXPLAIN`
59///
60/// Supports both the legacy keyword form and, on dialects whose
61/// [`Dialect::supports_explain_with_utility_options`] returns `true`
62/// (PostgreSQL, DuckDB, etc.), the Postgres-style parenthesized option list:
63///
64/// ```sql
65/// -- Legacy keyword form (any dialect)
66/// EXPLAIN <ANALYZE> <VERBOSE> [FORMAT format] statement
67///
68/// -- Postgres-style option form (dialect-gated)
69/// EXPLAIN (option [arg] [, ...]) statement
70/// ```
71///
72/// See [`ExplainStatementOptions`] for the list of supported options in the
73/// parenthesized form.
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct ExplainStatement {
76    /// Normalized options parsed from either the legacy keyword form or the
77    /// parenthesized option list.
78    pub options: ExplainStatementOptions,
79    /// The statement to analyze. Note this is a DataFusion [`Statement`] (not a
80    /// [`sqlparser::ast::Statement`] so that we can use `EXPLAIN`, `COPY`, and other
81    /// DataFusion specific statements
82    pub statement: Box<Statement>,
83}
84
85impl fmt::Display for ExplainStatement {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        let Self { options, statement } = self;
88
89        // If only the legacy-era fields are set, print the legacy keyword
90        // form so existing round-trip tests continue to pass.
91        let uses_parenthesized = options.analyze_level.is_some()
92            || options.analyze_categories.is_some()
93            || options.show_statistics.is_some();
94
95        write!(f, "EXPLAIN ")?;
96        if uses_parenthesized {
97            // Emit a parenthesized option list.
98            let mut parts: Vec<String> = Vec::new();
99            if options.analyze {
100                parts.push("ANALYZE".to_string());
101            }
102            if options.verbose {
103                parts.push("VERBOSE".to_string());
104            }
105            if let Some(format) = &options.format {
106                parts.push(format!("FORMAT {format}"));
107            }
108            if let Some(level) = options.analyze_level {
109                parts.push(format!("LEVEL {level}"));
110            }
111            if let Some(cats) = &options.analyze_categories {
112                parts.push(format!("METRICS '{cats}'"));
113            }
114            if let Some(stats) = options.show_statistics {
115                parts.push(format!("COSTS {}", if stats { "ON" } else { "OFF" }));
116            }
117            write!(f, "({}) ", parts.join(", "))?;
118        } else {
119            if options.analyze {
120                write!(f, "ANALYZE ")?;
121            }
122            if options.verbose {
123                write!(f, "VERBOSE ")?;
124            }
125            if let Some(format) = &options.format {
126                write!(f, "FORMAT {format} ")?;
127            }
128        }
129
130        write!(f, "{statement}")
131    }
132}
133
134/// DataFusion extension DDL for `COPY`
135///
136/// # Syntax:
137///
138/// ```text
139/// COPY <table_name | (<query>)>
140/// TO
141/// <destination_url>
142/// (key_value_list)
143/// ```
144///
145/// # Examples
146///
147/// ```sql
148/// COPY lineitem  TO 'lineitem'
149/// STORED AS PARQUET (
150///   partitions 16,
151///   row_group_limit_rows 100000,
152///   row_group_limit_bytes 200000
153/// )
154///
155/// COPY (SELECT l_orderkey from lineitem) to 'lineitem.parquet';
156/// ```
157#[derive(Debug, Clone, PartialEq, Eq)]
158pub struct CopyToStatement {
159    /// From where the data comes from
160    pub source: CopyToSource,
161    /// The URL to where the data is heading
162    pub target: String,
163    /// Partition keys
164    pub partitioned_by: Vec<String>,
165    /// File type (Parquet, NDJSON, CSV etc.)
166    pub stored_as: Option<String>,
167    /// Target specific options
168    pub options: Vec<(String, Value)>,
169}
170
171impl fmt::Display for CopyToStatement {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        let Self {
174            source,
175            target,
176            partitioned_by,
177            stored_as,
178            options,
179            ..
180        } = self;
181
182        write!(f, "COPY {source} TO {target}")?;
183        if let Some(file_type) = stored_as {
184            write!(f, " STORED AS {file_type}")?;
185        }
186        if !partitioned_by.is_empty() {
187            write!(f, " PARTITIONED BY ({})", partitioned_by.join(", "))?;
188        }
189
190        if !options.is_empty() {
191            let opts: Vec<_> =
192                options.iter().map(|(k, v)| format!("'{k}' {v}")).collect();
193            write!(f, " OPTIONS ({})", opts.join(", "))?;
194        }
195
196        Ok(())
197    }
198}
199
200#[derive(Debug, Clone, PartialEq, Eq)]
201pub enum CopyToSource {
202    /// `COPY <table> TO ...`
203    Relation(ObjectName),
204    /// COPY (...query...) TO ...
205    Query(Box<Query>),
206}
207
208impl fmt::Display for CopyToSource {
209    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210        match self {
211            CopyToSource::Relation(r) => write!(f, "{r}"),
212            CopyToSource::Query(q) => write!(f, "({q})"),
213        }
214    }
215}
216
217/// This type defines a lexicographical ordering.
218pub(crate) type LexOrdering = Vec<OrderByExpr>;
219
220/// DataFusion extension DDL for `CREATE EXTERNAL TABLE`
221///
222/// Syntax:
223///
224/// ```text
225/// CREATE
226/// [ OR REPLACE ]
227/// EXTERNAL TABLE
228/// [ IF NOT EXISTS ]
229/// <TABLE_NAME>[ (<column_definition>) ]
230/// STORED AS <file_type>
231/// [ PARTITIONED BY (<column_definition list> | <column list>) ]
232/// [ WITH ORDER (<ordered column list>)
233/// [ OPTIONS (<key_value_list>) ]
234/// LOCATION <literal> | LOCATION (<literal>[, ...])
235///
236/// <column_definition> := (<column_name> <data_type>, ...)
237///
238/// <column_list> := (<column_name>, ...)
239///
240/// <ordered_column_list> := (<column_name> <sort_clause>, ...)
241///
242/// <key_value_list> := (<literal> <literal, <literal> <literal>, ...)
243/// ```
244#[derive(Debug, Clone, PartialEq, Eq)]
245pub struct CreateExternalTable {
246    /// Table name
247    pub name: ObjectName,
248    /// Optional schema
249    pub columns: Vec<ColumnDef>,
250    /// File type (Parquet, NDJSON, CSV, etc)
251    pub file_type: String,
252    /// Paths to files
253    pub locations: Vec<String>,
254    /// Partition Columns
255    pub table_partition_cols: Vec<String>,
256    /// Ordered expressions
257    pub order_exprs: Vec<LexOrdering>,
258    /// Option to not error if table already exists
259    pub if_not_exists: bool,
260    /// Option to replace table content if table already exists
261    pub or_replace: bool,
262    /// Whether the table is a temporary table
263    pub temporary: bool,
264    /// Infinite streams?
265    pub unbounded: bool,
266    /// Table(provider) specific options
267    pub options: Vec<(String, Value)>,
268    /// A table-level constraint
269    pub constraints: Vec<TableConstraint>,
270}
271
272impl fmt::Display for CreateExternalTable {
273    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274        write!(f, "CREATE EXTERNAL TABLE ")?;
275        if self.if_not_exists {
276            write!(f, "IF NOT EXISTS ")?;
277        }
278        write!(f, "{} ", self.name)?;
279        write!(f, "STORED AS {} ", self.file_type)?;
280        if !self.order_exprs.is_empty() {
281            write!(f, "WITH ORDER (")?;
282            let mut first = true;
283            for expr in self.order_exprs.iter().flatten() {
284                if !first {
285                    write!(f, ", ")?;
286                }
287                write!(f, "{expr}")?;
288                first = false;
289            }
290            write!(f, ") ")?;
291        }
292        match self.locations.as_slice() {
293            [location] => write!(
294                f,
295                "LOCATION {}",
296                Value::SingleQuotedString(location.clone())
297            ),
298            locations => {
299                write!(f, "LOCATION (")?;
300                for (idx, location) in locations.iter().enumerate() {
301                    if idx > 0 {
302                        write!(f, ", ")?;
303                    }
304                    write!(f, "{}", Value::SingleQuotedString(location.clone()))?;
305                }
306                write!(f, ")")
307            }
308        }
309    }
310}
311
312/// DataFusion extension for `RESET`
313#[derive(Debug, Clone, PartialEq, Eq)]
314pub enum ResetStatement {
315    /// Reset a single configuration variable (stored as provided)
316    Variable(ObjectName),
317}
318
319impl fmt::Display for ResetStatement {
320    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321        match self {
322            ResetStatement::Variable(name) => write!(f, "RESET {name}"),
323        }
324    }
325}
326
327/// DataFusion SQL Statement.
328///
329/// This can either be a [`Statement`] from [`sqlparser`] from a
330/// standard SQL dialect, or a DataFusion extension such as `CREATE
331/// EXTERNAL TABLE`. See [`DFParser`] for more information.
332///
333/// [`Statement`]: sqlparser::ast::Statement
334#[derive(Debug, Clone, PartialEq, Eq)]
335pub enum Statement {
336    /// ANSI SQL AST node (from sqlparser-rs)
337    Statement(Box<SQLStatement>),
338    /// Extension: `CREATE EXTERNAL TABLE`
339    CreateExternalTable(CreateExternalTable),
340    /// Extension: `COPY TO`
341    CopyTo(CopyToStatement),
342    /// EXPLAIN for extensions
343    Explain(ExplainStatement),
344    /// Extension: `RESET`
345    Reset(ResetStatement),
346}
347
348impl fmt::Display for Statement {
349    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350        match self {
351            Statement::Statement(stmt) => write!(f, "{stmt}"),
352            Statement::CreateExternalTable(stmt) => write!(f, "{stmt}"),
353            Statement::CopyTo(stmt) => write!(f, "{stmt}"),
354            Statement::Explain(stmt) => write!(f, "{stmt}"),
355            Statement::Reset(stmt) => write!(f, "{stmt}"),
356        }
357    }
358}
359
360fn ensure_not_set<T>(field: &Option<T>, name: &str) -> Result<(), DataFusionError> {
361    if field.is_some() {
362        parser_err!(format!("{name} specified more than once",))?
363    }
364    Ok(())
365}
366
367/// DataFusion SQL Parser based on [`sqlparser`]
368///
369/// Parses DataFusion's SQL dialect, often delegating to [`sqlparser`]'s [`Parser`].
370///
371/// DataFusion mostly follows existing SQL dialects via
372/// `sqlparser`. However, certain statements such as `COPY` and
373/// `CREATE EXTERNAL TABLE` have special syntax in DataFusion. See
374/// [`Statement`] for a list of this special syntax
375pub struct DFParser<'a> {
376    pub parser: Parser<'a>,
377    options: SqlParserOptions,
378    /// Whether the configured dialect supports Postgres-style
379    /// `EXPLAIN (option, ...)` utility-option syntax. Cached here because
380    /// sqlparser's [`Parser::dialect`] field is private.
381    supports_explain_with_utility_options: bool,
382}
383
384/// Same as `sqlparser`
385const DEFAULT_RECURSION_LIMIT: usize = 50;
386const DEFAULT_DIALECT: GenericDialect = GenericDialect {};
387
388/// Builder for [`DFParser`]
389///
390/// # Example: Create and Parse SQL statements
391/// ```
392/// # use datafusion_sql::parser::DFParserBuilder;
393/// # use datafusion_common::Result;
394/// # fn test() -> Result<()> {
395/// let mut parser = DFParserBuilder::new("SELECT * FROM foo; SELECT 1 + 2").build()?;
396/// // parse the SQL into DFStatements
397/// let statements = parser.parse_statements()?;
398/// assert_eq!(statements.len(), 2);
399/// # Ok(())
400/// # }
401/// ```
402///
403/// # Example: Create and Parse expression with a different dialect
404/// ```
405/// # use datafusion_sql::parser::DFParserBuilder;
406/// # use datafusion_common::Result;
407/// # use datafusion_sql::sqlparser::dialect::MySqlDialect;
408/// # use datafusion_sql::sqlparser::ast::Expr;
409/// # fn test() -> Result<()> {
410/// let dialect = MySqlDialect {}; // Parse using MySQL dialect
411/// let mut parser = DFParserBuilder::new("1 + 2")
412///     .with_dialect(&dialect)
413///     .build()?;
414/// // parse 1+2 into an sqlparser::ast::Expr
415/// let res = parser.parse_expr()?;
416/// assert!(matches!(res.expr, Expr::BinaryOp { .. }));
417/// # Ok(())
418/// # }
419/// ```
420pub struct DFParserBuilder<'a, 'b> {
421    /// Parser input: either raw SQL or tokens
422    input: ParserInput<'a>,
423    /// The Dialect to use (defaults to [`GenericDialect`]
424    dialect: &'b dyn Dialect,
425    /// The recursion limit while parsing
426    recursion_limit: usize,
427}
428
429/// Describes a possible input for parser
430pub enum ParserInput<'a> {
431    /// Raw SQL. Tokenization will be performed automatically as a
432    /// part of [`DFParserBuilder::build`]
433    Sql(&'a str),
434    /// Tokens
435    Tokens(Vec<TokenWithSpan>),
436}
437
438impl<'a> From<&'a str> for ParserInput<'a> {
439    fn from(sql: &'a str) -> Self {
440        Self::Sql(sql)
441    }
442}
443
444impl From<Vec<TokenWithSpan>> for ParserInput<'static> {
445    fn from(tokens: Vec<TokenWithSpan>) -> Self {
446        Self::Tokens(tokens)
447    }
448}
449
450impl<'a, 'b> DFParserBuilder<'a, 'b> {
451    /// Create a new parser builder for the specified tokens using the
452    /// [`GenericDialect`].
453    pub fn new(input: impl Into<ParserInput<'a>>) -> Self {
454        Self {
455            input: input.into(),
456            dialect: &DEFAULT_DIALECT,
457            recursion_limit: DEFAULT_RECURSION_LIMIT,
458        }
459    }
460
461    /// Adjust the parser builder's dialect. Defaults to [`GenericDialect`]
462    pub fn with_dialect(mut self, dialect: &'b dyn Dialect) -> Self {
463        self.dialect = dialect;
464        self
465    }
466
467    /// Adjust the recursion limit of sql parsing.  Defaults to 50
468    pub fn with_recursion_limit(mut self, recursion_limit: usize) -> Self {
469        self.recursion_limit = recursion_limit;
470        self
471    }
472
473    /// Build resulting parser
474    pub fn build(self) -> Result<DFParser<'b>, DataFusionError> {
475        let tokens = match self.input {
476            ParserInput::Tokens(tokens) => tokens,
477            ParserInput::Sql(sql) => {
478                let mut tokenizer = Tokenizer::new(self.dialect, sql);
479                // Convert TokenizerError -> ParserError
480                tokenizer
481                    .tokenize_with_location()
482                    .map_err(ParserError::from)?
483            }
484        };
485
486        Ok(DFParser {
487            parser: Parser::new(self.dialect)
488                .with_tokens_with_locations(tokens)
489                .with_recursion_limit(self.recursion_limit),
490            options: SqlParserOptions {
491                recursion_limit: ConfigNonZeroUsize::try_new(self.recursion_limit)?,
492                ..Default::default()
493            },
494            supports_explain_with_utility_options: self
495                .dialect
496                .supports_explain_with_utility_options(),
497        })
498    }
499}
500
501/// Returns true when `tok` is the start of a query / parenthesized query
502/// group. Used to disambiguate `EXPLAIN (SELECT ...)` (a parenthesized query)
503/// from `EXPLAIN (ANALYZE) SELECT ...` (a Postgres-style option list).
504fn token_starts_query(tok: &Token) -> bool {
505    match tok {
506        Token::LParen => true,
507        Token::Word(Word { keyword, .. }) => matches!(
508            keyword,
509            Keyword::SELECT
510                | Keyword::WITH
511                | Keyword::VALUES
512                | Keyword::TABLE
513                | Keyword::INSERT
514                | Keyword::UPDATE
515                | Keyword::DELETE
516                | Keyword::MERGE
517        ),
518        _ => false,
519    }
520}
521
522impl<'a> DFParser<'a> {
523    /// Parse a sql string into one or [`Statement`]s using the
524    /// [`GenericDialect`].
525    pub fn parse_sql(sql: &'a str) -> Result<VecDeque<Statement>, DataFusionError> {
526        let mut parser = DFParserBuilder::new(sql).build()?;
527
528        parser.parse_statements()
529    }
530
531    /// Parse a SQL string and produce one or more [`Statement`]s with
532    /// with the specified dialect.
533    pub fn parse_sql_with_dialect(
534        sql: &str,
535        dialect: &dyn Dialect,
536    ) -> Result<VecDeque<Statement>, DataFusionError> {
537        let mut parser = DFParserBuilder::new(sql).with_dialect(dialect).build()?;
538        parser.parse_statements()
539    }
540
541    pub fn parse_sql_into_expr(sql: &str) -> Result<ExprWithAlias, DataFusionError> {
542        DFParserBuilder::new(sql).build()?.parse_into_expr()
543    }
544
545    pub fn parse_sql_into_expr_with_dialect(
546        sql: &str,
547        dialect: &dyn Dialect,
548    ) -> Result<ExprWithAlias, DataFusionError> {
549        DFParserBuilder::new(sql)
550            .with_dialect(dialect)
551            .build()?
552            .parse_into_expr()
553    }
554
555    /// Parse a sql string into one or [`Statement`]s
556    pub fn parse_statements(&mut self) -> Result<VecDeque<Statement>, DataFusionError> {
557        let mut stmts = VecDeque::new();
558        let mut expecting_statement_delimiter = false;
559        loop {
560            // ignore empty statements (between successive statement delimiters)
561            while self.parser.consume_token(&Token::SemiColon) {
562                expecting_statement_delimiter = false;
563            }
564
565            if self.parser.peek_token() == Token::EOF {
566                break;
567            }
568            if expecting_statement_delimiter {
569                return self.expected("end of statement", &self.parser.peek_token());
570            }
571
572            let statement = self.parse_statement()?;
573            stmts.push_back(statement);
574            expecting_statement_delimiter = true;
575        }
576        Ok(stmts)
577    }
578
579    /// Report an unexpected token
580    fn expected<T>(
581        &self,
582        expected: &str,
583        found: &TokenWithSpan,
584    ) -> Result<T, DataFusionError> {
585        let sql_parser_span = found.span;
586        let span = Span::try_from_sqlparser_span(sql_parser_span);
587        let diagnostic = Diagnostic::new_error(
588            format!("Expected: {expected}, found: {found}{}", found.span.start),
589            span,
590        );
591        parser_err!(
592            format!("Expected: {expected}, found: {found}{}", found.span.start);
593            diagnostic=
594            diagnostic
595        )
596    }
597
598    fn expect_token(
599        &mut self,
600        expected: &str,
601        token: &Token,
602    ) -> Result<(), DataFusionError> {
603        let next_token = self.parser.peek_token_ref();
604        if next_token.token != *token {
605            self.expected(expected, next_token)
606        } else {
607            Ok(())
608        }
609    }
610
611    /// Parse a new expression
612    pub fn parse_statement(&mut self) -> Result<Statement, DataFusionError> {
613        match self.parser.peek_token().token {
614            Token::Word(w) => {
615                match w.keyword {
616                    Keyword::CREATE => {
617                        self.parser.next_token(); // CREATE
618                        self.parse_create()
619                    }
620                    Keyword::COPY => {
621                        if let Token::Word(w) = self.parser.peek_nth_token(1).token {
622                            // use native parser for COPY INTO
623                            if w.keyword == Keyword::INTO {
624                                return self.parse_and_handle_statement();
625                            }
626                        }
627                        self.parser.next_token(); // COPY
628                        self.parse_copy()
629                    }
630                    Keyword::EXPLAIN => {
631                        self.parser.next_token(); // EXPLAIN
632                        self.parse_explain()
633                    }
634                    Keyword::RESET => {
635                        self.parser.next_token(); // RESET
636                        self.parse_reset()
637                    }
638                    _ => {
639                        // use sqlparser-rs parser
640                        self.parse_and_handle_statement()
641                    }
642                }
643            }
644            _ => {
645                // use the native parser
646                self.parse_and_handle_statement()
647            }
648        }
649    }
650
651    pub fn parse_expr(&mut self) -> Result<ExprWithAlias, DataFusionError> {
652        if let Token::Word(w) = self.parser.peek_token().token {
653            match w.keyword {
654                Keyword::CREATE | Keyword::COPY | Keyword::EXPLAIN => {
655                    return parser_err!("Unsupported command in expression")?;
656                }
657                _ => {}
658            }
659        }
660
661        Ok(self.parser.parse_expr_with_alias()?)
662    }
663
664    /// Parses the entire SQL string into an expression.
665    ///
666    /// In contrast to [`DFParser::parse_expr`], this function will report an error if the input
667    /// contains any trailing, unparsed tokens.
668    pub fn parse_into_expr(&mut self) -> Result<ExprWithAlias, DataFusionError> {
669        let expr = self.parse_expr()?;
670        self.expect_token("end of expression", &Token::EOF)?;
671        Ok(expr)
672    }
673
674    /// Helper method to parse a statement and handle errors consistently, especially for recursion limits
675    fn parse_and_handle_statement(&mut self) -> Result<Statement, DataFusionError> {
676        self.parser
677            .parse_statement()
678            .map(|stmt| Statement::Statement(Box::from(stmt)))
679            .map_err(|e| match e {
680                ParserError::RecursionLimitExceeded => DataFusionError::SQL(
681                    Box::new(ParserError::RecursionLimitExceeded),
682                    Some(format!(
683                        " (current limit: {})",
684                        self.options.recursion_limit
685                    )),
686                ),
687                other => DataFusionError::SQL(Box::new(other), None),
688            })
689    }
690
691    /// Parse a SQL `COPY TO` statement
692    pub fn parse_copy(&mut self) -> Result<Statement, DataFusionError> {
693        // parse as a query
694        let source = if self.parser.consume_token(&Token::LParen) {
695            let query = self.parser.parse_query()?;
696            self.parser.expect_token(&Token::RParen)?;
697            CopyToSource::Query(query)
698        } else {
699            // parse as table reference
700            let table_name = self.parser.parse_object_name(true)?;
701            CopyToSource::Relation(table_name)
702        };
703
704        #[derive(Default)]
705        struct Builder {
706            stored_as: Option<String>,
707            target: Option<String>,
708            partitioned_by: Option<Vec<String>>,
709            options: Option<Vec<(String, Value)>>,
710        }
711
712        let mut builder = Builder::default();
713
714        loop {
715            if let Some(keyword) = self.parser.parse_one_of_keywords(&[
716                Keyword::STORED,
717                Keyword::TO,
718                Keyword::PARTITIONED,
719                Keyword::OPTIONS,
720                Keyword::WITH,
721            ]) {
722                match keyword {
723                    Keyword::STORED => {
724                        self.parser.expect_keyword(Keyword::AS)?;
725                        ensure_not_set(&builder.stored_as, "STORED AS")?;
726                        builder.stored_as = Some(self.parse_file_format()?);
727                    }
728                    Keyword::TO => {
729                        ensure_not_set(&builder.target, "TO")?;
730                        builder.target = Some(self.parser.parse_literal_string()?);
731                    }
732                    Keyword::WITH => {
733                        self.parser.expect_keyword(Keyword::HEADER)?;
734                        self.parser.expect_keyword(Keyword::ROW)?;
735                        return parser_err!(
736                            "WITH HEADER ROW clause is no longer in use. Please use the OPTIONS clause with 'format.has_header' set appropriately, e.g., OPTIONS ('format.has_header' 'true')"
737                        )?;
738                    }
739                    Keyword::PARTITIONED => {
740                        self.parser.expect_keyword(Keyword::BY)?;
741                        ensure_not_set(&builder.partitioned_by, "PARTITIONED BY")?;
742                        builder.partitioned_by = Some(self.parse_partitions()?);
743                    }
744                    Keyword::OPTIONS => {
745                        ensure_not_set(&builder.options, "OPTIONS")?;
746                        builder.options = Some(self.parse_value_options()?);
747                    }
748                    _ => {
749                        unreachable!()
750                    }
751                }
752            } else {
753                let token = self.parser.peek_token();
754                if token == Token::EOF || token == Token::SemiColon {
755                    break;
756                } else {
757                    return self.expected("end of statement or ;", &token)?;
758                }
759            }
760        }
761
762        let Some(target) = builder.target else {
763            return parser_err!("Missing TO clause in COPY statement")?;
764        };
765
766        Ok(Statement::CopyTo(CopyToStatement {
767            source,
768            target,
769            partitioned_by: builder.partitioned_by.unwrap_or(vec![]),
770            stored_as: builder.stored_as,
771            options: builder.options.unwrap_or(vec![]),
772        }))
773    }
774
775    /// Parse the next token as a key name for an option list
776    ///
777    /// Note this is different than [`parse_literal_string`]
778    /// because it allows keywords as well as other non words
779    ///
780    /// [`parse_literal_string`]: sqlparser::parser::Parser::parse_literal_string
781    pub fn parse_option_key(&mut self) -> Result<String, DataFusionError> {
782        let next_token = self.parser.next_token();
783        match next_token.token {
784            Token::Word(Word { value, .. }) => {
785                let mut parts = vec![value];
786                while self.parser.consume_token(&Token::Period) {
787                    let next_token = self.parser.next_token();
788                    if let Token::Word(Word { value, .. }) = next_token.token {
789                        parts.push(value);
790                    } else {
791                        // Unquoted namespaced keys have to conform to the syntax
792                        // "<WORD>[\.<WORD>]*". If we have a key that breaks this
793                        // pattern, error out:
794                        return self.expected("key name", &next_token);
795                    }
796                }
797                Ok(parts.join("."))
798            }
799            Token::SingleQuotedString(s) => Ok(s),
800            Token::DoubleQuotedString(s) => Ok(s),
801            Token::EscapedStringLiteral(s) => Ok(s),
802            _ => self.expected("key name", &next_token),
803        }
804    }
805
806    /// Parse the next token as a value for an option list
807    ///
808    /// Note this is different than [`parse_value`] as it allows any
809    /// word or keyword in this location.
810    ///
811    /// [`parse_value`]: sqlparser::parser::Parser::parse_value
812    pub fn parse_option_value(&mut self) -> Result<Value, DataFusionError> {
813        let next_token = self.parser.next_token();
814        match next_token.token {
815            // e.g. things like "snappy" or "gzip" that may be keywords
816            Token::Word(word) => Ok(Value::SingleQuotedString(word.value)),
817            Token::SingleQuotedString(s) => Ok(Value::SingleQuotedString(s)),
818            Token::DoubleQuotedString(s) => Ok(Value::DoubleQuotedString(s)),
819            Token::EscapedStringLiteral(s) => Ok(Value::EscapedStringLiteral(s)),
820            Token::Number(n, l) => Ok(Value::Number(n, l)),
821            _ => self.expected("string or numeric value", &next_token),
822        }
823    }
824
825    /// Parse a SQL `EXPLAIN`
826    ///
827    /// After the `EXPLAIN` keyword, if the dialect supports the Postgres-style
828    /// option list and the next non-whitespace token is `(`, we must
829    /// disambiguate between an option list (`EXPLAIN (ANALYZE) SELECT ...`)
830    /// and a parenthesized query (`EXPLAIN (SELECT ...)` or
831    /// `EXPLAIN (q1 EXCEPT q2) UNION ALL ...`).
832    pub fn parse_explain(&mut self) -> Result<Statement, DataFusionError> {
833        if self.supports_explain_with_utility_options
834            && self.parser.peek_token().token == Token::LParen
835            && !token_starts_query(&self.parser.peek_nth_token(1).token)
836        {
837            let raw = self.parser.parse_utility_options()?;
838            let options = ExplainStatementOptions::from_utility_options(&raw)?;
839            let statement = self.parse_statement()?;
840            return Ok(Statement::Explain(ExplainStatement {
841                statement: Box::new(statement),
842                options,
843            }));
844        }
845
846        // Legacy keyword form.
847        let analyze = self.parser.parse_keyword(Keyword::ANALYZE);
848        let verbose = self.parser.parse_keyword(Keyword::VERBOSE);
849        let format = self
850            .parse_explain_format()?
851            .map(|s| ExplainFormat::from_str(&s))
852            .transpose()?;
853
854        let statement = self.parse_statement()?;
855
856        let options = ExplainStatementOptions {
857            analyze,
858            verbose,
859            format,
860            ..Default::default()
861        };
862
863        Ok(Statement::Explain(ExplainStatement {
864            statement: Box::new(statement),
865            options,
866        }))
867    }
868
869    /// Parse a SQL `RESET`
870    pub fn parse_reset(&mut self) -> Result<Statement, DataFusionError> {
871        let mut parts: Vec<String> = Vec::new();
872        let mut expecting_segment = true;
873
874        loop {
875            let next_token = self.parser.peek_token();
876            match &next_token.token {
877                Token::Word(word) => {
878                    self.parser.next_token();
879                    parts.push(word.value.clone());
880                    expecting_segment = false;
881                }
882                Token::SingleQuotedString(s)
883                | Token::DoubleQuotedString(s)
884                | Token::EscapedStringLiteral(s) => {
885                    self.parser.next_token();
886                    parts.push(s.clone());
887                    expecting_segment = false;
888                }
889                Token::Period => {
890                    self.parser.next_token();
891                    if expecting_segment || parts.is_empty() {
892                        return self.expected("configuration parameter", &next_token);
893                    }
894                    expecting_segment = true;
895                }
896                Token::EOF | Token::SemiColon => break,
897                _ => return self.expected("configuration parameter", &next_token),
898            }
899        }
900
901        if parts.is_empty() || expecting_segment {
902            return self.expected("configuration parameter", &self.parser.peek_token());
903        }
904
905        let idents: Vec<Ident> = parts.into_iter().map(Ident::new).collect();
906        let variable = ObjectName::from(idents);
907        Ok(Statement::Reset(ResetStatement::Variable(variable)))
908    }
909
910    pub fn parse_explain_format(&mut self) -> Result<Option<String>, DataFusionError> {
911        if !self.parser.parse_keyword(Keyword::FORMAT) {
912            return Ok(None);
913        }
914
915        let next_token = self.parser.next_token();
916        let format = match next_token.token {
917            Token::Word(w) => Ok(w.value),
918            Token::SingleQuotedString(w) => Ok(w),
919            Token::DoubleQuotedString(w) => Ok(w),
920            _ => self.expected("an explain format such as TREE", &next_token),
921        }?;
922        Ok(Some(format))
923    }
924
925    /// Parse a SQL `CREATE` statement handling `CREATE EXTERNAL TABLE`
926    pub fn parse_create(&mut self) -> Result<Statement, DataFusionError> {
927        // TODO: Change sql parser to take in `or_replace: bool` inside parse_create()
928        if self
929            .parser
930            .parse_keywords(&[Keyword::OR, Keyword::REPLACE, Keyword::EXTERNAL])
931        {
932            self.parse_create_external_table(false, true)
933        } else if self.parser.parse_keywords(&[
934            Keyword::OR,
935            Keyword::REPLACE,
936            Keyword::UNBOUNDED,
937            Keyword::EXTERNAL,
938        ]) {
939            self.parse_create_external_table(true, true)
940        } else if self.parser.parse_keyword(Keyword::EXTERNAL) {
941            self.parse_create_external_table(false, false)
942        } else if self
943            .parser
944            .parse_keywords(&[Keyword::UNBOUNDED, Keyword::EXTERNAL])
945        {
946            self.parse_create_external_table(true, false)
947        } else {
948            Ok(Statement::Statement(Box::from(self.parser.parse_create()?)))
949        }
950    }
951
952    fn parse_partitions(&mut self) -> Result<Vec<String>, DataFusionError> {
953        let mut partitions: Vec<String> = vec![];
954        if !self.parser.consume_token(&Token::LParen)
955            || self.parser.consume_token(&Token::RParen)
956        {
957            return Ok(partitions);
958        }
959
960        loop {
961            if let Token::Word(_) = self.parser.peek_token().token {
962                let identifier = self.parser.parse_identifier()?;
963                partitions.push(identifier.to_string());
964            } else {
965                return self.expected("partition name", &self.parser.peek_token());
966            }
967            let comma = self.parser.consume_token(&Token::Comma);
968            if self.parser.consume_token(&Token::RParen) {
969                // allow a trailing comma, even though it's not in standard
970                break;
971            } else if !comma {
972                return self.expected(
973                    "',' or ')' after partition definition",
974                    &self.parser.peek_token(),
975                );
976            }
977        }
978        Ok(partitions)
979    }
980
981    /// Parse the ordering clause of a `CREATE EXTERNAL TABLE` SQL statement
982    pub fn parse_order_by_exprs(&mut self) -> Result<Vec<OrderByExpr>, DataFusionError> {
983        let mut values = vec![];
984        self.parser.expect_token(&Token::LParen)?;
985        loop {
986            values.push(self.parse_order_by_expr()?);
987            if !self.parser.consume_token(&Token::Comma) {
988                self.parser.expect_token(&Token::RParen)?;
989                return Ok(values);
990            }
991        }
992    }
993
994    /// Parse an ORDER BY sub-expression optionally followed by ASC or DESC.
995    pub fn parse_order_by_expr(&mut self) -> Result<OrderByExpr, DataFusionError> {
996        let expr = self.parser.parse_expr()?;
997
998        let asc = if self.parser.parse_keyword(Keyword::ASC) {
999            Some(true)
1000        } else if self.parser.parse_keyword(Keyword::DESC) {
1001            Some(false)
1002        } else {
1003            None
1004        };
1005
1006        let nulls_first = if self
1007            .parser
1008            .parse_keywords(&[Keyword::NULLS, Keyword::FIRST])
1009        {
1010            Some(true)
1011        } else if self.parser.parse_keywords(&[Keyword::NULLS, Keyword::LAST]) {
1012            Some(false)
1013        } else {
1014            None
1015        };
1016
1017        Ok(OrderByExpr {
1018            expr,
1019            options: OrderByOptions { asc, nulls_first },
1020            with_fill: None,
1021        })
1022    }
1023
1024    // This is a copy of the equivalent implementation in sqlparser.
1025    fn parse_columns(
1026        &mut self,
1027    ) -> Result<(Vec<ColumnDef>, Vec<TableConstraint>), DataFusionError> {
1028        let mut columns = vec![];
1029        let mut constraints = vec![];
1030        if !self.parser.consume_token(&Token::LParen)
1031            || self.parser.consume_token(&Token::RParen)
1032        {
1033            return Ok((columns, constraints));
1034        }
1035
1036        loop {
1037            if let Some(constraint) = self.parser.parse_optional_table_constraint()? {
1038                constraints.push(constraint);
1039            } else if let Token::Word(_) = self.parser.peek_token().token {
1040                let column_def = self.parse_column_def()?;
1041                columns.push(column_def);
1042            } else {
1043                return self.expected(
1044                    "column name or constraint definition",
1045                    &self.parser.peek_token(),
1046                );
1047            }
1048            let comma = self.parser.consume_token(&Token::Comma);
1049            if self.parser.consume_token(&Token::RParen) {
1050                // allow a trailing comma, even though it's not in standard
1051                break;
1052            } else if !comma {
1053                return self.expected(
1054                    "',' or ')' after column definition",
1055                    &self.parser.peek_token(),
1056                );
1057            }
1058        }
1059
1060        Ok((columns, constraints))
1061    }
1062
1063    fn parse_column_def(&mut self) -> Result<ColumnDef, DataFusionError> {
1064        let name = self.parser.parse_identifier()?;
1065        let data_type = self.parser.parse_data_type()?;
1066        let mut options = vec![];
1067        loop {
1068            if self.parser.parse_keyword(Keyword::CONSTRAINT) {
1069                let name = Some(self.parser.parse_identifier()?);
1070                if let Some(option) = self.parser.parse_optional_column_option()? {
1071                    options.push(ColumnOptionDef { name, option });
1072                } else {
1073                    return self.expected(
1074                        "constraint details after CONSTRAINT <name>",
1075                        &self.parser.peek_token(),
1076                    );
1077                }
1078            } else if let Some(option) = self.parser.parse_optional_column_option()? {
1079                options.push(ColumnOptionDef { name: None, option });
1080            } else {
1081                break;
1082            };
1083        }
1084        Ok(ColumnDef {
1085            name,
1086            data_type,
1087            options,
1088        })
1089    }
1090
1091    fn parse_create_external_table(
1092        &mut self,
1093        unbounded: bool,
1094        or_replace: bool,
1095    ) -> Result<Statement, DataFusionError> {
1096        let temporary = self
1097            .parser
1098            .parse_one_of_keywords(&[Keyword::TEMP, Keyword::TEMPORARY])
1099            .is_some();
1100
1101        self.parser.expect_keyword(Keyword::TABLE)?;
1102        let if_not_exists =
1103            self.parser
1104                .parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
1105
1106        if if_not_exists && or_replace {
1107            return parser_err!("'IF NOT EXISTS' cannot coexist with 'REPLACE'");
1108        }
1109
1110        let table_name = self.parser.parse_object_name(true)?;
1111        let (mut columns, constraints) = self.parse_columns()?;
1112
1113        #[derive(Default)]
1114        struct Builder {
1115            file_type: Option<String>,
1116            locations: Option<Vec<String>>,
1117            table_partition_cols: Option<Vec<String>>,
1118            order_exprs: Vec<LexOrdering>,
1119            options: Option<Vec<(String, Value)>>,
1120        }
1121        let mut builder = Builder::default();
1122
1123        loop {
1124            if let Some(keyword) = self.parser.parse_one_of_keywords(&[
1125                Keyword::STORED,
1126                Keyword::LOCATION,
1127                Keyword::WITH,
1128                Keyword::DELIMITER,
1129                Keyword::COMPRESSION,
1130                Keyword::PARTITIONED,
1131                Keyword::OPTIONS,
1132            ]) {
1133                match keyword {
1134                    Keyword::STORED => {
1135                        self.parser.expect_keyword(Keyword::AS)?;
1136                        ensure_not_set(&builder.file_type, "STORED AS")?;
1137                        builder.file_type = Some(self.parse_file_format()?);
1138                    }
1139                    Keyword::LOCATION => {
1140                        ensure_not_set(&builder.locations, "LOCATION")?;
1141                        builder.locations = Some(self.parse_locations()?);
1142                    }
1143                    Keyword::WITH => {
1144                        if self.parser.parse_keyword(Keyword::ORDER) {
1145                            builder.order_exprs.push(self.parse_order_by_exprs()?);
1146                        } else {
1147                            self.parser.expect_keyword(Keyword::HEADER)?;
1148                            self.parser.expect_keyword(Keyword::ROW)?;
1149                            return parser_err!(
1150                                "WITH HEADER ROW clause is no longer in use. Please use the OPTIONS clause with 'format.has_header' set appropriately, e.g., OPTIONS (format.has_header true)"
1151                            )?;
1152                        }
1153                    }
1154                    Keyword::DELIMITER => {
1155                        return parser_err!(
1156                            "DELIMITER clause is no longer in use. Please use the OPTIONS clause with 'format.delimiter' set appropriately, e.g., OPTIONS (format.delimiter ',')"
1157                        )?;
1158                    }
1159                    Keyword::COMPRESSION => {
1160                        self.parser.expect_keyword(Keyword::TYPE)?;
1161                        return parser_err!(
1162                            "COMPRESSION TYPE clause is no longer in use. Please use the OPTIONS clause with 'format.compression' set appropriately, e.g., OPTIONS (format.compression gzip)"
1163                        )?;
1164                    }
1165                    Keyword::PARTITIONED => {
1166                        self.parser.expect_keyword(Keyword::BY)?;
1167                        ensure_not_set(&builder.table_partition_cols, "PARTITIONED BY")?;
1168                        // Expects either list of column names (col_name [, col_name]*)
1169                        // or list of column definitions (col_name datatype [, col_name datatype]* )
1170                        // use the token after the name to decide which parsing rule to use
1171                        // Note that mixing both names and definitions is not allowed
1172                        let peeked = self.parser.peek_nth_token(2);
1173                        if peeked == Token::Comma || peeked == Token::RParen {
1174                            // List of column names
1175                            builder.table_partition_cols = Some(self.parse_partitions()?)
1176                        } else {
1177                            // List of column defs
1178                            let (cols, cons) = self.parse_columns()?;
1179                            builder.table_partition_cols = Some(
1180                                cols.iter().map(|col| col.name.to_string()).collect(),
1181                            );
1182
1183                            columns.extend(cols);
1184
1185                            if !cons.is_empty() {
1186                                return sql_err!(ParserError::ParserError(
1187                                    "Constraints on Partition Columns are not supported"
1188                                        .to_string(),
1189                                ));
1190                            }
1191                        }
1192                    }
1193                    Keyword::OPTIONS => {
1194                        ensure_not_set(&builder.options, "OPTIONS")?;
1195                        builder.options = Some(self.parse_value_options()?);
1196                    }
1197                    _ => {
1198                        unreachable!()
1199                    }
1200                }
1201            } else {
1202                let token = self.parser.peek_token();
1203                if token == Token::EOF || token == Token::SemiColon {
1204                    break;
1205                } else {
1206                    return self.expected("end of statement or ;", &token)?;
1207                }
1208            }
1209        }
1210
1211        // Validations: location and file_type are required
1212        if builder.file_type.is_none() {
1213            return sql_err!(ParserError::ParserError(
1214                "Missing STORED AS clause in CREATE EXTERNAL TABLE statement".into(),
1215            ));
1216        }
1217        if builder.locations.is_none() {
1218            return sql_err!(ParserError::ParserError(
1219                "Missing LOCATION clause in CREATE EXTERNAL TABLE statement".into(),
1220            ));
1221        }
1222
1223        let locations = builder.locations.unwrap();
1224        if locations.is_empty() {
1225            return parser_err!("LOCATION requires at least one path");
1226        }
1227
1228        let create = CreateExternalTable {
1229            name: table_name,
1230            columns,
1231            file_type: builder.file_type.unwrap(),
1232            locations,
1233            table_partition_cols: builder.table_partition_cols.unwrap_or(vec![]),
1234            order_exprs: builder.order_exprs,
1235            if_not_exists,
1236            or_replace,
1237            temporary,
1238            unbounded,
1239            options: builder.options.unwrap_or(Vec::new()),
1240            constraints,
1241        };
1242        Ok(Statement::CreateExternalTable(create))
1243    }
1244
1245    /// Parses one or more external table locations.
1246    fn parse_locations(&mut self) -> Result<Vec<String>, DataFusionError> {
1247        if !self.parser.consume_token(&Token::LParen) {
1248            return Ok(vec![self.parser.parse_literal_string()?]);
1249        }
1250
1251        let mut locations = vec![];
1252        loop {
1253            locations.push(self.parser.parse_literal_string()?);
1254            let comma = self.parser.consume_token(&Token::Comma);
1255            if self.parser.consume_token(&Token::RParen) {
1256                // Allow a trailing comma, even though it's not in standard
1257                break;
1258            } else if !comma {
1259                return self.expected(
1260                    "',' or ')' after location definition",
1261                    &self.parser.peek_token(),
1262                );
1263            }
1264        }
1265        Ok(locations)
1266    }
1267
1268    /// Parses the set of valid formats
1269    fn parse_file_format(&mut self) -> Result<String, DataFusionError> {
1270        let token = self.parser.next_token();
1271        match &token.token {
1272            Token::Word(w) => parse_file_type(&w.value),
1273            _ => self.expected("one of ARROW, PARQUET, NDJSON, or CSV", &token),
1274        }
1275    }
1276
1277    /// Parses (key value) style options into a map of String --> [`Value`].
1278    ///
1279    /// This method supports keywords as key names as well as multiple
1280    /// value types such as Numbers as well as Strings.
1281    fn parse_value_options(&mut self) -> Result<Vec<(String, Value)>, DataFusionError> {
1282        let mut options = vec![];
1283        self.parser.expect_token(&Token::LParen)?;
1284
1285        loop {
1286            let key = self.parse_option_key()?;
1287            let value = self.parse_option_value()?;
1288            options.push((key, value));
1289            let comma = self.parser.consume_token(&Token::Comma);
1290            if self.parser.consume_token(&Token::RParen) {
1291                // Allow a trailing comma, even though it's not in standard
1292                break;
1293            } else if !comma {
1294                return self.expected(
1295                    "',' or ')' after option definition",
1296                    &self.parser.peek_token(),
1297                );
1298            }
1299        }
1300        Ok(options)
1301    }
1302}
1303
1304#[cfg(test)]
1305mod tests {
1306    use super::*;
1307    use datafusion_common::assert_contains;
1308    use sqlparser::ast::Expr::Identifier;
1309    use sqlparser::ast::{
1310        BinaryOperator, DataType, ExactNumberInfo, Expr, Ident, ValueWithSpan,
1311    };
1312    use sqlparser::dialect::SnowflakeDialect;
1313    use sqlparser::tokenizer::{Location, Span, Whitespace};
1314
1315    fn expect_parse_ok(sql: &str, expected: Statement) -> Result<(), DataFusionError> {
1316        let statements = DFParser::parse_sql(sql)?;
1317        assert_eq!(
1318            statements.len(),
1319            1,
1320            "Expected to parse exactly one statement"
1321        );
1322        assert_eq!(statements[0], expected, "actual:\n{:#?}", statements[0]);
1323        Ok(())
1324    }
1325
1326    /// Parses sql and asserts that the expected error message was found
1327    fn expect_parse_error(sql: &str, expected_error: &str) {
1328        match DFParser::parse_sql(sql) {
1329            Ok(statements) => {
1330                panic!(
1331                    "Expected parse error for '{sql}', but was successful: {statements:?}"
1332                );
1333            }
1334            Err(e) => {
1335                let error_message = e.to_string();
1336                assert!(
1337                    error_message.contains(expected_error),
1338                    "Expected error '{expected_error}' not found in actual error '{error_message}'"
1339                );
1340            }
1341        }
1342    }
1343
1344    fn make_column_def(name: impl Into<String>, data_type: DataType) -> ColumnDef {
1345        ColumnDef {
1346            name: Ident {
1347                value: name.into(),
1348                quote_style: None,
1349                span: Span::empty(),
1350            },
1351            data_type,
1352            options: vec![],
1353        }
1354    }
1355
1356    fn make_create_external_table(location: &str) -> CreateExternalTable {
1357        make_create_external_table_with_locations(&[location])
1358    }
1359
1360    fn make_create_external_table_with_locations(
1361        locations: &[&str],
1362    ) -> CreateExternalTable {
1363        let locations = locations
1364            .iter()
1365            .map(|location| location.to_string())
1366            .collect::<Vec<_>>();
1367
1368        CreateExternalTable {
1369            name: ObjectName::from(vec![Ident::from("t")]),
1370            columns: vec![],
1371            file_type: "CSV".to_string(),
1372            locations,
1373            table_partition_cols: vec![],
1374            order_exprs: vec![],
1375            if_not_exists: false,
1376            or_replace: false,
1377            temporary: false,
1378            unbounded: false,
1379            options: vec![],
1380            constraints: vec![],
1381        }
1382    }
1383
1384    #[test]
1385    fn create_external_table() -> Result<(), DataFusionError> {
1386        // positive case
1387        let sql = "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV LOCATION 'foo.csv'";
1388        let display = None;
1389        let expected = Statement::CreateExternalTable(CreateExternalTable {
1390            columns: vec![make_column_def("c1", DataType::Int(display))],
1391            ..make_create_external_table("foo.csv")
1392        });
1393        expect_parse_ok(sql, expected)?;
1394
1395        // positive case: literal comma remains part of a single path
1396        let sql = "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV LOCATION 'foo,bar.csv'";
1397        let expected = Statement::CreateExternalTable(CreateExternalTable {
1398            columns: vec![make_column_def("c1", DataType::Int(display))],
1399            ..make_create_external_table("foo,bar.csv")
1400        });
1401        expect_parse_ok(sql, expected)?;
1402
1403        // positive case: multiple locations use an explicit list
1404        let sql = "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV LOCATION ('foo.csv', 'bar.csv')";
1405        let expected = Statement::CreateExternalTable(CreateExternalTable {
1406            columns: vec![make_column_def("c1", DataType::Int(display))],
1407            ..make_create_external_table_with_locations(&["foo.csv", "bar.csv"])
1408        });
1409        expect_parse_ok(sql, expected)?;
1410
1411        assert_eq!(
1412            Statement::CreateExternalTable(make_create_external_table("foo.csv"))
1413                .to_string(),
1414            "CREATE EXTERNAL TABLE t STORED AS CSV LOCATION 'foo.csv'"
1415        );
1416        assert_eq!(
1417            Statement::CreateExternalTable(make_create_external_table_with_locations(&[
1418                "foo.csv", "bar.csv"
1419            ]))
1420            .to_string(),
1421            "CREATE EXTERNAL TABLE t STORED AS CSV LOCATION ('foo.csv', 'bar.csv')"
1422        );
1423        assert_eq!(
1424            Statement::CreateExternalTable(make_create_external_table("foo'bar.csv"))
1425                .to_string(),
1426            "CREATE EXTERNAL TABLE t STORED AS CSV LOCATION 'foo''bar.csv'"
1427        );
1428
1429        // positive case: leading space
1430        let sql = "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV LOCATION 'foo.csv'     ";
1431        let expected = Statement::CreateExternalTable(CreateExternalTable {
1432            columns: vec![make_column_def("c1", DataType::Int(None))],
1433            ..make_create_external_table("foo.csv")
1434        });
1435        expect_parse_ok(sql, expected)?;
1436
1437        // positive case: leading space + semicolon
1438        let sql =
1439            "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV LOCATION 'foo.csv'      ;";
1440        let expected = Statement::CreateExternalTable(CreateExternalTable {
1441            columns: vec![make_column_def("c1", DataType::Int(None))],
1442            ..make_create_external_table("foo.csv")
1443        });
1444        expect_parse_ok(sql, expected)?;
1445
1446        // positive case with delimiter
1447        let sql = "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV LOCATION 'foo.csv' OPTIONS (format.delimiter '|')";
1448        let display = None;
1449        let expected = Statement::CreateExternalTable(CreateExternalTable {
1450            columns: vec![make_column_def("c1", DataType::Int(display))],
1451            options: vec![(
1452                "format.delimiter".into(),
1453                Value::SingleQuotedString("|".into()),
1454            )],
1455            ..make_create_external_table("foo.csv")
1456        });
1457        expect_parse_ok(sql, expected)?;
1458
1459        // positive case: partitioned by
1460        let sql = "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV PARTITIONED BY (p1, p2) LOCATION 'foo.csv'";
1461        let display = None;
1462        let expected = Statement::CreateExternalTable(CreateExternalTable {
1463            columns: vec![make_column_def("c1", DataType::Int(display))],
1464            table_partition_cols: vec!["p1".to_string(), "p2".to_string()],
1465            ..make_create_external_table("foo.csv")
1466        });
1467        expect_parse_ok(sql, expected)?;
1468
1469        // positive case: it is ok for sql stmt with `COMPRESSION TYPE GZIP` tokens
1470        let sqls =
1471            vec![
1472             ("CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV LOCATION 'foo.csv' OPTIONS
1473             ('format.compression' 'GZIP')", "GZIP"),
1474             ("CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV LOCATION 'foo.csv' OPTIONS
1475             ('format.compression' 'BZIP2')", "BZIP2"),
1476             ("CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV LOCATION 'foo.csv' OPTIONS
1477             ('format.compression' 'XZ')", "XZ"),
1478             ("CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV LOCATION 'foo.csv' OPTIONS
1479             ('format.compression' 'ZSTD')", "ZSTD"),
1480        ];
1481        for (sql, compression) in sqls {
1482            let expected = Statement::CreateExternalTable(CreateExternalTable {
1483                columns: vec![make_column_def("c1", DataType::Int(display))],
1484                options: vec![(
1485                    "format.compression".into(),
1486                    Value::SingleQuotedString(compression.into()),
1487                )],
1488                ..make_create_external_table("foo.csv")
1489            });
1490            expect_parse_ok(sql, expected)?;
1491        }
1492
1493        // positive case: it is ok for parquet files not to have columns specified
1494        let sql = "CREATE EXTERNAL TABLE t STORED AS PARQUET LOCATION 'foo.parquet'";
1495        let expected = Statement::CreateExternalTable(CreateExternalTable {
1496            file_type: "PARQUET".to_string(),
1497            ..make_create_external_table("foo.parquet")
1498        });
1499        expect_parse_ok(sql, expected)?;
1500
1501        // positive case: it is ok for parquet files to be other than upper case
1502        let sql = "CREATE EXTERNAL TABLE t STORED AS parqueT LOCATION 'foo.parquet'";
1503        let expected = Statement::CreateExternalTable(CreateExternalTable {
1504            file_type: "PARQUET".to_string(),
1505            ..make_create_external_table("foo.parquet")
1506        });
1507        expect_parse_ok(sql, expected)?;
1508
1509        // positive case: it is ok for avro files not to have columns specified
1510        let sql = "CREATE EXTERNAL TABLE t STORED AS AVRO LOCATION 'foo.avro'";
1511        let expected = Statement::CreateExternalTable(CreateExternalTable {
1512            file_type: "AVRO".to_string(),
1513            ..make_create_external_table("foo.avro")
1514        });
1515        expect_parse_ok(sql, expected)?;
1516
1517        // positive case: it is ok for avro files not to have columns specified
1518        let sql = "CREATE EXTERNAL TABLE IF NOT EXISTS t STORED AS PARQUET LOCATION 'foo.parquet'";
1519        let expected = Statement::CreateExternalTable(CreateExternalTable {
1520            file_type: "PARQUET".to_string(),
1521            if_not_exists: true,
1522            ..make_create_external_table("foo.parquet")
1523        });
1524        expect_parse_ok(sql, expected)?;
1525
1526        // positive case: or replace
1527        let sql =
1528            "CREATE OR REPLACE EXTERNAL TABLE t STORED AS PARQUET LOCATION 'foo.parquet'";
1529        let expected = Statement::CreateExternalTable(CreateExternalTable {
1530            file_type: "PARQUET".to_string(),
1531            or_replace: true,
1532            ..make_create_external_table("foo.parquet")
1533        });
1534        expect_parse_ok(sql, expected)?;
1535
1536        // positive case: column definition allowed in 'partition by' clause
1537        let sql = "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV PARTITIONED BY (p1 int) LOCATION 'foo.csv'";
1538        let expected = Statement::CreateExternalTable(CreateExternalTable {
1539            columns: vec![
1540                make_column_def("c1", DataType::Int(None)),
1541                make_column_def("p1", DataType::Int(None)),
1542            ],
1543            table_partition_cols: vec!["p1".to_string()],
1544            ..make_create_external_table("foo.csv")
1545        });
1546        expect_parse_ok(sql, expected)?;
1547
1548        // negative case: mixed column defs and column names in `PARTITIONED BY` clause
1549        let sql = "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV PARTITIONED BY (p1 int, c1) LOCATION 'foo.csv'";
1550        expect_parse_error(
1551            sql,
1552            "SQL error: ParserError(\"Expected: a data type name, found: ) at Line: 1, Column: 73\")",
1553        );
1554
1555        // negative case: mixed column defs and column names in `PARTITIONED BY` clause
1556        let sql = "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV PARTITIONED BY (c1, p1 int) LOCATION 'foo.csv'";
1557        expect_parse_error(
1558            sql,
1559            "SQL error: ParserError(\"Expected: ',' or ')' after partition definition, found: int at Line: 1, Column: 70\")",
1560        );
1561
1562        // positive case: additional options (one entry) can be specified
1563        let sql =
1564            "CREATE EXTERNAL TABLE t STORED AS x OPTIONS ('k1' 'v1') LOCATION 'blahblah'";
1565        let expected = Statement::CreateExternalTable(CreateExternalTable {
1566            file_type: "X".to_string(),
1567            options: vec![("k1".into(), Value::SingleQuotedString("v1".into()))],
1568            ..make_create_external_table("blahblah")
1569        });
1570        expect_parse_ok(sql, expected)?;
1571
1572        // positive case: additional options (multiple entries) can be specified
1573        let sql = "CREATE EXTERNAL TABLE t STORED AS x OPTIONS ('k1' 'v1', k2 v2) LOCATION 'blahblah'";
1574        let expected = Statement::CreateExternalTable(CreateExternalTable {
1575            file_type: "X".to_string(),
1576            options: vec![
1577                ("k1".into(), Value::SingleQuotedString("v1".into())),
1578                ("k2".into(), Value::SingleQuotedString("v2".into())),
1579            ],
1580            ..make_create_external_table("blahblah")
1581        });
1582        expect_parse_ok(sql, expected)?;
1583
1584        // Ordered Col
1585        let sqls = [
1586            "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV WITH ORDER (c1) LOCATION 'foo.csv'",
1587            "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV WITH ORDER (c1 NULLS FIRST) LOCATION 'foo.csv'",
1588            "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV WITH ORDER (c1 NULLS LAST) LOCATION 'foo.csv'",
1589            "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV WITH ORDER (c1 ASC) LOCATION 'foo.csv'",
1590            "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV WITH ORDER (c1 DESC) LOCATION 'foo.csv'",
1591            "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV WITH ORDER (c1 DESC NULLS FIRST) LOCATION 'foo.csv'",
1592            "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV WITH ORDER (c1 DESC NULLS LAST) LOCATION 'foo.csv'",
1593            "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV WITH ORDER (c1 ASC NULLS FIRST) LOCATION 'foo.csv'",
1594            "CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV WITH ORDER (c1 ASC NULLS LAST) LOCATION 'foo.csv'",
1595        ];
1596        let expected = vec![
1597            (None, None),
1598            (None, Some(true)),
1599            (None, Some(false)),
1600            (Some(true), None),
1601            (Some(false), None),
1602            (Some(false), Some(true)),
1603            (Some(false), Some(false)),
1604            (Some(true), Some(true)),
1605            (Some(true), Some(false)),
1606        ];
1607        for (sql, (asc, nulls_first)) in sqls.iter().zip(expected) {
1608            let expected = Statement::CreateExternalTable(CreateExternalTable {
1609                columns: vec![make_column_def("c1", DataType::Int(None))],
1610                order_exprs: vec![vec![OrderByExpr {
1611                    expr: Identifier(Ident {
1612                        value: "c1".to_owned(),
1613                        quote_style: None,
1614                        span: Span::empty(),
1615                    }),
1616                    options: OrderByOptions { asc, nulls_first },
1617                    with_fill: None,
1618                }]],
1619                ..make_create_external_table("foo.csv")
1620            });
1621            expect_parse_ok(sql, expected)?;
1622        }
1623
1624        // Ordered Col
1625        let sql = "CREATE EXTERNAL TABLE t(c1 int, c2 int) STORED AS CSV WITH ORDER (c1 ASC, c2 DESC NULLS FIRST) LOCATION 'foo.csv'";
1626        let display = None;
1627        let expected = Statement::CreateExternalTable(CreateExternalTable {
1628            columns: vec![
1629                make_column_def("c1", DataType::Int(display)),
1630                make_column_def("c2", DataType::Int(display)),
1631            ],
1632            order_exprs: vec![vec![
1633                OrderByExpr {
1634                    expr: Identifier(Ident {
1635                        value: "c1".to_owned(),
1636                        quote_style: None,
1637                        span: Span::empty(),
1638                    }),
1639                    options: OrderByOptions {
1640                        asc: Some(true),
1641                        nulls_first: None,
1642                    },
1643                    with_fill: None,
1644                },
1645                OrderByExpr {
1646                    expr: Identifier(Ident {
1647                        value: "c2".to_owned(),
1648                        quote_style: None,
1649                        span: Span::empty(),
1650                    }),
1651                    options: OrderByOptions {
1652                        asc: Some(false),
1653                        nulls_first: Some(true),
1654                    },
1655                    with_fill: None,
1656                },
1657            ]],
1658            ..make_create_external_table("foo.csv")
1659        });
1660        expect_parse_ok(sql, expected)?;
1661
1662        // Ordered Binary op
1663        let sql = "CREATE EXTERNAL TABLE t(c1 int, c2 int) STORED AS CSV WITH ORDER (c1 - c2 ASC) LOCATION 'foo.csv'";
1664        let display = None;
1665        let expected = Statement::CreateExternalTable(CreateExternalTable {
1666            columns: vec![
1667                make_column_def("c1", DataType::Int(display)),
1668                make_column_def("c2", DataType::Int(display)),
1669            ],
1670            order_exprs: vec![vec![OrderByExpr {
1671                expr: Expr::BinaryOp {
1672                    left: Box::new(Identifier(Ident {
1673                        value: "c1".to_owned(),
1674                        quote_style: None,
1675                        span: Span::empty(),
1676                    })),
1677                    op: BinaryOperator::Minus,
1678                    right: Box::new(Identifier(Ident {
1679                        value: "c2".to_owned(),
1680                        quote_style: None,
1681                        span: Span::empty(),
1682                    })),
1683                },
1684                options: OrderByOptions {
1685                    asc: Some(true),
1686                    nulls_first: None,
1687                },
1688                with_fill: None,
1689            }]],
1690            ..make_create_external_table("foo.csv")
1691        });
1692        expect_parse_ok(sql, expected)?;
1693
1694        // Most complete CREATE EXTERNAL TABLE statement possible (using IF NOT EXISTS)
1695        let sql = "
1696            CREATE UNBOUNDED EXTERNAL TABLE IF NOT EXISTS t (c1 int, c2 float)
1697            STORED AS PARQUET
1698            WITH ORDER (c1 - c2 ASC)
1699            PARTITIONED BY (c1)
1700            LOCATION 'foo.parquet'
1701            OPTIONS ('format.compression' 'zstd',
1702                     'format.delimiter' '*',
1703                     'ROW_GROUP_SIZE' '1024',
1704                     'TRUNCATE' 'NO',
1705                     'format.has_header' 'true')";
1706        let expected = Statement::CreateExternalTable(CreateExternalTable {
1707            columns: vec![
1708                make_column_def("c1", DataType::Int(None)),
1709                make_column_def("c2", DataType::Float(ExactNumberInfo::None)),
1710            ],
1711            file_type: "PARQUET".to_string(),
1712            table_partition_cols: vec!["c1".into()],
1713            order_exprs: vec![vec![OrderByExpr {
1714                expr: Expr::BinaryOp {
1715                    left: Box::new(Identifier(Ident {
1716                        value: "c1".to_owned(),
1717                        quote_style: None,
1718                        span: Span::empty(),
1719                    })),
1720                    op: BinaryOperator::Minus,
1721                    right: Box::new(Identifier(Ident {
1722                        value: "c2".to_owned(),
1723                        quote_style: None,
1724                        span: Span::empty(),
1725                    })),
1726                },
1727                options: OrderByOptions {
1728                    asc: Some(true),
1729                    nulls_first: None,
1730                },
1731                with_fill: None,
1732            }]],
1733            if_not_exists: true,
1734            unbounded: true,
1735            options: vec![
1736                (
1737                    "format.compression".into(),
1738                    Value::SingleQuotedString("zstd".into()),
1739                ),
1740                (
1741                    "format.delimiter".into(),
1742                    Value::SingleQuotedString("*".into()),
1743                ),
1744                (
1745                    "ROW_GROUP_SIZE".into(),
1746                    Value::SingleQuotedString("1024".into()),
1747                ),
1748                ("TRUNCATE".into(), Value::SingleQuotedString("NO".into())),
1749                (
1750                    "format.has_header".into(),
1751                    Value::SingleQuotedString("true".into()),
1752                ),
1753            ],
1754            ..make_create_external_table("foo.parquet")
1755        });
1756        expect_parse_ok(sql, expected)?;
1757
1758        // Most complete CREATE EXTERNAL TABLE statement possible (using OR REPLACE)
1759        let sql = "
1760            CREATE OR REPLACE UNBOUNDED EXTERNAL TABLE t (c1 int, c2 float)
1761            STORED AS PARQUET
1762            WITH ORDER (c1 - c2 ASC)
1763            PARTITIONED BY (c1)
1764            LOCATION 'foo.parquet'
1765            OPTIONS ('format.compression' 'zstd',
1766                     'format.delimiter' '*',
1767                     'ROW_GROUP_SIZE' '1024',
1768                     'TRUNCATE' 'NO',
1769                     'format.has_header' 'true')";
1770        let expected = Statement::CreateExternalTable(CreateExternalTable {
1771            columns: vec![
1772                make_column_def("c1", DataType::Int(None)),
1773                make_column_def("c2", DataType::Float(ExactNumberInfo::None)),
1774            ],
1775            file_type: "PARQUET".to_string(),
1776            table_partition_cols: vec!["c1".into()],
1777            order_exprs: vec![vec![OrderByExpr {
1778                expr: Expr::BinaryOp {
1779                    left: Box::new(Identifier(Ident {
1780                        value: "c1".to_owned(),
1781                        quote_style: None,
1782                        span: Span::empty(),
1783                    })),
1784                    op: BinaryOperator::Minus,
1785                    right: Box::new(Identifier(Ident {
1786                        value: "c2".to_owned(),
1787                        quote_style: None,
1788                        span: Span::empty(),
1789                    })),
1790                },
1791                options: OrderByOptions {
1792                    asc: Some(true),
1793                    nulls_first: None,
1794                },
1795                with_fill: None,
1796            }]],
1797            or_replace: true,
1798            unbounded: true,
1799            options: vec![
1800                (
1801                    "format.compression".into(),
1802                    Value::SingleQuotedString("zstd".into()),
1803                ),
1804                (
1805                    "format.delimiter".into(),
1806                    Value::SingleQuotedString("*".into()),
1807                ),
1808                (
1809                    "ROW_GROUP_SIZE".into(),
1810                    Value::SingleQuotedString("1024".into()),
1811                ),
1812                ("TRUNCATE".into(), Value::SingleQuotedString("NO".into())),
1813                (
1814                    "format.has_header".into(),
1815                    Value::SingleQuotedString("true".into()),
1816                ),
1817            ],
1818            ..make_create_external_table("foo.parquet")
1819        });
1820        expect_parse_ok(sql, expected)?;
1821
1822        // For error cases, see: `create_external_table.slt`
1823
1824        Ok(())
1825    }
1826
1827    #[test]
1828    fn copy_to_table_to_table() -> Result<(), DataFusionError> {
1829        // positive case
1830        let sql = "COPY foo TO bar STORED AS CSV";
1831        let expected = Statement::CopyTo(CopyToStatement {
1832            source: object_name("foo"),
1833            target: "bar".to_string(),
1834            partitioned_by: vec![],
1835            stored_as: Some("CSV".to_owned()),
1836            options: vec![],
1837        });
1838
1839        assert_eq!(verified_stmt(sql), expected);
1840        Ok(())
1841    }
1842
1843    #[test]
1844    fn skip_copy_into_snowflake() -> Result<(), DataFusionError> {
1845        let sql = "COPY INTO foo FROM @~/staged FILE_FORMAT = (FORMAT_NAME = 'mycsv');";
1846        let dialect = Box::new(SnowflakeDialect);
1847        let statements = DFParser::parse_sql_with_dialect(sql, dialect.as_ref())?;
1848
1849        assert_eq!(
1850            statements.len(),
1851            1,
1852            "Expected to parse exactly one statement"
1853        );
1854        if let Statement::CopyTo(_) = &statements[0] {
1855            panic!("Expected non COPY TO statement, but was successful: {statements:?}");
1856        }
1857        Ok(())
1858    }
1859
1860    #[test]
1861    fn explain_copy_to_table_to_table() -> Result<(), DataFusionError> {
1862        let cases = vec![
1863            ("EXPLAIN COPY foo TO bar STORED AS PARQUET", false, false),
1864            (
1865                "EXPLAIN ANALYZE COPY foo TO bar STORED AS PARQUET",
1866                true,
1867                false,
1868            ),
1869            (
1870                "EXPLAIN VERBOSE COPY foo TO bar STORED AS PARQUET",
1871                false,
1872                true,
1873            ),
1874            (
1875                "EXPLAIN ANALYZE VERBOSE COPY foo TO bar STORED AS PARQUET",
1876                true,
1877                true,
1878            ),
1879        ];
1880        for (sql, analyze, verbose) in cases {
1881            println!("sql: {sql}, analyze: {analyze}, verbose: {verbose}");
1882
1883            let expected_copy = Statement::CopyTo(CopyToStatement {
1884                source: object_name("foo"),
1885                target: "bar".to_string(),
1886                partitioned_by: vec![],
1887                stored_as: Some("PARQUET".to_owned()),
1888                options: vec![],
1889            });
1890            let expected = Statement::Explain(ExplainStatement {
1891                options: ExplainStatementOptions {
1892                    analyze,
1893                    verbose,
1894                    format: None,
1895                    analyze_level: None,
1896                    analyze_categories: None,
1897                    show_statistics: None,
1898                },
1899                statement: Box::new(expected_copy),
1900            });
1901            assert_eq!(verified_stmt(sql), expected);
1902        }
1903        Ok(())
1904    }
1905
1906    #[test]
1907    fn copy_to_query_to_table() -> Result<(), DataFusionError> {
1908        let statement = verified_stmt("SELECT 1");
1909
1910        // unwrap the various layers
1911        let statement = if let Statement::Statement(statement) = statement {
1912            *statement
1913        } else {
1914            panic!("Expected statement, got {statement:?}");
1915        };
1916
1917        let query = if let SQLStatement::Query(query) = statement {
1918            query
1919        } else {
1920            panic!("Expected query, got {statement:?}");
1921        };
1922
1923        let sql =
1924            "COPY (SELECT 1) TO bar STORED AS CSV OPTIONS ('format.has_header' 'true')";
1925        let expected = Statement::CopyTo(CopyToStatement {
1926            source: CopyToSource::Query(query),
1927            target: "bar".to_string(),
1928            partitioned_by: vec![],
1929            stored_as: Some("CSV".to_owned()),
1930            options: vec![(
1931                "format.has_header".into(),
1932                Value::SingleQuotedString("true".into()),
1933            )],
1934        });
1935        assert_eq!(verified_stmt(sql), expected);
1936        Ok(())
1937    }
1938
1939    #[test]
1940    fn copy_to_options() -> Result<(), DataFusionError> {
1941        let sql = "COPY foo TO bar STORED AS CSV OPTIONS ('row_group_size' '55')";
1942        let expected = Statement::CopyTo(CopyToStatement {
1943            source: object_name("foo"),
1944            target: "bar".to_string(),
1945            partitioned_by: vec![],
1946            stored_as: Some("CSV".to_owned()),
1947            options: vec![(
1948                "row_group_size".to_string(),
1949                Value::SingleQuotedString("55".to_string()),
1950            )],
1951        });
1952        assert_eq!(verified_stmt(sql), expected);
1953        Ok(())
1954    }
1955
1956    #[test]
1957    fn copy_to_partitioned_by() -> Result<(), DataFusionError> {
1958        let sql = "COPY foo TO bar STORED AS CSV PARTITIONED BY (a) OPTIONS ('row_group_size' '55')";
1959        let expected = Statement::CopyTo(CopyToStatement {
1960            source: object_name("foo"),
1961            target: "bar".to_string(),
1962            partitioned_by: vec!["a".to_string()],
1963            stored_as: Some("CSV".to_owned()),
1964            options: vec![(
1965                "row_group_size".to_string(),
1966                Value::SingleQuotedString("55".to_string()),
1967            )],
1968        });
1969        assert_eq!(verified_stmt(sql), expected);
1970        Ok(())
1971    }
1972
1973    #[test]
1974    fn copy_to_multi_options() -> Result<(), DataFusionError> {
1975        // order of options is preserved
1976        let sql = "COPY foo TO bar STORED AS parquet OPTIONS ('format.row_group_size' 55, 'format.compression' snappy, 'execution.keep_partition_by_columns' true)";
1977
1978        let expected_options = vec![
1979            (
1980                "format.row_group_size".to_string(),
1981                Value::Number("55".to_string(), false),
1982            ),
1983            (
1984                "format.compression".to_string(),
1985                Value::SingleQuotedString("snappy".to_string()),
1986            ),
1987            (
1988                "execution.keep_partition_by_columns".to_string(),
1989                Value::SingleQuotedString("true".to_string()),
1990            ),
1991        ];
1992
1993        let mut statements = DFParser::parse_sql(sql).unwrap();
1994        assert_eq!(statements.len(), 1);
1995        let only_statement = statements.pop_front().unwrap();
1996
1997        let options = if let Statement::CopyTo(copy_to) = only_statement {
1998            copy_to.options
1999        } else {
2000            panic!("Expected copy");
2001        };
2002
2003        assert_eq!(options, expected_options);
2004
2005        Ok(())
2006    }
2007
2008    // For error cases, see: `copy.slt`
2009
2010    fn object_name(name: &str) -> CopyToSource {
2011        CopyToSource::Relation(ObjectName::from(vec![Ident::new(name)]))
2012    }
2013
2014    // Based on  sqlparser-rs
2015    // https://github.com/sqlparser-rs/sqlparser-rs/blob/ae3b5844c839072c235965fe0d1bddc473dced87/src/test_utils.rs#L104-L116
2016
2017    /// Ensures that `sql` parses as a single [Statement]
2018    ///
2019    /// If `canonical` is non empty,this function additionally asserts
2020    /// that:
2021    ///
2022    /// 1. parsing `sql` results in the same [`Statement`] as parsing
2023    ///    `canonical`.
2024    ///
2025    /// 2. re-serializing the result of parsing `sql` produces the same
2026    ///    `canonical` sql string
2027    fn one_statement_parses_to(sql: &str, canonical: &str) -> Statement {
2028        let mut statements = DFParser::parse_sql(sql).unwrap();
2029        assert_eq!(statements.len(), 1);
2030
2031        if sql != canonical {
2032            assert_eq!(DFParser::parse_sql(canonical).unwrap(), statements);
2033        }
2034
2035        let only_statement = statements.pop_front().unwrap();
2036        assert_eq!(
2037            canonical.to_uppercase(),
2038            only_statement.to_string().to_uppercase()
2039        );
2040        only_statement
2041    }
2042
2043    /// Ensures that `sql` parses as a single [Statement], and that
2044    /// re-serializing the parse result produces the same `sql`
2045    /// string (is not modified after a serialization round-trip).
2046    fn verified_stmt(sql: &str) -> Statement {
2047        one_statement_parses_to(sql, sql)
2048    }
2049
2050    #[test]
2051    /// Checks the recursion limit works for sql queries
2052    /// Recursion can happen easily with binary exprs (i.e, AND or OR)
2053    fn test_recursion_limit() {
2054        let sql = "SELECT 1 OR 2";
2055
2056        // Expect parse to succeed
2057        DFParserBuilder::new(sql)
2058            .build()
2059            .unwrap()
2060            .parse_statements()
2061            .unwrap();
2062
2063        let err = DFParserBuilder::new(sql)
2064            .with_recursion_limit(1)
2065            .build()
2066            .unwrap()
2067            .parse_statements()
2068            .unwrap_err();
2069
2070        assert_contains!(
2071            err.to_string(),
2072            "SQL error: RecursionLimitExceeded (current limit: 1)"
2073        );
2074    }
2075
2076    #[test]
2077    fn test_multistatement() {
2078        let sql = "COPY foo TO bar STORED AS CSV; \
2079             CREATE EXTERNAL TABLE t(c1 int) STORED AS CSV LOCATION 'foo.csv'; \
2080             RESET var;";
2081        let statements = DFParser::parse_sql(sql).unwrap();
2082        assert_eq!(
2083            statements,
2084            vec![
2085                Statement::CopyTo(CopyToStatement {
2086                    source: object_name("foo"),
2087                    target: "bar".to_string(),
2088                    partitioned_by: vec![],
2089                    stored_as: Some("CSV".to_owned()),
2090                    options: vec![],
2091                }),
2092                {
2093                    let display = None;
2094                    Statement::CreateExternalTable(CreateExternalTable {
2095                        columns: vec![make_column_def("c1", DataType::Int(display))],
2096                        ..make_create_external_table("foo.csv")
2097                    })
2098                },
2099                {
2100                    let name = ObjectName::from(vec![Ident::from("var")]);
2101                    Statement::Reset(ResetStatement::Variable(name))
2102                }
2103            ]
2104        );
2105    }
2106
2107    #[test]
2108    fn test_custom_tokens() {
2109        // Span mock.
2110        let span = Span {
2111            start: Location { line: 0, column: 0 },
2112            end: Location { line: 0, column: 0 },
2113        };
2114        let tokens = vec![
2115            TokenWithSpan {
2116                token: Token::make_keyword("SELECT"),
2117                span,
2118            },
2119            TokenWithSpan {
2120                token: Token::Whitespace(Whitespace::Space),
2121                span,
2122            },
2123            TokenWithSpan {
2124                token: Token::Placeholder("1".to_string()),
2125                span,
2126            },
2127        ];
2128
2129        let statements = DFParserBuilder::new(tokens)
2130            .build()
2131            .unwrap()
2132            .parse_statements()
2133            .unwrap();
2134        assert_eq!(statements.len(), 1);
2135    }
2136
2137    fn expect_parse_expr_ok(sql: &str, expected: ExprWithAlias) {
2138        let expr = DFParser::parse_sql_into_expr(sql).unwrap();
2139        assert_eq!(expr, expected, "actual:\n{expr:#?}");
2140    }
2141
2142    /// Parses sql and asserts that the expected error message was found
2143    fn expect_parse_expr_error(sql: &str, expected_error: &str) {
2144        match DFParser::parse_sql_into_expr(sql) {
2145            Ok(expr) => {
2146                panic!("Expected parse error for '{sql}', but was successful: {expr:#?}");
2147            }
2148            Err(e) => {
2149                let error_message = e.to_string();
2150                assert!(
2151                    error_message.contains(expected_error),
2152                    "Expected error '{expected_error}' not found in actual error '{error_message}'"
2153                );
2154            }
2155        }
2156    }
2157
2158    #[test]
2159    fn literal() {
2160        expect_parse_expr_ok(
2161            "1234",
2162            ExprWithAlias {
2163                expr: Expr::Value(ValueWithSpan::from(Value::Number(
2164                    "1234".to_string(),
2165                    false,
2166                ))),
2167                alias: None,
2168            },
2169        )
2170    }
2171
2172    #[test]
2173    fn literal_with_alias() {
2174        expect_parse_expr_ok(
2175            "1234 as foo",
2176            ExprWithAlias {
2177                expr: Expr::Value(ValueWithSpan::from(Value::Number(
2178                    "1234".to_string(),
2179                    false,
2180                ))),
2181                alias: Some(Ident::from("foo")),
2182            },
2183        )
2184    }
2185
2186    #[test]
2187    fn literal_with_alias_and_trailing_tokens() {
2188        expect_parse_expr_error(
2189            "1234 as foo.bar",
2190            "Expected: end of expression, found: .",
2191        )
2192    }
2193
2194    #[test]
2195    fn literal_with_alias_and_trailing_whitespace() {
2196        expect_parse_expr_ok(
2197            "1234 as foo   ",
2198            ExprWithAlias {
2199                expr: Expr::Value(ValueWithSpan::from(Value::Number(
2200                    "1234".to_string(),
2201                    false,
2202                ))),
2203                alias: Some(Ident::from("foo")),
2204            },
2205        )
2206    }
2207
2208    #[test]
2209    fn literal_with_alias_and_trailing_whitespace_and_token() {
2210        expect_parse_expr_error(
2211            "1234 as foo    bar",
2212            "Expected: end of expression, found: bar",
2213        )
2214    }
2215
2216    // ------------------------------------------------------------------
2217    // Postgres-style `EXPLAIN (option, ...)` tests
2218    // ------------------------------------------------------------------
2219
2220    fn parse_with_pg(sql: &str) -> Result<Statement, DataFusionError> {
2221        let dialect = sqlparser::dialect::PostgreSqlDialect {};
2222        let mut statements = DFParser::parse_sql_with_dialect(sql, &dialect)?;
2223        assert_eq!(statements.len(), 1, "Expected exactly one statement");
2224        Ok(statements.pop_front().unwrap())
2225    }
2226
2227    fn parse_with_generic(sql: &str) -> Result<Statement, DataFusionError> {
2228        let mut statements = DFParser::parse_sql(sql)?;
2229        assert_eq!(statements.len(), 1, "Expected exactly one statement");
2230        Ok(statements.pop_front().unwrap())
2231    }
2232
2233    #[test]
2234    fn explain_legacy_keyword_form_postgres_dialect() {
2235        // The legacy keyword form still works under PostgreSQL dialect.
2236        let stmt = parse_with_pg("EXPLAIN ANALYZE VERBOSE SELECT 1").unwrap();
2237        let Statement::Explain(ExplainStatement { options, .. }) = stmt else {
2238            panic!("Expected Statement::Explain");
2239        };
2240        assert!(options.analyze);
2241        assert!(options.verbose);
2242        assert!(options.format.is_none());
2243        assert!(options.analyze_level.is_none());
2244    }
2245
2246    #[test]
2247    fn explain_paren_form_on_generic_supports_utility_options() {
2248        // sqlparser's GenericDialect also declares
2249        // `supports_explain_with_utility_options = true`, so DataFusion's
2250        // default parser accepts the parenthesized form too.
2251        let stmt = parse_with_generic("EXPLAIN (FORMAT TREE) SELECT 1").unwrap();
2252        let Statement::Explain(ExplainStatement { options, .. }) = stmt else {
2253            panic!("Expected Statement::Explain");
2254        };
2255        assert_eq!(options.format, Some(ExplainFormat::Tree));
2256    }
2257
2258    #[test]
2259    fn explain_paren_form_on_non_supporting_dialect_is_parse_error() {
2260        // Dialects that do NOT declare support for utility options (e.g.
2261        // Snowflake) must still error on the parenthesized form — proving
2262        // the dialect gate itself works.
2263        use sqlparser::dialect::SnowflakeDialect;
2264        let dialect = SnowflakeDialect {};
2265        let res =
2266            DFParser::parse_sql_with_dialect("EXPLAIN (FORMAT TREE) SELECT 1", &dialect);
2267        assert!(
2268            res.is_err(),
2269            "expected parse error under non-supporting dialect"
2270        );
2271    }
2272
2273    #[test]
2274    fn explain_paren_grouping_query_is_not_mistaken_for_options() {
2275        // Historic DataFusion behavior allows parentheses around the
2276        // query after EXPLAIN (e.g. `EXPLAIN (SELECT ...)` or
2277        // `EXPLAIN (q1 EXCEPT q2) UNION ALL (q3 EXCEPT q4)`). The dialect
2278        // gate for Postgres-style options must not swallow these.
2279        for sql in [
2280            "EXPLAIN (SELECT 1)",
2281            "EXPLAIN (WITH t AS (SELECT 1) SELECT * FROM t)",
2282            "EXPLAIN (VALUES (1), (2))",
2283            "EXPLAIN ((SELECT 1))",
2284        ] {
2285            let stmt = parse_with_pg(sql).unwrap_or_else(|e| {
2286                panic!("{sql} failed under PG dialect: {e}");
2287            });
2288            let Statement::Explain(ExplainStatement { options, .. }) = stmt else {
2289                panic!("Expected Statement::Explain for {sql}");
2290            };
2291            assert!(!options.analyze, "{sql} should not be ANALYZE");
2292            assert!(!options.verbose, "{sql} should not be VERBOSE");
2293            assert!(options.format.is_none(), "{sql} should have no FORMAT");
2294        }
2295    }
2296
2297    #[test]
2298    fn explain_paren_form_analyze_verbose() {
2299        let stmt = parse_with_pg("EXPLAIN (ANALYZE, VERBOSE) SELECT 1").unwrap();
2300        let Statement::Explain(ExplainStatement { options, .. }) = stmt else {
2301            panic!("Expected Statement::Explain");
2302        };
2303        assert!(options.analyze);
2304        assert!(options.verbose);
2305    }
2306
2307    #[test]
2308    fn explain_paren_form_format_tree() {
2309        let stmt = parse_with_pg("EXPLAIN (FORMAT tree) SELECT 1").unwrap();
2310        let Statement::Explain(ExplainStatement { options, .. }) = stmt else {
2311            panic!("Expected Statement::Explain");
2312        };
2313        assert!(!options.analyze);
2314        assert_eq!(options.format, Some(ExplainFormat::Tree));
2315    }
2316
2317    #[test]
2318    fn explain_paren_form_metrics_level() {
2319        use datafusion_common::format::{
2320            ExplainAnalyzeCategories, MetricCategory, MetricType,
2321        };
2322        let stmt =
2323            parse_with_pg("EXPLAIN (ANALYZE, METRICS 'rows,bytes', LEVEL dev) SELECT 1")
2324                .unwrap();
2325        let Statement::Explain(ExplainStatement { options, .. }) = stmt else {
2326            panic!("Expected Statement::Explain");
2327        };
2328        assert!(options.analyze);
2329        assert_eq!(options.analyze_level, Some(MetricType::Dev));
2330        assert_eq!(
2331            options.analyze_categories,
2332            Some(ExplainAnalyzeCategories::Only(vec![
2333                MetricCategory::Rows,
2334                MetricCategory::Bytes,
2335            ]))
2336        );
2337    }
2338
2339    #[test]
2340    fn explain_paren_form_bool_spellings() {
2341        let stmt =
2342            parse_with_pg("EXPLAIN (ANALYZE ON, VERBOSE OFF, COSTS TRUE) SELECT 1")
2343                .unwrap();
2344        let Statement::Explain(ExplainStatement { options, .. }) = stmt else {
2345            panic!("Expected Statement::Explain");
2346        };
2347        assert!(options.analyze);
2348        assert!(!options.verbose);
2349        assert_eq!(options.show_statistics, Some(true));
2350    }
2351
2352    #[test]
2353    fn explain_paren_form_buffers_rejected() {
2354        let err = parse_with_pg("EXPLAIN (BUFFERS) SELECT 1").unwrap_err();
2355        let msg = err.to_string();
2356        assert!(
2357            msg.contains("BUFFERS"),
2358            "error should mention BUFFERS: {msg}"
2359        );
2360        assert!(
2361            msg.contains("not supported"),
2362            "error should say not supported: {msg}"
2363        );
2364    }
2365
2366    #[test]
2367    fn explain_paren_form_unknown_option_rejected() {
2368        let err = parse_with_pg("EXPLAIN (ASDF) SELECT 1").unwrap_err();
2369        let msg = err.to_string();
2370        assert!(
2371            msg.contains("unknown EXPLAIN option"),
2372            "error should describe unknown option: {msg}"
2373        );
2374    }
2375}