Repository: apache/datafusion-sqlparser-rs (rust)

Issue:
Table comment is always parsed as CommentDef::WithoutEq
Say I have the following table:

```sql
CREATE TABLE users (
  user_id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '口座内部ID',
  user_name VARCHAR(255) NOT NULL,
  PRIMARY KEY (user_id),
) COMMENT = 'users of the service';
```

And I parse and re-build it with sqlparser like this:

```rust
use sqlparser::{
    dialect,
    parser::Parser,
};

pub fn test() {
    let dialect = dialect::GenericDialect {};
    let parsed = Parser::parse_sql(&dialect, r#"
        CREATE TABLE users (
            user_id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '口座内部ID',
            user_name VARCHAR(255) NOT NULL,
            PRIMARY KEY (user_id)
        ) COMMENT = 'users of the service';
    "#);

    let binding = parsed.unwrap();
    let stmt = binding.first().unwrap();

    println!("{}", stmt.to_string());
}
```

then I got the following result:

```
CREATE TABLE users (user_id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '口座内部ID', user_name VARCHAR(255) NOT NULL, PRIMARY KEY (user_id)) COMMENT 'users of the service'
```

It looks like a table comment is always treated as `CommentDef::WithoutEq` here.

https://github.com/apache/datafusion-sqlparser-rs/blob/main/src/parser/mod.rs#L5877

Pull request:
Fix always uses CommentDef::WithoutEq while parsing the inline comment
This closes #1452.

Patch:
diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs
index e14746d..f256c9b 100644
--- a/src/dialect/snowflake.rs
+++ b/src/dialect/snowflake.rs
@@ -22,9 +22,7 @@ use crate::ast::helpers::stmt_data_loading::{
     DataLoadingOption, DataLoadingOptionType, DataLoadingOptions, StageLoadSelectItem,
     StageParamsObject,
 };
-use crate::ast::{
-    CommentDef, Ident, ObjectName, RowAccessPolicy, Statement, Tag, WrappedCollection,
-};
+use crate::ast::{Ident, ObjectName, RowAccessPolicy, Statement, Tag, WrappedCollection};
 use crate::dialect::{Dialect, Precedence};
 use crate::keywords::Keyword;
 use crate::parser::{Parser, ParserError};
@@ -210,13 +208,9 @@ pub fn parse_create_table(
                     builder = builder.copy_grants(true);
                 }
                 Keyword::COMMENT => {
-                    parser.expect_token(&Token::Eq)?;
-                    let next_token = parser.next_token();
-                    let comment = match next_token.token {
-                        Token::SingleQuotedString(str) => Some(CommentDef::WithEq(str)),
-                        _ => parser.expected("comment", next_token)?,
-                    };
-                    builder = builder.comment(comment);
+                    // Rewind the COMMENT keyword
+                    parser.prev_token();
+                    builder = builder.comment(parser.parse_optional_inline_comment()?);
                 }
                 Keyword::AS => {
                     let query = parser.parse_boxed_query()?;
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index a4445e0..028bae7 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -5871,12 +5871,9 @@ impl<'a> Parser<'a> {
 
         // 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();
-            comment = match next_token.token {
-                Token::SingleQuotedString(str) => Some(CommentDef::WithoutEq(str)),
-                _ => self.expected("comment", next_token)?,
-            }
+            // rewind the COMMENT keyword
+            self.prev_token();
+            comment = self.parse_optional_inline_comment()?
         };
 
         // Parse optional `AS ( query )`
@@ -5957,6 +5954,24 @@ impl<'a> Parser<'a> {
         })
     }
 
+    pub fn parse_optional_inline_comment(&mut self) -> Result<Option<CommentDef>, ParserError> {
+        let comment = if self.parse_keyword(Keyword::COMMENT) {
+            let has_eq = self.consume_token(&Token::Eq);
+            let next_token = self.next_token();
+            match next_token.token {
+                Token::SingleQuotedString(str) => Some(if has_eq {
+                    CommentDef::WithEq(str)
+                } else {
+                    CommentDef::WithoutEq(str)
+                }),
+                _ => self.expected("comment", next_token)?,
+            }
+        } else {
+            None
+        };
+        Ok(comment)
+    }
+
     pub fn parse_optional_procedure_parameters(
         &mut self,
     ) -> Result<Option<Vec<ProcedureParam>>, ParserError> {


Test patch:
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 2677c19..a06b47a 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -47,6 +47,7 @@ mod test_utils;
 
 #[cfg(test)]
 use pretty_assertions::assert_eq;
+use sqlparser::ast::ColumnOption::Comment;
 use sqlparser::ast::Expr::{Identifier, UnaryOp};
 use sqlparser::test_utils::all_dialects_except;
 
@@ -9841,6 +9842,37 @@ fn test_comment_hash_syntax() {
     dialects.verified_only_select_with_canonical(sql, canonical);
 }
 
+#[test]
+fn test_parse_inline_comment() {
+    let sql =
+        "CREATE TABLE t0 (id INT COMMENT 'comment without equal') COMMENT = 'comment with equal'";
+    // Hive dialect doesn't support `=` in table comment, please refer:
+    // [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, ..
+        }) => {
+            assert_eq!(
+                columns,
+                vec![ColumnDef {
+                    name: Ident::new("id".to_string()),
+                    data_type: DataType::Int(None),
+                    collation: None,
+                    options: vec![ColumnOptionDef {
+                        name: None,
+                        option: Comment("comment without equal".to_string()),
+                    }]
+                }]
+            );
+            assert_eq!(
+                comment.unwrap(),
+                CommentDef::WithEq("comment with equal".to_string())
+            );
+        }
+        _ => unreachable!(),
+    }
+}
+
 #[test]
 fn test_buffer_reuse() {
     let d = GenericDialect {};
diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs
index 87468b9..19dbda2 100644
--- a/tests/sqlparser_mysql.rs
+++ b/tests/sqlparser_mysql.rs
@@ -713,11 +713,11 @@ fn parse_create_table_primary_and_unique_key_characteristic_test() {
 
 #[test]
 fn parse_create_table_comment() {
-    let canonical = "CREATE TABLE foo (bar INT) COMMENT 'baz'";
+    let without_equal = "CREATE TABLE foo (bar INT) COMMENT 'baz'";
     let with_equal = "CREATE TABLE foo (bar INT) COMMENT = 'baz'";
 
-    for sql in [canonical, with_equal] {
-        match mysql().one_statement_parses_to(sql, canonical) {
+    for sql in [without_equal, with_equal] {
+        match mysql().verified_stmt(sql) {
             Statement::CreateTable(CreateTable { name, comment, .. }) => {
                 assert_eq!(name.to_string(), "foo");
                 assert_eq!(comment.expect("Should exist").to_string(), "baz");