diff --git a/src/ast/dml.rs b/src/ast/dml.rs index 884b378db..c0e58e21a 100644 --- a/src/ast/dml.rs +++ b/src/ast/dml.rs @@ -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")?; @@ -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(_) => (), } } diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 2bb7a161a..ff949cf53 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -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}"), } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 30e776787..1eabe41b1 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -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]); @@ -5723,15 +5734,14 @@ impl<'a> Parser<'a> { let strict = self.parse_keyword(Keyword::STRICT); - let comment = if self.parse_keyword(Keyword::COMMENT) { + // Excludes Hive dialect here since it has been handled after table column definitions. + if !dialect_of!(self is HiveDialect) && self.parse_keyword(Keyword::COMMENT) { let _ = self.consume_token(&Token::Eq); let next_token = self.next_token(); - match next_token.token { + comment = match next_token.token { Token::SingleQuotedString(str) => Some(CommentDef::WithoutEq(str)), _ => self.expected("comment", next_token)?, } - } else { - None }; // Parse optional `AS ( query )` diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index 1bb4229e1..5426dfbc4 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -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, }; @@ -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!(