You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by if...@apache.org on 2026/04/29 09:50:21 UTC
(datafusion-sqlparser-rs) 01/01: [Teradata] Add `CREATE TABLE` options
This is an automated email from the ASF dual-hosted git repository.
iffyio pushed a commit to branch teradata-create-table-statement
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
commit 352ca4c6e55795ed429b9d297e248f3f702c8dbe
Author: ifeanyi <if...@validio.io>
AuthorDate: Fri Apr 17 15:41:46 2026 +0200
[Teradata] Add `CREATE TABLE` options
Adds support for some options in a Teradata `CREATE TABLE`
statement.
---
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 67aefb39..ad22d5c2 100644
--- a/src/ast/ddl.rs
+++ b/src/ast/ddl.rs
@@ -3060,6 +3060,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 {
@@ -3073,7 +3087,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 { "" },
@@ -3087,14 +3101,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}")?;
}
@@ -3379,6 +3399,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 886bea26..26de859e 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 0dc834ba..18691c03 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 668c520e..e7667743 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)
@@ -8470,11 +8472,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
@@ -8626,6 +8642,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)
@@ -8633,6 +8656,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)
@@ -8652,6 +8678,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)
@@ -8660,6 +8687,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 790bf151..52b4a841 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