Add all missing table options to be handled in any order (#1747)

Co-authored-by: Tomer Shani <tomer.shani@satoricyber.com>
This commit is contained in:
benrsatori 2025-05-02 16:16:59 +03:00 committed by GitHub
parent a464f8e8d7
commit 728645fb31
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 767 additions and 382 deletions

View file

@ -484,7 +484,7 @@ fn parse_create_table_with_options() {
columns,
partition_by,
cluster_by,
options,
table_options,
..
}) => {
assert_eq!(
@ -539,7 +539,7 @@ fn parse_create_table_with_options() {
Ident::new("userid"),
Ident::new("age"),
])),
Some(vec![
CreateTableOptions::Options(vec![
SqlOption::KeyValue {
key: Ident::new("partition_expiration_days"),
value: Expr::Value(
@ -561,7 +561,7 @@ fn parse_create_table_with_options() {
},
])
),
(partition_by, cluster_by, options)
(partition_by, cluster_by, table_options)
)
}
_ => unreachable!(),

View file

@ -219,10 +219,10 @@ fn parse_delimited_identifiers() {
#[test]
fn parse_create_table() {
clickhouse().verified_stmt(r#"CREATE TABLE "x" ("a" "int") ENGINE=MergeTree ORDER BY ("x")"#);
clickhouse().verified_stmt(r#"CREATE TABLE "x" ("a" "int") ENGINE=MergeTree ORDER BY "x""#);
clickhouse().verified_stmt(r#"CREATE TABLE "x" ("a" "int") ENGINE = MergeTree ORDER BY ("x")"#);
clickhouse().verified_stmt(r#"CREATE TABLE "x" ("a" "int") ENGINE = MergeTree ORDER BY "x""#);
clickhouse().verified_stmt(
r#"CREATE TABLE "x" ("a" "int") ENGINE=MergeTree ORDER BY "x" AS SELECT * FROM "t" WHERE true"#,
r#"CREATE TABLE "x" ("a" "int") ENGINE = MergeTree ORDER BY "x" AS SELECT * FROM "t" WHERE true"#,
);
}
@ -589,7 +589,7 @@ fn parse_clickhouse_data_types() {
#[test]
fn parse_create_table_with_nullable() {
let sql = r#"CREATE TABLE table (k UInt8, `a` Nullable(String), `b` Nullable(DateTime64(9, 'UTC')), c Nullable(DateTime64(9)), d Date32 NULL) ENGINE=MergeTree ORDER BY (`k`)"#;
let sql = r#"CREATE TABLE table (k UInt8, `a` Nullable(String), `b` Nullable(DateTime64(9, 'UTC')), c Nullable(DateTime64(9)), d Date32 NULL) ENGINE = MergeTree ORDER BY (`k`)"#;
// ClickHouse has a case-sensitive definition of data type, but canonical representation is not
let canonical_sql = sql.replace("String", "STRING");
@ -714,14 +714,14 @@ fn parse_create_table_with_nested_data_types() {
fn parse_create_table_with_primary_key() {
match clickhouse_and_generic().verified_stmt(concat!(
r#"CREATE TABLE db.table (`i` INT, `k` INT)"#,
" ENGINE=SharedMergeTree('/clickhouse/tables/{uuid}/{shard}', '{replica}')",
" ENGINE = SharedMergeTree('/clickhouse/tables/{uuid}/{shard}', '{replica}')",
" PRIMARY KEY tuple(i)",
" ORDER BY tuple(i)",
)) {
Statement::CreateTable(CreateTable {
name,
columns,
engine,
table_options,
primary_key,
order_by,
..
@ -742,16 +742,23 @@ fn parse_create_table_with_primary_key() {
],
columns
);
assert_eq!(
engine,
Some(TableEngine {
name: "SharedMergeTree".to_string(),
parameters: Some(vec![
let plain_options = match table_options {
CreateTableOptions::Plain(options) => options,
_ => unreachable!(),
};
assert!(plain_options.contains(&SqlOption::NamedParenthesizedList(
NamedParenthesizedList {
key: Ident::new("ENGINE"),
name: Some(Ident::new("SharedMergeTree")),
values: vec![
Ident::with_quote('\'', "/clickhouse/tables/{uuid}/{shard}"),
Ident::with_quote('\'', "{replica}"),
]),
})
);
]
}
)));
fn assert_function(actual: &Function, name: &str, arg: &str) -> bool {
assert_eq!(actual.name, ObjectName::from(vec![Ident::new(name)]));
assert_eq!(
@ -798,7 +805,7 @@ fn parse_create_table_with_variant_default_expressions() {
" b DATETIME EPHEMERAL now(),",
" c DATETIME EPHEMERAL,",
" d STRING ALIAS toString(c)",
") ENGINE=MergeTree"
") ENGINE = MergeTree"
);
match clickhouse_and_generic().verified_stmt(sql) {
Statement::CreateTable(CreateTable { columns, .. }) => {

View file

@ -3657,7 +3657,7 @@ fn parse_create_table() {
name,
columns,
constraints,
with_options,
table_options,
if_not_exists: false,
external: false,
file_format: None,
@ -3795,7 +3795,7 @@ fn parse_create_table() {
},
]
);
assert_eq!(with_options, vec![]);
assert_eq!(table_options, CreateTableOptions::None);
}
_ => unreachable!(),
}
@ -3840,7 +3840,7 @@ fn parse_create_table_with_constraint_characteristics() {
name,
columns,
constraints,
with_options,
table_options,
if_not_exists: false,
external: false,
file_format: None,
@ -3934,7 +3934,7 @@ fn parse_create_table_with_constraint_characteristics() {
},
]
);
assert_eq!(with_options, vec![]);
assert_eq!(table_options, CreateTableOptions::None);
}
_ => unreachable!(),
}
@ -4421,7 +4421,11 @@ fn parse_create_table_with_options() {
let sql = "CREATE TABLE t (c INT) WITH (foo = 'bar', a = 123)";
match generic.verified_stmt(sql) {
Statement::CreateTable(CreateTable { with_options, .. }) => {
Statement::CreateTable(CreateTable { table_options, .. }) => {
let with_options = match table_options {
CreateTableOptions::With(options) => options,
_ => unreachable!(),
};
assert_eq!(
vec![
SqlOption::KeyValue {
@ -4482,7 +4486,7 @@ fn parse_create_external_table() {
name,
columns,
constraints,
with_options,
table_options,
if_not_exists,
external,
file_format,
@ -4525,7 +4529,7 @@ fn parse_create_external_table() {
assert_eq!(FileFormat::TEXTFILE, file_format.unwrap());
assert_eq!("/tmp/example.csv", location.unwrap());
assert_eq!(with_options, vec![]);
assert_eq!(table_options, CreateTableOptions::None);
assert!(!if_not_exists);
}
_ => unreachable!(),
@ -4550,7 +4554,7 @@ fn parse_create_or_replace_external_table() {
name,
columns,
constraints,
with_options,
table_options,
if_not_exists,
external,
file_format,
@ -4579,7 +4583,7 @@ fn parse_create_or_replace_external_table() {
assert_eq!(FileFormat::TEXTFILE, file_format.unwrap());
assert_eq!("/tmp/example.csv", location.unwrap());
assert_eq!(with_options, vec![]);
assert_eq!(table_options, CreateTableOptions::None);
assert!(!if_not_exists);
assert!(or_replace);
}
@ -11420,7 +11424,9 @@ fn test_parse_inline_comment() {
// [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable)
match all_dialects_except(|d| d.is::<HiveDialect>()).verified_stmt(sql) {
Statement::CreateTable(CreateTable {
columns, comment, ..
columns,
table_options,
..
}) => {
assert_eq!(
columns,
@ -11434,8 +11440,10 @@ fn test_parse_inline_comment() {
}]
);
assert_eq!(
comment.unwrap(),
CommentDef::WithEq("comment with equal".to_string())
table_options,
CreateTableOptions::Plain(vec![SqlOption::Comment(CommentDef::WithEq(
"comment with equal".to_string()
))])
);
}
_ => unreachable!(),
@ -12460,21 +12468,6 @@ fn parse_select_wildcard_with_except() {
);
}
#[test]
fn parse_auto_increment_too_large() {
let dialect = GenericDialect {};
let u64_max = u64::MAX;
let sql =
format!("CREATE TABLE foo (bar INT NOT NULL AUTO_INCREMENT) AUTO_INCREMENT=1{u64_max}");
let res = Parser::new(&dialect)
.try_with_sql(&sql)
.expect("tokenize to work")
.parse_statements();
assert!(res.is_err(), "{res:?}");
}
#[test]
fn test_group_by_nothing() {
let Select { group_by, .. } = all_dialects_where(|d| d.supports_group_by_expr())

View file

@ -735,19 +735,13 @@ fn test_duckdb_union_datatype() {
storage: Default::default(),
location: Default::default()
}),
table_properties: Default::default(),
with_options: Default::default(),
file_format: Default::default(),
location: Default::default(),
query: Default::default(),
without_rowid: Default::default(),
like: Default::default(),
clone: Default::default(),
engine: Default::default(),
comment: Default::default(),
auto_increment_offset: Default::default(),
default_charset: Default::default(),
collation: Default::default(),
on_commit: Default::default(),
on_cluster: Default::default(),
primary_key: Default::default(),
@ -755,7 +749,6 @@ fn test_duckdb_union_datatype() {
partition_by: Default::default(),
cluster_by: Default::default(),
clustered_by: Default::default(),
options: Default::default(),
inherits: Default::default(),
strict: Default::default(),
copy_grants: Default::default(),
@ -772,6 +765,7 @@ fn test_duckdb_union_datatype() {
catalog: Default::default(),
catalog_sync: Default::default(),
storage_serialization_policy: Default::default(),
table_options: CreateTableOptions::None
}),
stmt
);

View file

@ -133,9 +133,7 @@ fn create_table_with_comment() {
Statement::CreateTable(CreateTable { comment, .. }) => {
assert_eq!(
comment,
Some(CommentDef::AfterColumnDefsWithoutEq(
"table comment".to_string()
))
Some(CommentDef::WithoutEq("table comment".to_string()))
)
}
_ => unreachable!(),

View file

@ -1725,7 +1725,6 @@ fn parse_create_table_with_valid_options() {
span: Span::empty(),
},
data_type: Int(None,),
options: vec![],
},
ColumnDef {
@ -1735,7 +1734,6 @@ fn parse_create_table_with_valid_options() {
span: Span::empty(),
},
data_type: Int(None,),
options: vec![],
},
],
@ -1747,19 +1745,13 @@ fn parse_create_table_with_valid_options() {
storage: None,
location: None,
},),
table_properties: vec![],
with_options,
file_format: None,
location: None,
query: None,
without_rowid: false,
like: None,
clone: None,
engine: None,
comment: None,
auto_increment_offset: None,
default_charset: None,
collation: None,
on_commit: None,
on_cluster: None,
primary_key: None,
@ -1767,7 +1759,6 @@ fn parse_create_table_with_valid_options() {
partition_by: None,
cluster_by: None,
clustered_by: None,
options: None,
inherits: None,
strict: false,
iceberg: false,
@ -1785,6 +1776,7 @@ fn parse_create_table_with_valid_options() {
catalog: None,
catalog_sync: None,
storage_serialization_policy: None,
table_options: CreateTableOptions::With(with_options)
})
);
}
@ -1918,19 +1910,13 @@ fn parse_create_table_with_identity_column() {
storage: None,
location: None,
},),
table_properties: vec![],
with_options: vec![],
file_format: None,
location: None,
query: None,
without_rowid: false,
like: None,
clone: None,
engine: None,
comment: None,
auto_increment_offset: None,
default_charset: None,
collation: None,
on_commit: None,
on_cluster: None,
primary_key: None,
@ -1938,7 +1924,6 @@ fn parse_create_table_with_identity_column() {
partition_by: None,
cluster_by: None,
clustered_by: None,
options: None,
inherits: None,
strict: false,
copy_grants: false,
@ -1955,6 +1940,7 @@ fn parse_create_table_with_identity_column() {
catalog: None,
catalog_sync: None,
storage_serialization_policy: None,
table_options: CreateTableOptions::None
}),
);
}

View file

@ -848,9 +848,23 @@ fn parse_create_table_comment() {
for sql in [without_equal, with_equal] {
match mysql().verified_stmt(sql) {
Statement::CreateTable(CreateTable { name, comment, .. }) => {
Statement::CreateTable(CreateTable {
name,
table_options,
..
}) => {
assert_eq!(name.to_string(), "foo");
assert_eq!(comment.expect("Should exist").to_string(), "baz");
let plain_options = match table_options {
CreateTableOptions::Plain(options) => options,
_ => unreachable!(),
};
let comment = match plain_options.first().unwrap() {
SqlOption::Comment(CommentDef::WithEq(c))
| SqlOption::Comment(CommentDef::WithoutEq(c)) => c,
_ => unreachable!(),
};
assert_eq!(comment, "baz");
}
_ => unreachable!(),
}
@ -859,29 +873,226 @@ fn parse_create_table_comment() {
#[test]
fn parse_create_table_auto_increment_offset() {
let canonical =
"CREATE TABLE foo (bar INT NOT NULL AUTO_INCREMENT) ENGINE=InnoDB AUTO_INCREMENT 123";
let with_equal =
"CREATE TABLE foo (bar INT NOT NULL AUTO_INCREMENT) ENGINE=InnoDB AUTO_INCREMENT=123";
let sql =
"CREATE TABLE foo (bar INT NOT NULL AUTO_INCREMENT) ENGINE = InnoDB AUTO_INCREMENT = 123";
for sql in [canonical, with_equal] {
match mysql().one_statement_parses_to(sql, canonical) {
match mysql().verified_stmt(sql) {
Statement::CreateTable(CreateTable {
name,
table_options,
..
}) => {
assert_eq!(name.to_string(), "foo");
let plain_options = match table_options {
CreateTableOptions::Plain(options) => options,
_ => unreachable!(),
};
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("AUTO_INCREMENT"),
value: Expr::Value(test_utils::number("123").with_empty_span())
}));
}
_ => unreachable!(),
}
}
#[test]
fn parse_create_table_multiple_options_order_independent() {
let sql1 = "CREATE TABLE mytable (id INT) ENGINE=InnoDB ROW_FORMAT=DYNAMIC KEY_BLOCK_SIZE=8 COMMENT='abc'";
let sql2 = "CREATE TABLE mytable (id INT) KEY_BLOCK_SIZE=8 COMMENT='abc' ENGINE=InnoDB ROW_FORMAT=DYNAMIC";
let sql3 = "CREATE TABLE mytable (id INT) ROW_FORMAT=DYNAMIC KEY_BLOCK_SIZE=8 COMMENT='abc' ENGINE=InnoDB";
for sql in [sql1, sql2, sql3] {
match mysql().parse_sql_statements(sql).unwrap().pop().unwrap() {
Statement::CreateTable(CreateTable {
name,
auto_increment_offset,
table_options,
..
}) => {
assert_eq!(name.to_string(), "foo");
assert_eq!(
auto_increment_offset.expect("Should exist").to_string(),
"123"
);
assert_eq!(name.to_string(), "mytable");
let plain_options = match table_options {
CreateTableOptions::Plain(options) => options,
_ => unreachable!(),
};
assert!(plain_options.contains(&SqlOption::NamedParenthesizedList(
NamedParenthesizedList {
key: Ident::new("ENGINE"),
name: Some(Ident::new("InnoDB")),
values: vec![]
}
)));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("KEY_BLOCK_SIZE"),
value: Expr::Value(test_utils::number("8").with_empty_span())
}));
assert!(plain_options
.contains(&SqlOption::Comment(CommentDef::WithEq("abc".to_owned()))));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("ROW_FORMAT"),
value: Expr::Identifier(Ident::new("DYNAMIC".to_owned()))
}));
}
_ => unreachable!(),
}
}
}
#[test]
fn parse_create_table_with_all_table_options() {
let sql =
"CREATE TABLE foo (bar INT NOT NULL AUTO_INCREMENT) ENGINE = InnoDB AUTO_INCREMENT = 123 DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci INSERT_METHOD = FIRST KEY_BLOCK_SIZE = 8 ROW_FORMAT = DYNAMIC DATA DIRECTORY = '/var/lib/mysql/data' INDEX DIRECTORY = '/var/lib/mysql/index' PACK_KEYS = 1 STATS_AUTO_RECALC = 1 STATS_PERSISTENT = 0 STATS_SAMPLE_PAGES = 128 DELAY_KEY_WRITE = 1 COMPRESSION = 'ZLIB' ENCRYPTION = 'Y' MAX_ROWS = 10000 MIN_ROWS = 10 AUTOEXTEND_SIZE = 64 AVG_ROW_LENGTH = 128 CHECKSUM = 1 CONNECTION = 'mysql://localhost' ENGINE_ATTRIBUTE = 'primary' PASSWORD = 'secure_password' SECONDARY_ENGINE_ATTRIBUTE = 'secondary_attr' START TRANSACTION TABLESPACE my_tablespace STORAGE DISK UNION = (table1, table2, table3)";
match mysql().verified_stmt(sql) {
Statement::CreateTable(CreateTable {
name,
table_options,
..
}) => {
assert_eq!(name, vec![Ident::new("foo".to_owned())].into());
let plain_options = match table_options {
CreateTableOptions::Plain(options) => options,
_ => unreachable!(),
};
assert!(plain_options.contains(&SqlOption::NamedParenthesizedList(
NamedParenthesizedList {
key: Ident::new("ENGINE"),
name: Some(Ident::new("InnoDB")),
values: vec![]
}
)));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("COLLATE"),
value: Expr::Identifier(Ident::new("utf8mb4_0900_ai_ci".to_owned()))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("DEFAULT CHARSET"),
value: Expr::Identifier(Ident::new("utf8mb4".to_owned()))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("AUTO_INCREMENT"),
value: Expr::value(test_utils::number("123"))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("KEY_BLOCK_SIZE"),
value: Expr::value(test_utils::number("8"))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("ROW_FORMAT"),
value: Expr::Identifier(Ident::new("DYNAMIC".to_owned()))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("PACK_KEYS"),
value: Expr::value(test_utils::number("1"))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("STATS_AUTO_RECALC"),
value: Expr::value(test_utils::number("1"))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("STATS_PERSISTENT"),
value: Expr::value(test_utils::number("0"))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("STATS_SAMPLE_PAGES"),
value: Expr::value(test_utils::number("128"))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("STATS_SAMPLE_PAGES"),
value: Expr::value(test_utils::number("128"))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("INSERT_METHOD"),
value: Expr::Identifier(Ident::new("FIRST".to_owned()))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("COMPRESSION"),
value: Expr::value(Value::SingleQuotedString("ZLIB".to_owned()))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("ENCRYPTION"),
value: Expr::value(Value::SingleQuotedString("Y".to_owned()))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("MAX_ROWS"),
value: Expr::value(test_utils::number("10000"))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("MIN_ROWS"),
value: Expr::value(test_utils::number("10"))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("AUTOEXTEND_SIZE"),
value: Expr::value(test_utils::number("64"))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("AVG_ROW_LENGTH"),
value: Expr::value(test_utils::number("128"))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("CHECKSUM"),
value: Expr::value(test_utils::number("1"))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("CONNECTION"),
value: Expr::value(Value::SingleQuotedString("mysql://localhost".to_owned()))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("ENGINE_ATTRIBUTE"),
value: Expr::value(Value::SingleQuotedString("primary".to_owned()))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("PASSWORD"),
value: Expr::value(Value::SingleQuotedString("secure_password".to_owned()))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("SECONDARY_ENGINE_ATTRIBUTE"),
value: Expr::value(Value::SingleQuotedString("secondary_attr".to_owned()))
}));
assert!(plain_options.contains(&SqlOption::Ident(Ident::new(
"START TRANSACTION".to_owned()
))));
assert!(
plain_options.contains(&SqlOption::TableSpace(TablespaceOption {
name: "my_tablespace".to_string(),
storage: Some(StorageType::Disk),
}))
);
assert!(plain_options.contains(&SqlOption::NamedParenthesizedList(
NamedParenthesizedList {
key: Ident::new("UNION"),
name: None,
values: vec![
Ident::new("table1".to_string()),
Ident::new("table2".to_string()),
Ident::new("table3".to_string())
]
}
)));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("DATA DIRECTORY"),
value: Expr::value(Value::SingleQuotedString("/var/lib/mysql/data".to_owned()))
}));
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("INDEX DIRECTORY"),
value: Expr::value(Value::SingleQuotedString("/var/lib/mysql/index".to_owned()))
}));
}
_ => unreachable!(),
}
}
#[test]
fn parse_create_table_set_enum() {
let sql = "CREATE TABLE foo (bar SET('a', 'b'), baz ENUM('a', 'b'))";
@ -916,13 +1127,12 @@ fn parse_create_table_set_enum() {
#[test]
fn parse_create_table_engine_default_charset() {
let sql = "CREATE TABLE foo (id INT(11)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3";
let sql = "CREATE TABLE foo (id INT(11)) ENGINE = InnoDB DEFAULT CHARSET = utf8mb3";
match mysql().verified_stmt(sql) {
Statement::CreateTable(CreateTable {
name,
columns,
engine,
default_charset,
table_options,
..
}) => {
assert_eq!(name.to_string(), "foo");
@ -934,14 +1144,24 @@ fn parse_create_table_engine_default_charset() {
},],
columns
);
assert_eq!(
engine,
Some(TableEngine {
name: "InnoDB".to_string(),
parameters: None
})
);
assert_eq!(default_charset, Some("utf8mb3".to_string()));
let plain_options = match table_options {
CreateTableOptions::Plain(options) => options,
_ => unreachable!(),
};
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("DEFAULT CHARSET"),
value: Expr::Identifier(Ident::new("utf8mb3".to_owned()))
}));
assert!(plain_options.contains(&SqlOption::NamedParenthesizedList(
NamedParenthesizedList {
key: Ident::new("ENGINE"),
name: Some(Ident::new("InnoDB")),
values: vec![]
}
)));
}
_ => unreachable!(),
}
@ -949,12 +1169,12 @@ fn parse_create_table_engine_default_charset() {
#[test]
fn parse_create_table_collate() {
let sql = "CREATE TABLE foo (id INT(11)) COLLATE=utf8mb4_0900_ai_ci";
let sql = "CREATE TABLE foo (id INT(11)) COLLATE = utf8mb4_0900_ai_ci";
match mysql().verified_stmt(sql) {
Statement::CreateTable(CreateTable {
name,
columns,
collation,
table_options,
..
}) => {
assert_eq!(name.to_string(), "foo");
@ -966,7 +1186,16 @@ fn parse_create_table_collate() {
},],
columns
);
assert_eq!(collation, Some("utf8mb4_0900_ai_ci".to_string()));
let plain_options = match table_options {
CreateTableOptions::Plain(options) => options,
_ => unreachable!(),
};
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("COLLATE"),
value: Expr::Identifier(Ident::new("utf8mb4_0900_ai_ci".to_owned()))
}));
}
_ => unreachable!(),
}
@ -974,16 +1203,26 @@ fn parse_create_table_collate() {
#[test]
fn parse_create_table_both_options_and_as_query() {
let sql = "CREATE TABLE foo (id INT(11)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb4_0900_ai_ci AS SELECT 1";
let sql = "CREATE TABLE foo (id INT(11)) ENGINE = InnoDB DEFAULT CHARSET = utf8mb3 COLLATE = utf8mb4_0900_ai_ci AS SELECT 1";
match mysql_and_generic().verified_stmt(sql) {
Statement::CreateTable(CreateTable {
name,
collation,
query,
table_options,
..
}) => {
assert_eq!(name.to_string(), "foo");
assert_eq!(collation, Some("utf8mb4_0900_ai_ci".to_string()));
let plain_options = match table_options {
CreateTableOptions::Plain(options) => options,
_ => unreachable!(),
};
assert!(plain_options.contains(&SqlOption::KeyValue {
key: Ident::new("COLLATE"),
value: Expr::Identifier(Ident::new("utf8mb4_0900_ai_ci".to_owned()))
}));
assert_eq!(
query.unwrap().body.as_select().unwrap().projection,
vec![SelectItem::UnnamedExpr(Expr::Value(
@ -994,7 +1233,8 @@ fn parse_create_table_both_options_and_as_query() {
_ => unreachable!(),
}
let sql = r"CREATE TABLE foo (id INT(11)) ENGINE=InnoDB AS SELECT 1 DEFAULT CHARSET=utf8mb3";
let sql =
r"CREATE TABLE foo (id INT(11)) ENGINE = InnoDB AS SELECT 1 DEFAULT CHARSET = utf8mb3";
assert!(matches!(
mysql_and_generic().parse_sql_statements(sql),
Err(ParserError::ParserError(_))

View file

@ -348,7 +348,7 @@ fn parse_create_table_with_defaults() {
name,
columns,
constraints,
with_options,
table_options,
if_not_exists: false,
external: false,
file_format: None,
@ -485,6 +485,11 @@ fn parse_create_table_with_defaults() {
]
);
assert!(constraints.is_empty());
let with_options = match table_options {
CreateTableOptions::With(options) => options,
_ => unreachable!(),
};
assert_eq!(
with_options,
vec![
@ -4668,7 +4673,6 @@ fn parse_create_table_with_alias() {
name,
columns,
constraints,
with_options: _with_options,
if_not_exists: false,
external: false,
file_format: None,
@ -5078,7 +5082,11 @@ fn parse_at_time_zone() {
fn parse_create_table_with_options() {
let sql = "CREATE TABLE t (c INT) WITH (foo = 'bar', a = 123)";
match pg().verified_stmt(sql) {
Statement::CreateTable(CreateTable { with_options, .. }) => {
Statement::CreateTable(CreateTable { table_options, .. }) => {
let with_options = match table_options {
CreateTableOptions::With(options) => options,
_ => unreachable!(),
};
assert_eq!(
vec![
SqlOption::KeyValue {
@ -5506,19 +5514,13 @@ fn parse_trigger_related_functions() {
storage: None,
location: None
}),
table_properties: vec![],
with_options: vec![],
file_format: None,
location: None,
query: None,
without_rowid: false,
like: None,
clone: None,
engine: None,
comment: None,
auto_increment_offset: None,
default_charset: None,
collation: None,
on_commit: None,
on_cluster: None,
primary_key: None,
@ -5526,7 +5528,6 @@ fn parse_trigger_related_functions() {
partition_by: None,
cluster_by: None,
clustered_by: None,
options: None,
inherits: None,
strict: false,
copy_grants: false,
@ -5543,6 +5544,7 @@ fn parse_trigger_related_functions() {
catalog: None,
catalog_sync: None,
storage_serialization_policy: None,
table_options: CreateTableOptions::None
}
);

View file

@ -470,9 +470,22 @@ fn test_snowflake_create_table_cluster_by() {
#[test]
fn test_snowflake_create_table_comment() {
match snowflake().verified_stmt("CREATE TABLE my_table (a INT) COMMENT = 'some comment'") {
Statement::CreateTable(CreateTable { name, comment, .. }) => {
Statement::CreateTable(CreateTable {
name,
table_options,
..
}) => {
assert_eq!("my_table", name.to_string());
assert_eq!("some comment", comment.unwrap().to_string());
let plain_options = match table_options {
CreateTableOptions::Plain(options) => options,
_ => unreachable!(),
};
let comment = match plain_options.first().unwrap() {
SqlOption::Comment(CommentDef::WithEq(c))
| SqlOption::Comment(CommentDef::WithoutEq(c)) => c,
_ => unreachable!(),
};
assert_eq!("some comment", comment);
}
_ => unreachable!(),
}