Skip to content

Commit 095de3e

Browse files
git-hulkayman-sigma
authored andcommitted
Fix Hive table comment should be after table column definitions (apache#1413)
1 parent 6ce98c4 commit 095de3e

File tree

4 files changed

+64
-6
lines changed

4 files changed

+64
-6
lines changed

src/ast/dml.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,13 @@ impl Display for CreateTable {
226226
// PostgreSQL allows `CREATE TABLE t ();`, but requires empty parens
227227
write!(f, " ()")?;
228228
}
229+
230+
// Hive table comment should be after column definitions, please refer to:
231+
// [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable)
232+
if let Some(CommentDef::AfterColumnDefsWithoutEq(comment)) = &self.comment {
233+
write!(f, " COMMENT '{comment}'")?;
234+
}
235+
229236
// Only for SQLite
230237
if self.without_rowid {
231238
write!(f, " WITHOUT ROWID")?;
@@ -336,6 +343,8 @@ impl Display for CreateTable {
336343
CommentDef::WithoutEq(comment) => {
337344
write!(f, " COMMENT '{comment}'")?;
338345
}
346+
// For CommentDef::AfterColumnDefsWithoutEq will be displayed after column definition
347+
CommentDef::AfterColumnDefsWithoutEq(_) => (),
339348
}
340349
}
341350

src/ast/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6828,12 +6828,18 @@ pub enum CommentDef {
68286828
/// Does not include `=` when printing the comment, as `COMMENT 'comment'`
68296829
WithEq(String),
68306830
WithoutEq(String),
6831+
// For Hive dialect, the table comment is after the column definitions without `=`,
6832+
// so we need to add an extra variant to allow to identify this case when displaying.
6833+
// [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable)
6834+
AfterColumnDefsWithoutEq(String),
68316835
}
68326836

68336837
impl Display for CommentDef {
68346838
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68356839
match self {
6836-
CommentDef::WithEq(comment) | CommentDef::WithoutEq(comment) => write!(f, "{comment}"),
6840+
CommentDef::WithEq(comment)
6841+
| CommentDef::WithoutEq(comment)
6842+
| CommentDef::AfterColumnDefsWithoutEq(comment) => write!(f, "{comment}"),
68376843
}
68386844
}
68396845
}

src/parser/mod.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5612,6 +5612,17 @@ impl<'a> Parser<'a> {
56125612

56135613
// parse optional column list (schema)
56145614
let (columns, constraints) = self.parse_columns()?;
5615+
let mut comment = if dialect_of!(self is HiveDialect)
5616+
&& self.parse_keyword(Keyword::COMMENT)
5617+
{
5618+
let next_token = self.next_token();
5619+
match next_token.token {
5620+
Token::SingleQuotedString(str) => Some(CommentDef::AfterColumnDefsWithoutEq(str)),
5621+
_ => self.expected("comment", next_token)?,
5622+
}
5623+
} else {
5624+
None
5625+
};
56155626

56165627
// SQLite supports `WITHOUT ROWID` at the end of `CREATE TABLE`
56175628
let without_rowid = self.parse_keywords(&[Keyword::WITHOUT, Keyword::ROWID]);
@@ -5722,15 +5733,14 @@ impl<'a> Parser<'a> {
57225733

57235734
let strict = self.parse_keyword(Keyword::STRICT);
57245735

5725-
let comment = if self.parse_keyword(Keyword::COMMENT) {
5736+
// Excludes Hive dialect here since it has been handled after table column definitions.
5737+
if !dialect_of!(self is HiveDialect) && self.parse_keyword(Keyword::COMMENT) {
57265738
let _ = self.consume_token(&Token::Eq);
57275739
let next_token = self.next_token();
5728-
match next_token.token {
5740+
comment = match next_token.token {
57295741
Token::SingleQuotedString(str) => Some(CommentDef::WithoutEq(str)),
57305742
_ => self.expected("comment", next_token)?,
57315743
}
5732-
} else {
5733-
None
57345744
};
57355745

57365746
// Parse optional `AS ( query )`

tests/sqlparser_hive.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
//! is also tested (on the inputs it can handle).
1717
1818
use sqlparser::ast::{
19-
ClusteredBy, CreateFunctionBody, CreateFunctionUsing, CreateTable, Expr, Function,
19+
ClusteredBy, CommentDef, CreateFunctionBody, CreateFunctionUsing, CreateTable, Expr, Function,
2020
FunctionArgumentList, FunctionArguments, Ident, ObjectName, OneOrManyWithParens, OrderByExpr,
2121
SelectItem, Statement, TableFactor, UnaryOperator, Use, Value,
2222
};
@@ -115,6 +115,39 @@ fn create_table_like() {
115115
hive().verified_stmt(like);
116116
}
117117

118+
#[test]
119+
fn create_table_with_comment() {
120+
let sql = concat!(
121+
"CREATE TABLE db.table_name (a INT, b STRING)",
122+
" COMMENT 'table comment'",
123+
" PARTITIONED BY (a INT, b STRING)",
124+
" CLUSTERED BY (a, b) SORTED BY (a ASC, b DESC)",
125+
" INTO 4 BUCKETS"
126+
);
127+
match hive().verified_stmt(sql) {
128+
Statement::CreateTable(CreateTable { comment, .. }) => {
129+
assert_eq!(
130+
comment,
131+
Some(CommentDef::AfterColumnDefsWithoutEq(
132+
"table comment".to_string()
133+
))
134+
)
135+
}
136+
_ => unreachable!(),
137+
}
138+
139+
// negative test case
140+
let invalid_sql = concat!(
141+
"CREATE TABLE db.table_name (a INT, b STRING)",
142+
" PARTITIONED BY (a INT, b STRING)",
143+
" COMMENT 'table comment'",
144+
);
145+
assert_eq!(
146+
hive().parse_sql_statements(invalid_sql).unwrap_err(),
147+
ParserError::ParserError("Expected: end of statement, found: COMMENT".to_string())
148+
);
149+
}
150+
118151
#[test]
119152
fn create_table_with_clustered_by() {
120153
let sql = concat!(

0 commit comments

Comments
 (0)