Skip to content

Commit 66f5260

Browse files
committed
create table parsing - comment parsing to generic parameter parsing
1 parent 278b492 commit 66f5260

13 files changed

+97
-69
lines changed

src/ast/dml.rs

+5-15
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,10 @@ pub struct CreateTable {
160160
pub without_rowid: bool,
161161
pub like: Option<ObjectName>,
162162
pub clone: Option<ObjectName>,
163-
pub comment: Option<CommentDef>,
163+
// For Hive dialect, the table comment is after the column definitions without `=`,
164+
// so we need to add an extra variant to allow to identify this case when displaying.
165+
// [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable)
166+
pub comment_after_column_def: Option<CommentDef>,
164167
pub on_commit: Option<OnCommit>,
165168
/// ClickHouse "ON CLUSTER" clause:
166169
/// <https://clickhouse.com/docs/en/sql-reference/distributed-ddl/>
@@ -276,7 +279,7 @@ impl Display for CreateTable {
276279

277280
// Hive table comment should be after column definitions, please refer to:
278281
// [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable)
279-
if let Some(CommentDef::AfterColumnDefsWithoutEq(comment)) = &self.comment {
282+
if let Some(comment) = &self.comment_after_column_def {
280283
write!(f, " COMMENT '{comment}'")?;
281284
}
282285

@@ -384,19 +387,6 @@ impl Display for CreateTable {
384387
write!(f, " {}", display_separated(&self.plain_options, " "))?;
385388
}
386389

387-
if let Some(comment_def) = &self.comment {
388-
match comment_def {
389-
CommentDef::WithEq(comment) => {
390-
write!(f, " COMMENT = '{comment}'")?;
391-
}
392-
CommentDef::WithoutEq(comment) => {
393-
write!(f, " COMMENT '{comment}'")?;
394-
}
395-
// For CommentDef::AfterColumnDefsWithoutEq will be displayed after column definition
396-
CommentDef::AfterColumnDefsWithoutEq(_) => (),
397-
}
398-
}
399-
400390
if let Some(primary_key) = &self.primary_key {
401391
write!(f, " PRIMARY KEY {}", primary_key)?;
402392
}

src/ast/helpers/stmt_create_table.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ pub struct CreateTableBuilder {
8787
pub without_rowid: bool,
8888
pub like: Option<ObjectName>,
8989
pub clone: Option<ObjectName>,
90-
pub comment: Option<CommentDef>,
90+
pub comment_after_column_def: Option<CommentDef>,
9191
pub on_commit: Option<OnCommit>,
9292
pub on_cluster: Option<Ident>,
9393
pub primary_key: Option<Box<Expr>>,
@@ -137,7 +137,7 @@ impl CreateTableBuilder {
137137
without_rowid: false,
138138
like: None,
139139
clone: None,
140-
comment: None,
140+
comment_after_column_def: None,
141141
on_commit: None,
142142
on_cluster: None,
143143
primary_key: None,
@@ -261,8 +261,8 @@ impl CreateTableBuilder {
261261
self
262262
}
263263

264-
pub fn comment(mut self, comment: Option<CommentDef>) -> Self {
265-
self.comment = comment;
264+
pub fn comment_after_column_def(mut self, comment: Option<CommentDef>) -> Self {
265+
self.comment_after_column_def = comment;
266266
self
267267
}
268268

@@ -418,7 +418,7 @@ impl CreateTableBuilder {
418418
without_rowid: self.without_rowid,
419419
like: self.like,
420420
clone: self.clone,
421-
comment: self.comment,
421+
comment_after_column_def: self.comment_after_column_def,
422422
on_commit: self.on_commit,
423423
on_cluster: self.on_cluster,
424424
primary_key: self.primary_key,
@@ -476,7 +476,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
476476
without_rowid,
477477
like,
478478
clone,
479-
comment,
479+
comment_after_column_def: comment,
480480
on_commit,
481481
on_cluster,
482482
primary_key,
@@ -521,7 +521,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
521521
without_rowid,
522522
like,
523523
clone,
524-
comment,
524+
comment_after_column_def: comment,
525525
on_commit,
526526
on_cluster,
527527
primary_key,

src/ast/mod.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -7293,6 +7293,8 @@ pub enum SqlOption {
72937293
for_values: Vec<Expr>,
72947294
},
72957295

7296+
Comment(CommentDef),
7297+
72967298
TableSpace(TablespaceOption),
72977299

72987300
Union(Vec<Ident>),
@@ -7349,6 +7351,14 @@ impl fmt::Display for SqlOption {
73497351
)
73507352
}
73517353
SqlOption::TableEngine(table_engine) => write!(f, "ENGINE = {}", table_engine),
7354+
SqlOption::Comment(comment) => match comment {
7355+
CommentDef::WithEq(comment) => {
7356+
write!(f, "COMMENT = '{comment}'")
7357+
}
7358+
CommentDef::WithoutEq(comment) => {
7359+
write!(f, "COMMENT '{comment}'")
7360+
}
7361+
},
73527362
}
73537363
}
73547364
}
@@ -8672,18 +8682,12 @@ pub enum CommentDef {
86728682
/// Does not include `=` when printing the comment, as `COMMENT 'comment'`
86738683
WithEq(String),
86748684
WithoutEq(String),
8675-
// For Hive dialect, the table comment is after the column definitions without `=`,
8676-
// so we need to add an extra variant to allow to identify this case when displaying.
8677-
// [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable)
8678-
AfterColumnDefsWithoutEq(String),
86798685
}
86808686

86818687
impl Display for CommentDef {
86828688
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86838689
match self {
8684-
CommentDef::WithEq(comment)
8685-
| CommentDef::WithoutEq(comment)
8686-
| CommentDef::AfterColumnDefsWithoutEq(comment) => write!(f, "{comment}"),
8690+
CommentDef::WithEq(comment) | CommentDef::WithoutEq(comment) => write!(f, "{comment}"),
86878691
}
86888692
}
86898693
}

src/ast/spans.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ impl Spanned for CreateTable {
566566
without_rowid: _, // bool
567567
like,
568568
clone,
569-
comment: _, // todo, no span
569+
comment_after_column_def: _, // todo, no span
570570
on_commit: _,
571571
on_cluster: _, // todo, clickhouse specific
572572
primary_key: _, // todo, clickhouse specific
@@ -973,6 +973,7 @@ impl Spanned for SqlOption {
973973
SqlOption::Union(idents) => union_spans(idents.iter().map(|i| i.span)),
974974
SqlOption::TableSpace(_) => Span::empty(),
975975
SqlOption::TableEngine(_) => Span::empty(),
976+
SqlOption::Comment(_) => Span::empty(),
976977
}
977978
}
978979
}

src/dialect/snowflake.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ use crate::ast::helpers::stmt_data_loading::{
2525
use crate::ast::{
2626
ColumnOption, ColumnPolicy, ColumnPolicyProperty, CopyIntoSnowflakeKind, Ident,
2727
IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind,
28-
IdentityPropertyOrder, ObjectName, RowAccessPolicy, ShowObjects, Statement, TagsColumnOption,
29-
WrappedCollection,
28+
IdentityPropertyOrder, ObjectName, RowAccessPolicy, ShowObjects, SqlOption, Statement,
29+
TagsColumnOption, WrappedCollection,
3030
};
3131
use crate::dialect::{Dialect, Precedence};
3232
use crate::keywords::Keyword;
@@ -411,6 +411,8 @@ pub fn parse_create_table(
411411
// "CREATE TABLE x COPY GRANTS (c INT)" and "CREATE TABLE x (c INT) COPY GRANTS" are both
412412
// accepted by Snowflake
413413

414+
let mut plain_options = vec![];
415+
414416
loop {
415417
let next_token = parser.next_token();
416418
match &next_token.token {
@@ -422,7 +424,9 @@ pub fn parse_create_table(
422424
Keyword::COMMENT => {
423425
// Rewind the COMMENT keyword
424426
parser.prev_token();
425-
builder = builder.comment(parser.parse_optional_inline_comment()?);
427+
if let Some(comment_def) = parser.parse_optional_inline_comment()? {
428+
plain_options.push(SqlOption::Comment(comment_def))
429+
}
426430
}
427431
Keyword::AS => {
428432
let query = parser.parse_query()?;
@@ -584,6 +588,8 @@ pub fn parse_create_table(
584588
}
585589
}
586590

591+
builder = builder.plain_options(plain_options);
592+
587593
if iceberg && builder.base_location.is_none() {
588594
return Err(ParserError::ParserError(
589595
"BASE_LOCATION is required for ICEBERG tables".to_string(),

src/parser/mod.rs

+25-21
Original file line numberDiff line numberDiff line change
@@ -6878,17 +6878,16 @@ impl<'a> Parser<'a> {
68786878

68796879
// parse optional column list (schema)
68806880
let (columns, constraints) = self.parse_columns()?;
6881-
let mut comment = if dialect_of!(self is HiveDialect)
6882-
&& self.parse_keyword(Keyword::COMMENT)
6883-
{
6884-
let next_token = self.next_token();
6885-
match next_token.token {
6886-
Token::SingleQuotedString(str) => Some(CommentDef::AfterColumnDefsWithoutEq(str)),
6887-
_ => self.expected("comment", next_token)?,
6888-
}
6889-
} else {
6890-
None
6891-
};
6881+
let comment_after_column_def =
6882+
if dialect_of!(self is HiveDialect) && self.parse_keyword(Keyword::COMMENT) {
6883+
let next_token = self.next_token();
6884+
match next_token.token {
6885+
Token::SingleQuotedString(str) => Some(CommentDef::WithoutEq(str)),
6886+
_ => self.expected("comment", next_token)?,
6887+
}
6888+
} else {
6889+
None
6890+
};
68926891

68936892
// SQLite supports `WITHOUT ROWID` at the end of `CREATE TABLE`
68946893
let without_rowid = self.parse_keywords(&[Keyword::WITHOUT, Keyword::ROWID]);
@@ -6936,13 +6935,6 @@ impl<'a> Parser<'a> {
69366935

69376936
let strict = self.parse_keyword(Keyword::STRICT);
69386937

6939-
// Excludes Hive dialect here since it has been handled after table column definitions.
6940-
if !dialect_of!(self is HiveDialect) && self.parse_keyword(Keyword::COMMENT) {
6941-
// rewind the COMMENT keyword
6942-
self.prev_token();
6943-
comment = self.parse_optional_inline_comment()?
6944-
};
6945-
69466938
// Parse optional `AS ( query )`
69476939
let query = if self.parse_keyword(Keyword::AS) {
69486940
Some(self.parse_query()?)
@@ -6971,7 +6963,7 @@ impl<'a> Parser<'a> {
69716963
.without_rowid(without_rowid)
69726964
.like(like)
69736965
.clone_clause(clone)
6974-
.comment(comment)
6966+
.comment_after_column_def(comment_after_column_def)
69756967
.order_by(order_by)
69766968
.on_commit(on_commit)
69776969
.on_cluster(on_cluster)
@@ -7032,7 +7024,11 @@ impl<'a> Parser<'a> {
70327024
};
70337025
}
70347026

7035-
let plain_options = self.parse_plain_options()?;
7027+
let plain_options = if dialect_of!(self is HiveDialect) {
7028+
vec![]
7029+
} else {
7030+
self.parse_plain_options()?
7031+
};
70367032

70377033
Ok(CreateTableConfiguration {
70387034
partition_by,
@@ -7055,11 +7051,19 @@ impl<'a> Parser<'a> {
70557051
Keyword::CHARSET,
70567052
Keyword::COLLATE,
70577053
Keyword::INSERT_METHOD,
7054+
Keyword::COMMENT,
70587055
]) {
7059-
let _ = self.consume_token(&Token::Eq);
7056+
let has_eq = self.consume_token(&Token::Eq);
70607057
let value = self.next_token();
70617058

70627059
match (keyword, value.token) {
7060+
(Keyword::COMMENT, Token::SingleQuotedString(s)) => {
7061+
let comment = match has_eq {
7062+
true => CommentDef::WithEq(s),
7063+
false => CommentDef::WithoutEq(s),
7064+
};
7065+
return Ok(Some(SqlOption::Comment(comment)));
7066+
}
70637067
(Keyword::AUTO_INCREMENT, Token::Number(n, l)) => {
70647068
// validation
70657069
let _ = Some(Self::parse::<u32>(n.clone(), value.span.start)?);

tests/sqlparser_common.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -11369,7 +11369,9 @@ fn test_parse_inline_comment() {
1136911369
// [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable)
1137011370
match all_dialects_except(|d| d.is::<HiveDialect>()).verified_stmt(sql) {
1137111371
Statement::CreateTable(CreateTable {
11372-
columns, comment, ..
11372+
columns,
11373+
plain_options,
11374+
..
1137311375
}) => {
1137411376
assert_eq!(
1137511377
columns,
@@ -11383,8 +11385,10 @@ fn test_parse_inline_comment() {
1138311385
}]
1138411386
);
1138511387
assert_eq!(
11386-
comment.unwrap(),
11387-
CommentDef::WithEq("comment with equal".to_string())
11388+
plain_options,
11389+
vec![SqlOption::Comment(CommentDef::WithEq(
11390+
"comment with equal".to_string()
11391+
))]
1138811392
);
1138911393
}
1139011394
_ => unreachable!(),

tests/sqlparser_duckdb.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,7 @@ fn test_duckdb_union_datatype() {
717717
without_rowid: Default::default(),
718718
like: Default::default(),
719719
clone: Default::default(),
720-
comment: Default::default(),
720+
comment_after_column_def: Default::default(),
721721
on_commit: Default::default(),
722722
on_cluster: Default::default(),
723723
primary_key: Default::default(),

tests/sqlparser_hive.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,13 @@ fn create_table_with_comment() {
130130
" INTO 4 BUCKETS"
131131
);
132132
match hive().verified_stmt(sql) {
133-
Statement::CreateTable(CreateTable { comment, .. }) => {
133+
Statement::CreateTable(CreateTable {
134+
comment_after_column_def: comment,
135+
..
136+
}) => {
134137
assert_eq!(
135138
comment,
136-
Some(CommentDef::AfterColumnDefsWithoutEq(
137-
"table comment".to_string()
138-
))
139+
Some(CommentDef::WithoutEq("table comment".to_string()))
139140
)
140141
}
141142
_ => unreachable!(),

tests/sqlparser_mssql.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1581,7 +1581,7 @@ fn parse_create_table_with_valid_options() {
15811581
without_rowid: false,
15821582
like: None,
15831583
clone: None,
1584-
comment: None,
1584+
comment_after_column_def: None,
15851585
on_commit: None,
15861586
on_cluster: None,
15871587
primary_key: None,
@@ -1748,7 +1748,7 @@ fn parse_create_table_with_identity_column() {
17481748
without_rowid: false,
17491749
like: None,
17501750
clone: None,
1751-
comment: None,
1751+
comment_after_column_def: None,
17521752
on_commit: None,
17531753
on_cluster: None,
17541754
primary_key: None,

tests/sqlparser_mysql.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -848,9 +848,18 @@ fn parse_create_table_comment() {
848848

849849
for sql in [without_equal, with_equal] {
850850
match mysql().verified_stmt(sql) {
851-
Statement::CreateTable(CreateTable { name, comment, .. }) => {
851+
Statement::CreateTable(CreateTable {
852+
name,
853+
plain_options,
854+
..
855+
}) => {
852856
assert_eq!(name.to_string(), "foo");
853-
assert_eq!(comment.expect("Should exist").to_string(), "baz");
857+
let comment = match plain_options.get(0).unwrap() {
858+
SqlOption::Comment(CommentDef::WithEq(c))
859+
| SqlOption::Comment(CommentDef::WithoutEq(c)) => c,
860+
_ => unreachable!(),
861+
};
862+
assert_eq!(comment, "baz");
854863
}
855864
_ => unreachable!(),
856865
}

tests/sqlparser_postgres.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5413,7 +5413,7 @@ fn parse_trigger_related_functions() {
54135413
without_rowid: false,
54145414
like: None,
54155415
clone: None,
5416-
comment: None,
5416+
comment_after_column_def: None,
54175417
on_commit: None,
54185418
on_cluster: None,
54195419
primary_key: None,

0 commit comments

Comments
 (0)