Repository: AfterShip/clickhouse-sql-parser
Pull request #104 (2024-10-08 18:47:40)

Adding support for comment in table/view
Adding Table/View Comment Support as per document https://clickhouse.com/docs/en/sql-reference/statements/create/table
https://github.com/AfterShip/clickhouse-sql-parser/issues/105
Example:
```
CREATE TABLE IF NOT EXISTS test.events_local (
    f0 String,
    f1 String CODEC(ZSTD(1)),
    f2 VARCHAR(255),
    f3 Datetime,
    f4 Datetime,
    f5 Map(String,String),
    f6 String,
    f8 Datetime DEFAULT now(),
    f9 String MATERIALIZED toString(f7['f70']),
    f10 String ALIAS f11,
) ENGINE = MergeTree
PRIMARY KEY (f0, f1, f2)
PARTITION BY toYYYYMMDD(f3)
TTL f3 + INTERVAL 6 MONTH
ORDER BY (f1,f2,f3)
COMMENT 'Comment for table';




coveralls: ## Pull Request Test Coverage Report for [Build 11241780952](https://coveralls.io/builds/70231988)


### Details

* **12** of **14**   **(85.71%)**  changed or added relevant lines in **3** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.04%**) to **59.666%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [parser/parser_table.go](https://coveralls.io/builds/70231988/source?filename=parser%2Fparser_table.go#L172) | 3 | 5 | 60.0%
<!-- | **Total:** | **12** | **14** | **85.71%** | -->


|  Totals | [![Coverage Status](https://coveralls.io/builds/70231988/badge)](https://coveralls.io/builds/70231988) |
| :-- | --: |
| Change from base [Build 11227639957](https://coveralls.io/builds/70214171): |  0.04% |
| Covered Lines: | 6253 |
| Relevant Lines: | 10480 |

---
##### 💛  - [Coveralls](https://coveralls.io)

git-hulk: Generally looks good, just one comment inline. Thank you!

Patch:
diff --git a/parser/ast.go b/parser/ast.go
index 24ebbe4..41cac1a 100644
--- a/parser/ast.go
+++ b/parser/ast.go
@@ -1490,6 +1490,7 @@ type CreateTable struct {
 	Engine       *EngineExpr
 	SubQuery     *SubQuery
 	HasTemporary bool
+	Comment      *StringLiteral
 }
 
 func (c *CreateTable) Pos() Pos {
@@ -1534,6 +1535,10 @@ func (c *CreateTable) String() string {
 		builder.WriteString(" AS ")
 		builder.WriteString(c.SubQuery.String())
 	}
+	if c.Comment != nil {
+		builder.WriteString(" COMMENT ")
+		builder.WriteString(c.Comment.String())
+	}
 	return builder.String()
 }
 
@@ -1578,6 +1583,7 @@ type CreateMaterializedView struct {
 	Destination  *DestinationClause
 	SubQuery     *SubQuery
 	Populate     bool
+	Comment      *StringLiteral
 }
 
 func (c *CreateMaterializedView) Pos() Pos {
@@ -1621,6 +1627,11 @@ func (c *CreateMaterializedView) String() string {
 		builder.WriteString(" AS ")
 		builder.WriteString(c.SubQuery.String())
 	}
+
+	if c.Comment != nil {
+		builder.WriteString(" COMMENT ")
+		builder.WriteString(c.Comment.String())
+	}
 	return builder.String()
 }
 
diff --git a/parser/parser_common.go b/parser/parser_common.go
index cf486d7..5f21e30 100644
--- a/parser/parser_common.go
+++ b/parser/parser_common.go
@@ -137,6 +137,13 @@ func (p *Parser) tryParseUUID() (*UUID, error) {
 	return p.parseUUID()
 }
 
+func (p *Parser) tryParseComment() (*StringLiteral, error) {
+	if p.tryConsumeKeyword(KeywordComment) == nil {
+		return nil, nil
+	}
+	return p.parseString(p.Pos())
+}
+
 func (p *Parser) tryParseIfExists() (bool, error) {
 	if p.tryConsumeKeyword(KeywordIf) == nil {
 		return false, nil
diff --git a/parser/parser_table.go b/parser/parser_table.go
index c00937e..37bc521 100644
--- a/parser/parser_table.go
+++ b/parser/parser_table.go
@@ -166,6 +166,12 @@ func (p *Parser) parseCreateTable(pos Pos) (*CreateTable, error) {
 		createTable.SubQuery = subQuery
 		createTable.StatementEnd = subQuery.End()
 	}
+
+	comment, err := p.tryParseComment()
+	if err != nil {
+		return nil, err
+	}
+	createTable.Comment = comment
 	return createTable, nil
 }
 
diff --git a/parser/parser_view.go b/parser/parser_view.go
index 7b3f295..fbc6af0 100644
--- a/parser/parser_view.go
+++ b/parser/parser_view.go
@@ -69,6 +69,12 @@ func (p *Parser) parseCreateMaterializedView(pos Pos) (*CreateMaterializedView,
 		createMaterializedView.SubQuery = subQuery
 		createMaterializedView.StatementEnd = subQuery.End()
 	}
+
+	comment, err := p.tryParseComment()
+	if err != nil {
+		return nil, err
+	}
+	createMaterializedView.Comment = comment
 	return createMaterializedView, nil
 }
 


Test patch:
diff --git a/parser/testdata/ddl/create_materialized_view_basic.sql b/parser/testdata/ddl/create_materialized_view_basic.sql
index 4437911..3893e70 100644
--- a/parser/testdata/ddl/create_materialized_view_basic.sql
+++ b/parser/testdata/ddl/create_materialized_view_basic.sql
@@ -18,4 +18,5 @@ SELECT f1,
 FROM
     infra_bm.table_name1
 WHERE
-    infra_bm.table_name1.event = 'test-event';
\ No newline at end of file
+    infra_bm.table_name1.event = 'test-event'
+COMMENT 'Comment for table';
\ No newline at end of file
diff --git a/parser/testdata/ddl/create_table_basic.sql b/parser/testdata/ddl/create_table_basic.sql
index 611083b..5969526 100644
--- a/parser/testdata/ddl/create_table_basic.sql
+++ b/parser/testdata/ddl/create_table_basic.sql
@@ -26,4 +26,5 @@ CREATE TABLE IF NOT EXISTS test.events_local (
 PRIMARY KEY (f0, f1, f2)
 PARTITION BY toYYYYMMDD(f3)
 TTL f3 + INTERVAL 6 MONTH
-ORDER BY (f1,f2,f3)
\ No newline at end of file
+ORDER BY (f1,f2,f3)
+COMMENT 'Comment for table';
\ No newline at end of file
diff --git a/parser/testdata/ddl/format/create_materialized_view_basic.sql b/parser/testdata/ddl/format/create_materialized_view_basic.sql
index 50721ab..005dc68 100644
--- a/parser/testdata/ddl/format/create_materialized_view_basic.sql
+++ b/parser/testdata/ddl/format/create_materialized_view_basic.sql
@@ -19,7 +19,8 @@ SELECT f1,
 FROM
     infra_bm.table_name1
 WHERE
-    infra_bm.table_name1.event = 'test-event';
+    infra_bm.table_name1.event = 'test-event'
+COMMENT 'Comment for table';
 
 -- Format SQL:
-CREATE MATERIALIZED VIEW infra_bm.view_name ON CLUSTER 'default_cluster' TO infra_bm.table_name (`f1` DateTime64(3), `f2` String, `f3` String, `f4` String, `f5` String, `f6` Int64) AS SELECT f1, f2, visitParamExtractString(properties, 'f3') AS f3, visitParamExtractString(properties, 'f4') AS f4, visitParamExtractString(properties, 'f5') AS f5, visitParamExtractInt(properties, 'f6') AS f6 FROM infra_bm.table_name1 WHERE infra_bm.table_name1.event = 'test-event';
+CREATE MATERIALIZED VIEW infra_bm.view_name ON CLUSTER 'default_cluster' TO infra_bm.table_name (`f1` DateTime64(3), `f2` String, `f3` String, `f4` String, `f5` String, `f6` Int64) AS SELECT f1, f2, visitParamExtractString(properties, 'f3') AS f3, visitParamExtractString(properties, 'f4') AS f4, visitParamExtractString(properties, 'f5') AS f5, visitParamExtractInt(properties, 'f6') AS f6 FROM infra_bm.table_name1 WHERE infra_bm.table_name1.event = 'test-event' COMMENT 'Comment for table';
diff --git a/parser/testdata/ddl/format/create_table_basic.sql b/parser/testdata/ddl/format/create_table_basic.sql
index 319ab9a..3263d6c 100644
--- a/parser/testdata/ddl/format/create_table_basic.sql
+++ b/parser/testdata/ddl/format/create_table_basic.sql
@@ -28,6 +28,7 @@ PRIMARY KEY (f0, f1, f2)
 PARTITION BY toYYYYMMDD(f3)
 TTL f3 + INTERVAL 6 MONTH
 ORDER BY (f1,f2,f3)
+COMMENT 'Comment for table';
 
 -- Format SQL:
-CREATE TABLE IF NOT EXISTS test.events_local (f0 String, f1 String CODEC(ZSTD(1)), f2 VARCHAR(255), f3 Datetime, f4 Datetime, f5 Map(String, String), f6 String, f7 Nested(f70 UInt32, f71 UInt32, f72 DateTime, f73 Int64, f74 Int64, f75 String), f8 Datetime DEFAULT now(), f9 String MATERIALIZED toString(f7['f70']), f10 String ALIAS f11) ENGINE = MergeTree PRIMARY KEY (f0, f1, f2) PARTITION BY toYYYYMMDD(f3) TTL f3 + INTERVAL 6 MONTH ORDER BY (f1, f2, f3);
+CREATE TABLE IF NOT EXISTS test.events_local (f0 String, f1 String CODEC(ZSTD(1)), f2 VARCHAR(255), f3 Datetime, f4 Datetime, f5 Map(String, String), f6 String, f7 Nested(f70 UInt32, f71 UInt32, f72 DateTime, f73 Int64, f74 Int64, f75 String), f8 Datetime DEFAULT now(), f9 String MATERIALIZED toString(f7['f70']), f10 String ALIAS f11) ENGINE = MergeTree PRIMARY KEY (f0, f1, f2) PARTITION BY toYYYYMMDD(f3) TTL f3 + INTERVAL 6 MONTH ORDER BY (f1, f2, f3) COMMENT 'Comment for table';
diff --git a/parser/testdata/ddl/output/attach_table_basic.sql.golden.json b/parser/testdata/ddl/output/attach_table_basic.sql.golden.json
index 0936734..b5ae026 100644
--- a/parser/testdata/ddl/output/attach_table_basic.sql.golden.json
+++ b/parser/testdata/ddl/output/attach_table_basic.sql.golden.json
@@ -476,6 +476,7 @@
       }
     },
     "SubQuery": null,
-    "HasTemporary": false
+    "HasTemporary": false,
+    "Comment": null
   }
 ]
\ No newline at end of file
diff --git a/parser/testdata/ddl/output/bug_001.sql.golden.json b/parser/testdata/ddl/output/bug_001.sql.golden.json
index ef4a635..49ac5f1 100644
--- a/parser/testdata/ddl/output/bug_001.sql.golden.json
+++ b/parser/testdata/ddl/output/bug_001.sql.golden.json
@@ -564,6 +564,7 @@
         "Except": null
       }
     },
-    "Populate": false
+    "Populate": false,
+    "Comment": null
   }
 ]
\ No newline at end of file
diff --git a/parser/testdata/ddl/output/create_distributed_table.sql.golden.json b/parser/testdata/ddl/output/create_distributed_table.sql.golden.json
index 37c4d3a..0230585 100644
--- a/parser/testdata/ddl/output/create_distributed_table.sql.golden.json
+++ b/parser/testdata/ddl/output/create_distributed_table.sql.golden.json
@@ -139,6 +139,7 @@
       "OrderBy": null
     },
     "SubQuery": null,
-    "HasTemporary": false
+    "HasTemporary": false,
+    "Comment": null
   }
 ]
\ No newline at end of file
diff --git a/parser/testdata/ddl/output/create_materialized_view_basic.sql.golden.json b/parser/testdata/ddl/output/create_materialized_view_basic.sql.golden.json
index 69c32d2..a6cc772 100644
--- a/parser/testdata/ddl/output/create_materialized_view_basic.sql.golden.json
+++ b/parser/testdata/ddl/output/create_materialized_view_basic.sql.golden.json
@@ -531,6 +531,11 @@
         "Except": null
       }
     },
-    "Populate": false
+    "Populate": false,
+    "Comment": {
+      "LiteralPos": 548,
+      "LiteralEnd": 565,
+      "Literal": "Comment for table"
+    }
   }
 ]
\ No newline at end of file
diff --git a/parser/testdata/ddl/output/create_materialized_view_with_empty_table_schema.sql.golden.json b/parser/testdata/ddl/output/create_materialized_view_with_empty_table_schema.sql.golden.json
index ffc7715..ac35e60 100644
--- a/parser/testdata/ddl/output/create_materialized_view_with_empty_table_schema.sql.golden.json
+++ b/parser/testdata/ddl/output/create_materialized_view_with_empty_table_schema.sql.golden.json
@@ -541,6 +541,7 @@
         "Except": null
       }
     },
-    "Populate": true
+    "Populate": true,
+    "Comment": null
   }
 ]
\ No newline at end of file
diff --git a/parser/testdata/ddl/output/create_table_basic.sql.golden.json b/parser/testdata/ddl/output/create_table_basic.sql.golden.json
index 4718328..6cbb3b5 100644
--- a/parser/testdata/ddl/output/create_table_basic.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_basic.sql.golden.json
@@ -838,6 +838,11 @@
       }
     },
     "SubQuery": null,
-    "HasTemporary": false
+    "HasTemporary": false,
+    "Comment": {
+      "LiteralPos": 687,
+      "LiteralEnd": 704,
+      "Literal": "Comment for table"
+    }
   }
 ]
\ No newline at end of file
diff --git a/parser/testdata/ddl/output/create_table_with_codec_delta.sql.golden.json b/parser/testdata/ddl/output/create_table_with_codec_delta.sql.golden.json
index df3adfc..8ba0468 100644
--- a/parser/testdata/ddl/output/create_table_with_codec_delta.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_with_codec_delta.sql.golden.json
@@ -763,6 +763,7 @@
       }
     },
     "SubQuery": null,
-    "HasTemporary": false
+    "HasTemporary": false,
+    "Comment": null
   }
 ]
\ No newline at end of file
diff --git a/parser/testdata/ddl/output/create_table_with_index.sql.golden.json b/parser/testdata/ddl/output/create_table_with_index.sql.golden.json
index 0a2f0aa..816f63a 100644
--- a/parser/testdata/ddl/output/create_table_with_index.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_with_index.sql.golden.json
@@ -817,6 +817,7 @@
       }
     },
     "SubQuery": null,
-    "HasTemporary": false
+    "HasTemporary": false,
+    "Comment": null
   }
 ]
\ No newline at end of file
diff --git a/parser/testdata/ddl/output/create_table_with_keyword_partition_by.sql.golden.json b/parser/testdata/ddl/output/create_table_with_keyword_partition_by.sql.golden.json
index 6ae223f..5201d5e 100644
--- a/parser/testdata/ddl/output/create_table_with_keyword_partition_by.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_with_keyword_partition_by.sql.golden.json
@@ -244,6 +244,7 @@
       }
     },
     "SubQuery": null,
-    "HasTemporary": false
+    "HasTemporary": false,
+    "Comment": null
   }
 ]
\ No newline at end of file
diff --git a/parser/testdata/ddl/output/create_table_with_nullable.sql.golden.json b/parser/testdata/ddl/output/create_table_with_nullable.sql.golden.json
index ab339d0..5ca1b1a 100644
--- a/parser/testdata/ddl/output/create_table_with_nullable.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_with_nullable.sql.golden.json
@@ -422,6 +422,7 @@
       }
     },
     "SubQuery": null,
-    "HasTemporary": false
+    "HasTemporary": false,
+    "Comment": null
   }
 ]
\ No newline at end of file
diff --git a/parser/testdata/ddl/output/create_table_with_on_clsuter.sql.golden.json b/parser/testdata/ddl/output/create_table_with_on_clsuter.sql.golden.json
index 0936734..b5ae026 100644
--- a/parser/testdata/ddl/output/create_table_with_on_clsuter.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_with_on_clsuter.sql.golden.json
@@ -476,6 +476,7 @@
       }
     },
     "SubQuery": null,
-    "HasTemporary": false
+    "HasTemporary": false,
+    "Comment": null
   }
 ]
\ No newline at end of file
diff --git a/parser/testdata/ddl/output/create_table_with_sample_by.sql.golden.json b/parser/testdata/ddl/output/create_table_with_sample_by.sql.golden.json
index c46b9a1..2df3fb1 100644
--- a/parser/testdata/ddl/output/create_table_with_sample_by.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_with_sample_by.sql.golden.json
@@ -304,6 +304,7 @@
       }
     },
     "SubQuery": null,
-    "HasTemporary": false
+    "HasTemporary": false,
+    "Comment": null
   }
 ]
\ No newline at end of file
diff --git a/parser/testdata/ddl/output/create_table_with_uuid.sql.golden.json b/parser/testdata/ddl/output/create_table_with_uuid.sql.golden.json
index c67ed98..ae96c7c 100644
--- a/parser/testdata/ddl/output/create_table_with_uuid.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_with_uuid.sql.golden.json
@@ -482,6 +482,7 @@
       }
     },
     "SubQuery": null,
-    "HasTemporary": false
+    "HasTemporary": false,
+    "Comment": null
   }
 ]
\ No newline at end of file