Skip to content

Fix Hive table comment should be after table column definitions #1413

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/ast/dml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,13 @@ impl Display for CreateTable {
// PostgreSQL allows `CREATE TABLE t ();`, but requires empty parens
write!(f, " ()")?;
}

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

// Only for SQLite
if self.without_rowid {
write!(f, " WITHOUT ROWID")?;
Expand Down Expand Up @@ -336,6 +343,8 @@ impl Display for CreateTable {
CommentDef::WithoutEq(comment) => {
write!(f, " COMMENT '{comment}'")?;
}
// For CommentDef::AfterColumnDefsWithoutEq will be displayed after column definition
CommentDef::AfterColumnDefsWithoutEq(_) => (),
}
}

Expand Down
8 changes: 7 additions & 1 deletion src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6807,12 +6807,18 @@ pub enum CommentDef {
/// Does not include `=` when printing the comment, as `COMMENT 'comment'`
WithEq(String),
WithoutEq(String),
// For Hive dialect, the table comment is after the column definitions without `=`,
// so we need to add an extra variant to allow to identify this case when displaying.
// [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable)
AfterColumnDefsWithoutEq(String),
}

impl Display for CommentDef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CommentDef::WithEq(comment) | CommentDef::WithoutEq(comment) => write!(f, "{comment}"),
CommentDef::WithEq(comment)
| CommentDef::WithoutEq(comment)
| CommentDef::AfterColumnDefsWithoutEq(comment) => write!(f, "{comment}"),
}
}
}
Expand Down
34 changes: 24 additions & 10 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5613,6 +5613,17 @@ impl<'a> Parser<'a> {

// parse optional column list (schema)
let (columns, constraints) = self.parse_columns()?;
let mut comment = if dialect_of!(self is HiveDialect)
&& self.parse_keyword(Keyword::COMMENT)
{
let next_token = self.next_token();
match next_token.token {
Token::SingleQuotedString(str) => Some(CommentDef::AfterColumnDefsWithoutEq(str)),
_ => self.expected("comment", next_token)?,
}
} else {
None
};

// SQLite supports `WITHOUT ROWID` at the end of `CREATE TABLE`
let without_rowid = self.parse_keywords(&[Keyword::WITHOUT, Keyword::ROWID]);
Expand Down Expand Up @@ -5723,16 +5734,19 @@ impl<'a> Parser<'a> {

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

let comment = if self.parse_keyword(Keyword::COMMENT) {
let _ = self.consume_token(&Token::Eq);
let next_token = self.next_token();
match next_token.token {
Token::SingleQuotedString(str) => Some(CommentDef::WithoutEq(str)),
_ => self.expected("comment", next_token)?,
}
} else {
None
};
// For Hive, the comment is after the table column definitions
if !dialect_of!(self is HiveDialect) {
comment = if self.parse_keyword(Keyword::COMMENT) {
let _ = self.consume_token(&Token::Eq);
let next_token = self.next_token();
match next_token.token {
Token::SingleQuotedString(str) => Some(CommentDef::WithoutEq(str)),
_ => self.expected("comment", next_token)?,
}
} else {
None
};
}

// Parse optional `AS ( query )`
let query = if self.parse_keyword(Keyword::AS) {
Expand Down
35 changes: 34 additions & 1 deletion tests/sqlparser_hive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
//! is also tested (on the inputs it can handle).

use sqlparser::ast::{
ClusteredBy, CreateFunctionBody, CreateFunctionUsing, CreateTable, Expr, Function,
ClusteredBy, CommentDef, CreateFunctionBody, CreateFunctionUsing, CreateTable, Expr, Function,
FunctionArgumentList, FunctionArguments, Ident, ObjectName, OneOrManyWithParens, OrderByExpr,
SelectItem, Statement, TableFactor, UnaryOperator, Use, Value,
};
Expand Down Expand Up @@ -115,6 +115,39 @@ fn create_table_like() {
hive().verified_stmt(like);
}

#[test]
fn create_table_with_comment() {
let sql = concat!(
"CREATE TABLE db.table_name (a INT, b STRING)",
" COMMENT 'table comment'",
" PARTITIONED BY (a INT, b STRING)",
" CLUSTERED BY (a, b) SORTED BY (a ASC, b DESC)",
" INTO 4 BUCKETS"
);
match hive().verified_stmt(sql) {
Statement::CreateTable(CreateTable { comment, .. }) => {
assert_eq!(
comment,
Some(CommentDef::AfterColumnDefsWithoutEq(
"table comment".to_string()
))
)
}
_ => unreachable!(),
}

// negative test case
let invalid_sql = concat!(
"CREATE TABLE db.table_name (a INT, b STRING)",
" PARTITIONED BY (a INT, b STRING)",
" COMMENT 'table comment'",
);
assert_eq!(
hive().parse_sql_statements(invalid_sql).unwrap_err(),
ParserError::ParserError("Expected: end of statement, found: COMMENT".to_string())
);
}

#[test]
fn create_table_with_clustered_by() {
let sql = concat!(
Expand Down
Loading