Skip to content

Commit 8ce1476

Browse files
authored
Add COMMENT support to CREATE VIEW parser (#265)
CREATE VIEW in ClickHouse supports COMMENT between the schema and AS SELECT: CREATE VIEW db.v (columns) COMMENT '{...}' AS SELECT ... The parser previously failed with 'expected EOF or ; but got COMMENT'. Changes: - Add Comment field to CreateView struct (ast.go) - Parse COMMENT clause in parseCreateView before AS (parser_view.go) - Format COMMENT in CreateView.FormatSQL (format.go) - Add test case: create_view_with_comment.sql - Update golden files for existing view tests (new nil Comment field)
1 parent d5e88a6 commit 8ce1476

12 files changed

Lines changed: 244 additions & 0 deletions

parser/ast.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,6 +1452,7 @@ type CreateView struct {
14521452
UUID *UUID
14531453
OnCluster *ClusterClause
14541454
TableSchema *TableSchemaClause
1455+
Comment *StringLiteral
14551456
SubQuery *SubQuery
14561457
}
14571458

@@ -1488,6 +1489,11 @@ func (c *CreateView) Accept(visitor ASTVisitor) error {
14881489
return err
14891490
}
14901491
}
1492+
if c.Comment != nil {
1493+
if err := c.Comment.Accept(visitor); err != nil {
1494+
return err
1495+
}
1496+
}
14911497
if c.SubQuery != nil {
14921498
if err := c.SubQuery.Accept(visitor); err != nil {
14931499
return err

parser/format.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,6 +1139,11 @@ func (c *CreateView) FormatSQL(formatter *Formatter) {
11391139
formatter.WriteExpr(c.TableSchema)
11401140
}
11411141

1142+
if c.Comment != nil {
1143+
formatter.WriteString(" COMMENT ")
1144+
formatter.WriteExpr(c.Comment)
1145+
}
1146+
11421147
if c.SubQuery != nil {
11431148
formatter.WriteString(" AS ")
11441149
formatter.WriteExpr(c.SubQuery)

parser/parser_view.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,13 @@ func (p *Parser) parseCreateView(pos Pos, orReplace bool) (*CreateView, error) {
240240
createView.TableSchema = tableSchema
241241
}
242242

243+
// parse COMMENT clause if exists (before AS SELECT)
244+
comment, err := p.tryParseComment()
245+
if err != nil {
246+
return nil, err
247+
}
248+
createView.Comment = comment
249+
243250
if p.tryConsumeKeywords(KeywordAs) {
244251
subQuery, err := p.parseSubQuery(p.Pos())
245252
if err != nil {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CREATE VIEW IF NOT EXISTS db.my_view
2+
(
3+
`id` Int64,
4+
`name` String
5+
)
6+
COMMENT '{"blueprint_hash":"abc123"}'
7+
AS SELECT
8+
id,
9+
name
10+
FROM db.my_table;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-- Origin SQL:
2+
CREATE VIEW IF NOT EXISTS db.my_view
3+
(
4+
`id` Int64,
5+
`name` String
6+
)
7+
COMMENT '{"blueprint_hash":"abc123"}'
8+
AS SELECT
9+
id,
10+
name
11+
FROM db.my_table;
12+
13+
14+
-- Beautify SQL:
15+
CREATE VIEW IF NOT EXISTS db.my_view (
16+
`id` Int64,
17+
`name` String
18+
) COMMENT '{"blueprint_hash":"abc123"}' AS SELECT
19+
id,
20+
name
21+
FROM
22+
db.my_table;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- Origin SQL:
2+
CREATE VIEW IF NOT EXISTS db.my_view
3+
(
4+
`id` Int64,
5+
`name` String
6+
)
7+
COMMENT '{"blueprint_hash":"abc123"}'
8+
AS SELECT
9+
id,
10+
name
11+
FROM db.my_table;
12+
13+
14+
-- Format SQL:
15+
CREATE VIEW IF NOT EXISTS db.my_view (`id` Int64, `name` String) COMMENT '{"blueprint_hash":"abc123"}' AS SELECT id, name FROM db.my_table;

parser/testdata/ddl/output/create_or_replace.sql.golden.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@
409409
"AliasTable": null,
410410
"TableFunction": null
411411
},
412+
"Comment": null,
412413
"SubQuery": {
413414
"HasParen": false,
414415
"Select": {

parser/testdata/ddl/output/create_view_basic.sql.golden.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
"AliasTable": null,
8484
"TableFunction": null
8585
},
86+
"Comment": null,
8687
"SubQuery": {
8788
"HasParen": false,
8889
"Select": {

parser/testdata/ddl/output/create_view_on_cluster_with_uuid.sql.golden.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
}
3535
},
3636
"TableSchema": null,
37+
"Comment": null,
3738
"SubQuery": {
3839
"HasParen": true,
3940
"Select": {
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
[
2+
{
3+
"CreatePos": 0,
4+
"StatementEnd": 156,
5+
"OrReplace": false,
6+
"Name": {
7+
"Database": {
8+
"Name": "db",
9+
"QuoteType": 1,
10+
"NamePos": 26,
11+
"NameEnd": 28
12+
},
13+
"Table": {
14+
"Name": "my_view",
15+
"QuoteType": 1,
16+
"NamePos": 29,
17+
"NameEnd": 36
18+
}
19+
},
20+
"IfNotExists": true,
21+
"UUID": null,
22+
"OnCluster": null,
23+
"TableSchema": {
24+
"SchemaPos": 37,
25+
"SchemaEnd": 73,
26+
"Columns": [
27+
{
28+
"NamePos": 44,
29+
"ColumnEnd": 53,
30+
"Name": {
31+
"Ident": {
32+
"Name": "id",
33+
"QuoteType": 3,
34+
"NamePos": 44,
35+
"NameEnd": 46
36+
},
37+
"DotIdent": null
38+
},
39+
"Type": {
40+
"Name": {
41+
"Name": "Int64",
42+
"QuoteType": 1,
43+
"NamePos": 48,
44+
"NameEnd": 53
45+
}
46+
},
47+
"NotNull": null,
48+
"Nullable": null,
49+
"DefaultExpr": null,
50+
"MaterializedExpr": null,
51+
"AliasExpr": null,
52+
"Codec": null,
53+
"TTL": null,
54+
"Comment": null,
55+
"CompressionCodec": null
56+
},
57+
{
58+
"NamePos": 60,
59+
"ColumnEnd": 72,
60+
"Name": {
61+
"Ident": {
62+
"Name": "name",
63+
"QuoteType": 3,
64+
"NamePos": 60,
65+
"NameEnd": 64
66+
},
67+
"DotIdent": null
68+
},
69+
"Type": {
70+
"Name": {
71+
"Name": "String",
72+
"QuoteType": 1,
73+
"NamePos": 66,
74+
"NameEnd": 72
75+
}
76+
},
77+
"NotNull": null,
78+
"Nullable": null,
79+
"DefaultExpr": null,
80+
"MaterializedExpr": null,
81+
"AliasExpr": null,
82+
"Codec": null,
83+
"TTL": null,
84+
"Comment": null,
85+
"CompressionCodec": null
86+
}
87+
],
88+
"AliasTable": null,
89+
"TableFunction": null
90+
},
91+
"Comment": {
92+
"LiteralPos": 84,
93+
"LiteralEnd": 111,
94+
"Literal": "{\"blueprint_hash\":\"abc123\"}"
95+
},
96+
"SubQuery": {
97+
"HasParen": false,
98+
"Select": {
99+
"SelectPos": 116,
100+
"StatementEnd": 156,
101+
"With": null,
102+
"Top": null,
103+
"HasDistinct": false,
104+
"DistinctOn": null,
105+
"SelectItems": [
106+
{
107+
"Expr": {
108+
"Name": "id",
109+
"QuoteType": 1,
110+
"NamePos": 127,
111+
"NameEnd": 129
112+
},
113+
"Modifiers": [],
114+
"Alias": null
115+
},
116+
{
117+
"Expr": {
118+
"Name": "name",
119+
"QuoteType": 1,
120+
"NamePos": 135,
121+
"NameEnd": 139
122+
},
123+
"Modifiers": [],
124+
"Alias": null
125+
}
126+
],
127+
"From": {
128+
"FromPos": 140,
129+
"Expr": {
130+
"Table": {
131+
"TablePos": 145,
132+
"TableEnd": 156,
133+
"Alias": null,
134+
"Expr": {
135+
"Database": {
136+
"Name": "db",
137+
"QuoteType": 1,
138+
"NamePos": 145,
139+
"NameEnd": 147
140+
},
141+
"Table": {
142+
"Name": "my_table",
143+
"QuoteType": 1,
144+
"NamePos": 148,
145+
"NameEnd": 156
146+
}
147+
},
148+
"HasFinal": false
149+
},
150+
"StatementEnd": 156,
151+
"SampleRatio": null,
152+
"HasFinal": false
153+
}
154+
},
155+
"Window": null,
156+
"Prewhere": null,
157+
"Where": null,
158+
"GroupBy": null,
159+
"WithTotal": false,
160+
"Having": null,
161+
"OrderBy": null,
162+
"LimitBy": null,
163+
"Limit": null,
164+
"Settings": null,
165+
"Format": null,
166+
"UnionAll": null,
167+
"UnionDistinct": null,
168+
"Except": null
169+
}
170+
}
171+
}
172+
]

0 commit comments

Comments
 (0)