You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/05/06 11:01:51 UTC

(datafusion-sqlparser-rs) branch gh-readonly-queue/main/pr-2329-3f347e38c0b3290f90cb853f8b6d37c509bdc2f6 created (now f98c9d15)

This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a change to branch gh-readonly-queue/main/pr-2329-3f347e38c0b3290f90cb853f8b6d37c509bdc2f6
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


      at f98c9d15 [Teradata] Add `CREATE TABLE` options (#2329)

This branch includes the following new commits:

     new f98c9d15 [Teradata] Add `CREATE TABLE` options (#2329)

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org


(datafusion-sqlparser-rs) 01/01: [Teradata] Add `CREATE TABLE` options (#2329)

Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-2329-3f347e38c0b3290f90cb853f8b6d37c509bdc2f6
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git

commit f98c9d151170a86eacc57066c598dfbca3b304df
Author: Ifeanyi Ubah <if...@validio.io>
AuthorDate: Wed May 6 13:01:27 2026 +0200

    [Teradata] Add `CREATE TABLE` options (#2329)
---
 src/ast/ddl.rs                       | 59 ++++++++++++++++++++++++++++--
 src/ast/helpers/stmt_create_table.rs | 33 ++++++++++++++++-
 src/ast/mod.rs                       |  2 +-
 src/ast/spans.rs                     |  3 ++
 src/dialect/mod.rs                   | 10 ++++++
 src/dialect/teradata.rs              |  5 +++
 src/keywords.rs                      |  1 +
 src/parser/mod.rs                    | 70 +++++++++++++++++++++++++++++++++++-
 tests/sqlparser_duckdb.rs            |  3 ++
 tests/sqlparser_mssql.rs             |  6 ++++
 tests/sqlparser_postgres.rs          |  3 ++
 tests/sqlparser_snowflake.rs         |  7 ----
 tests/sqlparser_teradata.rs          | 62 +++++++++++++++++++++++++++++++-
 13 files changed, 251 insertions(+), 13 deletions(-)

diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs
index 067f4b8d..62a8a71a 100644
--- a/src/ast/ddl.rs
+++ b/src/ast/ddl.rs
@@ -3059,6 +3059,20 @@ pub struct CreateTable {
     /// Redshift `BACKUP` option: `BACKUP { YES | NO }`
     /// <https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_TABLE_NEW.html>
     pub backup: Option<bool>,
+    /// `MULTISET | SET` table-kind prefix.
+    /// `Some(true)` => `MULTISET`, `Some(false)` => `SET`.
+    ///
+    /// [Teradata](https://docs.teradata.com/r/Enterprise_IntelliFlex_VMware/SQL-Data-Definition-Language-Syntax-and-Examples/Table-Statements/CREATE-TABLE-and-CREATE-TABLE-AS/Syntax-Elements/MULTISET-or-SET)
+    pub multiset: Option<bool>,
+    /// `FALLBACK` clause.
+    /// `Some(true)` => `FALLBACK`, `Some(false)` => `NO FALLBACK`
+    ///
+    /// [Teradata](https://docs.teradata.com/r/Enterprise_IntelliFlex_VMware/SQL-Data-Definition-Language-Syntax-and-Examples/Table-Statements/CREATE-TABLE-and-CREATE-TABLE-AS/Syntax-Elements/FALLBACK-or-NO-FALLBACK)
+    pub fallback: Option<bool>,
+    /// `WITH DATA` clause on a `CREATE TABLE ... AS` statement.
+    ///
+    /// [Teradata](https://docs.teradata.com/r/Enterprise_IntelliFlex_VMware/SQL-Data-Definition-Language-Syntax-and-Examples/Table-Statements/CREATE-TABLE-and-CREATE-TABLE-AS/Syntax-Elements/AS_clause/WITH-Clause-Phrase)
+    pub with_data: Option<WithData>,
 }
 
 impl fmt::Display for CreateTable {
@@ -3072,7 +3086,7 @@ impl fmt::Display for CreateTable {
         //   `CREATE TABLE t (a INT) AS SELECT a from t2`
         write!(
             f,
-            "CREATE {or_replace}{external}{global}{temporary}{transient}{volatile}{dynamic}{iceberg}{snapshot}TABLE {if_not_exists}{name}",
+            "CREATE {or_replace}{external}{global}{multiset}{temporary}{transient}{volatile}{dynamic}{iceberg}{snapshot}TABLE {if_not_exists}{name}",
             or_replace = if self.or_replace { "OR REPLACE " } else { "" },
             external = if self.external { "EXTERNAL " } else { "" },
             snapshot = if self.snapshot { "SNAPSHOT " } else { "" },
@@ -3086,14 +3100,20 @@ impl fmt::Display for CreateTable {
                 })
                 .unwrap_or(""),
             if_not_exists = if self.if_not_exists { "IF NOT EXISTS " } else { "" },
+            multiset = self
+                .multiset
+                .map(|m| if m { "MULTISET " } else { "SET " })
+                .unwrap_or(""),
             temporary = if self.temporary { "TEMPORARY " } else { "" },
             transient = if self.transient { "TRANSIENT " } else { "" },
             volatile = if self.volatile { "VOLATILE " } else { "" },
-            // Only for Snowflake
             iceberg = if self.iceberg { "ICEBERG " } else { "" },
             dynamic = if self.dynamic { "DYNAMIC " } else { "" },
             name = self.name,
         )?;
+        if let Some(fallback) = self.fallback {
+            write!(f, ", {}", if fallback { "FALLBACK" } else { "NO FALLBACK" })?;
+        }
         if let Some(partition_of) = &self.partition_of {
             write!(f, " PARTITION OF {partition_of}")?;
         }
@@ -3378,6 +3398,41 @@ impl fmt::Display for CreateTable {
         if let Some(query) = &self.query {
             write!(f, " AS {query}")?;
         }
+        if let Some(with_data) = &self.with_data {
+            write!(f, " {with_data}")?;
+        }
+        Ok(())
+    }
+}
+
+/// `WITH DATA` clause on `CREATE TABLE ... AS` statement.
+///
+/// [Teradata](https://docs.teradata.com/r/Enterprise_IntelliFlex_VMware/SQL-Data-Definition-Language-Syntax-and-Examples/Table-Statements/CREATE-TABLE-and-CREATE-TABLE-AS/Syntax-Elements/AS_clause/WITH-Clause-Phrase)
+#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct WithData {
+    /// `true` for `WITH DATA`, `false` for `WITH NO DATA`.
+    pub data: bool,
+    /// `Some(true)` for `AND STATISTICS`, `Some(false)` for `AND NO STATISTICS`,
+    /// `None` if the `AND [NO] STATISTICS` sub-clause is omitted.
+    pub statistics: Option<bool>,
+}
+
+impl fmt::Display for WithData {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.write_str("WITH ")?;
+        if !self.data {
+            f.write_str("NO ")?;
+        }
+        f.write_str("DATA")?;
+        if let Some(stats) = self.statistics {
+            f.write_str(" AND ")?;
+            if !stats {
+                f.write_str("NO ")?;
+            }
+            f.write_str("STATISTICS")?;
+        }
         Ok(())
     }
 }
diff --git a/src/ast/helpers/stmt_create_table.rs b/src/ast/helpers/stmt_create_table.rs
index ab2feb69..fc81d3b8 100644
--- a/src/ast/helpers/stmt_create_table.rs
+++ b/src/ast/helpers/stmt_create_table.rs
@@ -29,7 +29,7 @@ use crate::ast::{
     DistStyle, Expr, FileFormat, ForValues, HiveDistributionStyle, HiveFormat, Ident,
     InitializeKind, ObjectName, OnCommit, OneOrManyWithParens, Query, RefreshModeKind,
     RowAccessPolicy, Statement, StorageLifecyclePolicy, StorageSerializationPolicy,
-    TableConstraint, TableVersion, Tag, WrappedCollection,
+    TableConstraint, TableVersion, Tag, WithData, WrappedCollection,
 };
 
 use crate::parser::ParserError;
@@ -183,6 +183,12 @@ pub struct CreateTableBuilder {
     pub sortkey: Option<Vec<Expr>>,
     /// Redshift `BACKUP` option.
     pub backup: Option<bool>,
+    /// `MULTISET | SET` table-kind prefix.
+    pub multiset: Option<bool>,
+    /// `FALLBACK` clause.
+    pub fallback: Option<bool>,
+    /// `WITH DATA` clause.
+    pub with_data: Option<WithData>,
 }
 
 impl CreateTableBuilder {
@@ -248,6 +254,9 @@ impl CreateTableBuilder {
             distkey: None,
             sortkey: None,
             backup: None,
+            multiset: None,
+            fallback: None,
+            with_data: None,
         }
     }
     /// Set `OR REPLACE` for the CREATE TABLE statement.
@@ -556,6 +565,22 @@ impl CreateTableBuilder {
         self.backup = backup;
         self
     }
+    /// Set `MULTISET | SET` table-kind prefix.
+    /// Some(true) => `MULTISET`, Some(false) => `SET`.
+    pub fn multiset(mut self, multiset: Option<bool>) -> Self {
+        self.multiset = multiset;
+        self
+    }
+    /// Set `FALLBACK` / `NO FALLBACK` flag.
+    pub fn fallback(mut self, fallback: Option<bool>) -> Self {
+        self.fallback = fallback;
+        self
+    }
+    /// Set `WITH DATA` clause.
+    pub fn with_data(mut self, with_data: Option<WithData>) -> Self {
+        self.with_data = with_data;
+        self
+    }
     /// Consume the builder and produce a `CreateTable`.
     pub fn build(self) -> CreateTable {
         CreateTable {
@@ -618,6 +643,9 @@ impl CreateTableBuilder {
             distkey: self.distkey,
             sortkey: self.sortkey,
             backup: self.backup,
+            multiset: self.multiset,
+            fallback: self.fallback,
+            with_data: self.with_data,
         }
     }
 }
@@ -699,6 +727,9 @@ impl From<CreateTable> for CreateTableBuilder {
             distkey: table.distkey,
             sortkey: table.sortkey,
             backup: table.backup,
+            multiset: table.multiset,
+            fallback: table.fallback,
+            with_data: table.with_data,
         }
     }
 }
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index ad2b244b..758bc6a6 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -81,7 +81,7 @@ pub use self::ddl::{
     PartitionBoundValue, ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity,
     TagsColumnOption, TriggerObjectKind, Truncate, UserDefinedTypeCompositeAttributeDef,
     UserDefinedTypeInternalLength, UserDefinedTypeRangeOption, UserDefinedTypeRepresentation,
-    UserDefinedTypeSqlDefinitionOption, UserDefinedTypeStorage, ViewColumnDef,
+    UserDefinedTypeSqlDefinitionOption, UserDefinedTypeStorage, ViewColumnDef, WithData,
 };
 pub use self::dml::{
     Delete, Insert, Merge, MergeAction, MergeClause, MergeClauseKind, MergeInsertExpr,
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index f0e3e252..343d99e7 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -604,6 +604,9 @@ impl Spanned for CreateTable {
             distkey: _,
             sortkey: _,
             backup: _,
+            multiset: _,
+            fallback: _,
+            with_data: _,
         } = self;
 
         union_spans(
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index 6ab6cb15..84032847 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -1224,6 +1224,16 @@ pub trait Dialect: Debug + Any {
         false
     }
 
+    /// Returns true if the dialect accepts a comma-separated list of table-level
+    /// options placed between the table name and the column-list parenthesis, e.g.
+    ///
+    /// ```sql
+    /// CREATE TABLE foo, NO FALLBACK, NO BEFORE JOURNAL (col INTEGER)
+    /// ```
+    fn supports_leading_comma_before_table_options(&self) -> bool {
+        false
+    }
+
     /// Returns true if the dialect supports PartiQL for querying semi-structured data
     /// <https://partiql.org/index.html>
     fn supports_partiql(&self) -> bool {
diff --git a/src/dialect/teradata.rs b/src/dialect/teradata.rs
index e88a4007..c8470cb6 100644
--- a/src/dialect/teradata.rs
+++ b/src/dialect/teradata.rs
@@ -89,4 +89,9 @@ impl Dialect for TeradataDialect {
     fn supports_string_literal_concatenation(&self) -> bool {
         true
     }
+
+    /// See <https://docs.teradata.com/r/Enterprise_IntelliFlex_VMware/SQL-Data-Definition-Language-Syntax-and-Examples/Table-Statements/CREATE-TABLE-and-CREATE-TABLE-AS>
+    fn supports_leading_comma_before_table_options(&self) -> bool {
+        true
+    }
 }
diff --git a/src/keywords.rs b/src/keywords.rs
index 4fc8f72d..a0a65be6 100644
--- a/src/keywords.rs
+++ b/src/keywords.rs
@@ -414,6 +414,7 @@ define_keywords!(
     FACTS,
     FAIL,
     FAILOVER,
+    FALLBACK,
     FALSE,
     FAMILY,
     FETCH,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 3d848910..76616bc3 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -5124,6 +5124,7 @@ impl<'a> Parser<'a> {
     pub fn parse_create(&mut self) -> Result<Statement, ParserError> {
         let or_replace = self.parse_keywords(&[Keyword::OR, Keyword::REPLACE]);
         let or_alter = self.parse_keywords(&[Keyword::OR, Keyword::ALTER]);
+        let multiset = self.maybe_parse_multiset();
         let local = self.parse_one_of_keywords(&[Keyword::LOCAL]).is_some();
         let global = self.parse_one_of_keywords(&[Keyword::GLOBAL]).is_some();
         let transient = self.parse_one_of_keywords(&[Keyword::TRANSIENT]).is_some();
@@ -5137,13 +5138,14 @@ impl<'a> Parser<'a> {
         let temporary = self
             .parse_one_of_keywords(&[Keyword::TEMP, Keyword::TEMPORARY])
             .is_some();
+        let volatile = self.parse_keyword(Keyword::VOLATILE);
         let persistent = dialect_of!(self is DuckDbDialect)
             && self.parse_one_of_keywords(&[Keyword::PERSISTENT]).is_some();
         let create_view_params = self.parse_create_view_params()?;
         if self.peek_keywords(&[Keyword::SNAPSHOT, Keyword::TABLE]) {
             self.parse_create_snapshot_table().map(Into::into)
         } else if self.parse_keyword(Keyword::TABLE) {
-            self.parse_create_table(or_replace, temporary, global, transient)
+            self.parse_create_table(or_replace, temporary, global, transient, volatile, multiset)
                 .map(Into::into)
         } else if self.peek_keyword(Keyword::MATERIALIZED)
             || self.peek_keyword(Keyword::VIEW)
@@ -8476,11 +8478,25 @@ impl<'a> Parser<'a> {
         temporary: bool,
         global: Option<bool>,
         transient: bool,
+        volatile: bool,
+        multiset: Option<bool>,
     ) -> Result<CreateTable, ParserError> {
         let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect);
         let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
         let table_name = self.parse_object_name(allow_unquoted_hyphen)?;
 
+        let fallback = if self.dialect.supports_leading_comma_before_table_options()
+            && self.consume_token(&Token::Comma)
+        {
+            let fallback = self.maybe_parse_fallback()?;
+            if fallback.is_none() {
+                self.prev_token(); // Put back comma.
+            }
+            fallback
+        } else {
+            None
+        };
+
         // PostgreSQL PARTITION OF for child partition tables
         // Note: This is a PostgreSQL-specific feature, but the dialect check was intentionally
         // removed to allow GenericDialect and other dialects to parse this syntax. This enables
@@ -8632,6 +8648,13 @@ impl<'a> Parser<'a> {
             None
         };
 
+        // `WITH DATA` clause only applies if there is a query body.
+        let with_data = if query.is_some() {
+            self.maybe_parse_with_data()?
+        } else {
+            None
+        };
+
         Ok(CreateTableBuilder::new(table_name)
             .temporary(temporary)
             .columns(columns)
@@ -8639,6 +8662,9 @@ impl<'a> Parser<'a> {
             .or_replace(or_replace)
             .if_not_exists(if_not_exists)
             .transient(transient)
+            .volatile(volatile)
+            .multiset(multiset)
+            .fallback(fallback)
             .hive_distribution(hive_distribution)
             .hive_formats(hive_formats)
             .global(global)
@@ -8658,6 +8684,7 @@ impl<'a> Parser<'a> {
             .for_values(for_values)
             .table_options(create_table_config.table_options)
             .primary_key(primary_key)
+            .with_data(with_data)
             .strict(strict)
             .backup(backup)
             .diststyle(diststyle)
@@ -8666,6 +8693,47 @@ impl<'a> Parser<'a> {
             .build())
     }
 
+    /// Parse `MULTISET` table-kind prefix on `CREATE TABLE`.
+    fn maybe_parse_multiset(&mut self) -> Option<bool> {
+        match self.parse_one_of_keywords(&[Keyword::SET, Keyword::MULTISET]) {
+            Some(Keyword::MULTISET) => Some(true),
+            Some(Keyword::SET) => Some(false),
+            _ => None,
+        }
+    }
+
+    /// Parse `FALLBACK` option on a `CREATE TABLE` statement,
+    fn maybe_parse_fallback(&mut self) -> Result<Option<bool>, ParserError> {
+        if self.parse_keywords(&[Keyword::NO, Keyword::FALLBACK]) {
+            Ok(Some(false))
+        } else if self.parse_keyword(Keyword::FALLBACK) {
+            Ok(Some(true))
+        } else {
+            Ok(None)
+        }
+    }
+
+    /// Parse [`WithData`] clause on `CREATE TABLE ... AS` statement.
+    fn maybe_parse_with_data(&mut self) -> Result<Option<WithData>, ParserError> {
+        let data = if self.parse_keywords(&[Keyword::WITH, Keyword::DATA]) {
+            true
+        } else if self.parse_keywords(&[Keyword::WITH, Keyword::NO, Keyword::DATA]) {
+            false
+        } else {
+            return Ok(None);
+        };
+
+        let statistics = if self.parse_keywords(&[Keyword::AND, Keyword::STATISTICS]) {
+            Some(true)
+        } else if self.parse_keywords(&[Keyword::AND, Keyword::NO, Keyword::STATISTICS]) {
+            Some(false)
+        } else {
+            None
+        };
+
+        Ok(Some(WithData { data, statistics }))
+    }
+
     fn maybe_parse_create_table_like(
         &mut self,
         allow_unquoted_hyphen: bool,
diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs
index df626858..548ad27c 100644
--- a/tests/sqlparser_duckdb.rs
+++ b/tests/sqlparser_duckdb.rs
@@ -794,6 +794,9 @@ fn test_duckdb_union_datatype() {
             distkey: Default::default(),
             sortkey: Default::default(),
             backup: Default::default(),
+            multiset: Default::default(),
+            fallback: Default::default(),
+            with_data: Default::default(),
         }),
         stmt
     );
diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs
index 1e053da7..d784c74a 100644
--- a/tests/sqlparser_mssql.rs
+++ b/tests/sqlparser_mssql.rs
@@ -2013,6 +2013,9 @@ fn parse_create_table_with_valid_options() {
                 distkey: None,
                 sortkey: None,
                 backup: None,
+                multiset: None,
+                fallback: None,
+                with_data: None,
             })
         );
     }
@@ -2187,6 +2190,9 @@ fn parse_create_table_with_identity_column() {
                 distkey: None,
                 sortkey: None,
                 backup: None,
+                multiset: None,
+                fallback: None,
+                with_data: None,
             }),
         );
     }
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index 86315b1e..87d17d1b 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -6716,6 +6716,9 @@ fn parse_trigger_related_functions() {
             distkey: None,
             sortkey: None,
             backup: None,
+            multiset: None,
+            fallback: None,
+            with_data: None,
         }
     );
 
diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs
index 75cf8ebb..e75dcbf7 100644
--- a/tests/sqlparser_snowflake.rs
+++ b/tests/sqlparser_snowflake.rs
@@ -471,13 +471,6 @@ fn test_snowflake_create_invalid_temporal_table() {
         ))
     );
 
-    assert_eq!(
-        snowflake().parse_sql_statements("CREATE TEMP VOLATILE TABLE my_table (a INT)"),
-        Err(ParserError::ParserError(
-            "Expected: an object type after CREATE, found: VOLATILE".to_string()
-        ))
-    );
-
     assert_eq!(
         snowflake().parse_sql_statements("CREATE TEMP TRANSIENT TABLE my_table (a INT)"),
         Err(ParserError::ParserError(
diff --git a/tests/sqlparser_teradata.rs b/tests/sqlparser_teradata.rs
index b8e6a53c..a4e66af0 100644
--- a/tests/sqlparser_teradata.rs
+++ b/tests/sqlparser_teradata.rs
@@ -17,7 +17,8 @@
 
 //! Test SQL syntax, specific to [sqlparser::dialect::TeradataDialect].
 
-use sqlparser::dialect::{Dialect, TeradataDialect};
+use sqlparser::dialect::{Dialect, GenericDialect, TeradataDialect};
+use sqlparser::test_utils::all_dialects_where;
 use test_utils::TestedDialects;
 
 mod test_utils;
@@ -26,6 +27,10 @@ fn teradata() -> TestedDialects {
     TestedDialects::new(vec![Box::new(TeradataDialect)])
 }
 
+fn teradata_and_generic() -> TestedDialects {
+    TestedDialects::new(vec![Box::new(TeradataDialect), Box::new(GenericDialect {})])
+}
+
 #[test]
 fn dialect_methods() {
     let d: &dyn Dialect = &TeradataDialect;
@@ -45,6 +50,7 @@ fn dialect_methods() {
     assert!(d.supports_top_before_distinct());
     assert!(d.supports_window_function_null_treatment_arg());
     assert!(d.supports_string_literal_concatenation());
+    assert!(d.supports_leading_comma_before_table_options());
 }
 
 #[test]
@@ -61,3 +67,57 @@ fn parse_identifier() {
         r#"NULL AS "quoted id""#
     ));
 }
+
+#[test]
+fn parse_create_table_multiset() {
+    teradata_and_generic().verified_stmt("CREATE MULTISET TABLE foo (id INT)");
+    teradata_and_generic().verified_stmt("CREATE SET TABLE foo (id INT)");
+}
+
+#[test]
+fn parse_create_table_volatile() {
+    teradata_and_generic().verified_stmt("CREATE VOLATILE TABLE foo (id INT)");
+    teradata_and_generic().verified_stmt("CREATE MULTISET VOLATILE TABLE foo (id INT)");
+}
+
+#[test]
+fn parse_create_table_fallback() {
+    teradata().verified_stmt("CREATE TABLE foo, FALLBACK (id INT)");
+    teradata().verified_stmt("CREATE TABLE foo, NO FALLBACK (id INT)");
+    teradata().verified_stmt("CREATE MULTISET TABLE foo, NO FALLBACK (id INT)");
+}
+
+#[test]
+fn parse_create_table_as_with_data() {
+    teradata_and_generic().verified_stmt("CREATE TABLE foo AS (SELECT 1 AS a) WITH DATA");
+    teradata_and_generic().verified_stmt("CREATE TABLE foo AS (SELECT 1 AS a) WITH NO DATA");
+    teradata_and_generic()
+        .verified_stmt("CREATE TABLE foo AS (SELECT 1 AS a) WITH DATA AND STATISTICS");
+    teradata_and_generic()
+        .verified_stmt("CREATE TABLE foo AS (SELECT 1 AS a) WITH DATA AND NO STATISTICS");
+    teradata_and_generic()
+        .verified_stmt("CREATE TABLE foo AS (SELECT 1 AS a) WITH NO DATA AND STATISTICS");
+    teradata_and_generic()
+        .verified_stmt("CREATE TABLE foo AS (SELECT 1 AS a) WITH NO DATA AND NO STATISTICS");
+}
+
+#[test]
+fn parse_create_table_options() {
+    teradata().verified_stmt(concat!(
+        "CREATE MULTISET VOLATILE TABLE foo, NO FALLBACK ",
+        "(id INT, name VARCHAR(100)) ",
+        "ON COMMIT PRESERVE ROWS"
+    ));
+}
+
+#[test]
+fn parse_leading_comma_before_table_options() {
+    let dialect = all_dialects_where(|d| d.supports_leading_comma_before_table_options());
+    dialect.verified_stmt("CREATE TABLE foo, FALLBACK (id INT)");
+
+    let unsupported_dialects =
+        all_dialects_where(|d| !d.supports_leading_comma_before_table_options());
+    assert!(unsupported_dialects
+        .parse_sql_statements("CREATE TABLE foo, FALLBACK (id INT)")
+        .is_err());
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org