Skip to content

Commit 03dc6de

Browse files
authored
Fix INSERT INTO should allow the empty column list (#153)
1 parent 9da9f1b commit 03dc6de

11 files changed

+257
-49
lines changed

parser/ast.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6730,12 +6730,13 @@ func (v *AssignmentValues) Accept(visitor ASTVisitor) error {
67306730
}
67316731

67326732
type InsertStmt struct {
6733-
InsertPos Pos
6734-
Format *FormatClause
6735-
Table Expr
6736-
ColumnNames *ColumnNamesExpr
6737-
Values []*AssignmentValues
6738-
SelectExpr *SelectQuery
6733+
InsertPos Pos
6734+
Format *FormatClause
6735+
HasTableKeyword bool
6736+
Table Expr
6737+
ColumnNames *ColumnNamesExpr
6738+
Values []*AssignmentValues
6739+
SelectExpr *SelectQuery
67396740
}
67406741

67416742
func (i *InsertStmt) Pos() Pos {
@@ -6751,7 +6752,10 @@ func (i *InsertStmt) End() Pos {
67516752

67526753
func (i *InsertStmt) String() string {
67536754
var builder strings.Builder
6754-
builder.WriteString("INSERT INTO TABLE ")
6755+
builder.WriteString("INSERT INTO ")
6756+
if i.HasTableKeyword {
6757+
builder.WriteString("TABLE ")
6758+
}
67556759
builder.WriteString(i.Table.String())
67566760
if i.ColumnNames != nil {
67576761
builder.WriteString(" ")
@@ -6762,11 +6766,11 @@ func (i *InsertStmt) String() string {
67626766
builder.WriteString(i.Format.String())
67636767
}
67646768

6765-
builder.WriteString(" ")
67666769
if i.SelectExpr != nil {
6770+
builder.WriteString(" ")
67676771
builder.WriteString(i.SelectExpr.String())
6768-
} else {
6769-
builder.WriteString("VALUES ")
6772+
} else if len(i.Values) > 0 {
6773+
builder.WriteString(" VALUES ")
67706774
for j, value := range i.Values {
67716775
if j > 0 {
67726776
builder.WriteString(", ")

parser/parser_table.go

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,7 +1275,9 @@ func (p *Parser) parseInsertStmt(pos Pos) (*InsertStmt, error) {
12751275
if err := p.expectKeyword(KeywordInto); err != nil {
12761276
return nil, err
12771277
}
1278-
_ = p.tryConsumeKeywords(KeywordTable)
1278+
1279+
insertExpr := &InsertStmt{InsertPos: pos}
1280+
insertExpr.HasTableKeyword = p.tryConsumeKeywords(KeywordTable)
12791281

12801282
var table Expr
12811283
var err error
@@ -1287,52 +1289,43 @@ func (p *Parser) parseInsertStmt(pos Pos) (*InsertStmt, error) {
12871289
if err != nil {
12881290
return nil, err
12891291
}
1292+
insertExpr.Table = table
12901293

1291-
insertExpr := &InsertStmt{
1292-
InsertPos: pos,
1293-
Table: table,
1294+
if p.matchTokenKind(TokenKindLParen) {
1295+
// parse column names
1296+
insertExpr.ColumnNames, err = p.parseColumnNamesExpr(p.Pos())
1297+
if err != nil {
1298+
return nil, err
1299+
}
12941300
}
12951301

1296-
for i := 0; i < 1; i++ {
1297-
switch {
1298-
case p.matchKeyword(KeywordFormat):
1299-
insertExpr.Format, err = p.parseFormat(p.Pos())
1300-
case p.matchKeyword(KeywordValues):
1301-
// consume VALUES keyword
1302-
_ = p.lexer.consumeToken()
1303-
case p.matchKeyword(KeywordSelect):
1304-
insertExpr.SelectExpr, err = p.parseSelectQuery(p.Pos())
1302+
switch {
1303+
case p.matchKeyword(KeywordFormat):
1304+
insertExpr.Format, err = p.parseFormat(p.Pos())
1305+
case p.matchKeyword(KeywordValues):
1306+
// consume VALUES keyword
1307+
_ = p.lexer.consumeToken()
1308+
values := make([]*AssignmentValues, 0)
1309+
for !p.lexer.isEOF() {
1310+
value, err := p.parseAssignmentValues(p.Pos())
13051311
if err != nil {
13061312
return nil, err
13071313
}
1308-
return insertExpr, nil
1309-
default:
1310-
if insertExpr.ColumnNames == nil {
1311-
// columns
1312-
insertExpr.ColumnNames, err = p.parseColumnNamesExpr(p.Pos())
1313-
// need another pass to parse keywords
1314-
i--
1314+
values = append(values, value)
1315+
if p.tryConsumeTokenKind(TokenKindComma) == nil {
1316+
break
13151317
}
13161318
}
1319+
insertExpr.Values = values
1320+
case p.matchKeyword(KeywordSelect):
1321+
insertExpr.SelectExpr, err = p.parseSelectQuery(p.Pos())
1322+
default:
1323+
// do nothing
13171324
}
1318-
1325+
13191326
if err != nil {
13201327
return nil, err
13211328
}
1322-
1323-
values := make([]*AssignmentValues, 0)
1324-
for !p.lexer.isEOF() {
1325-
value, err := p.parseAssignmentValues(p.Pos())
1326-
if err != nil {
1327-
return nil, err
1328-
}
1329-
values = append(values, value)
1330-
if p.tryConsumeTokenKind(TokenKindComma) == nil {
1331-
break
1332-
}
1333-
}
1334-
insertExpr.Values = values
1335-
13361329
return insertExpr, nil
13371330
}
13381331

parser/testdata/dml/format/insert_values.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ INSERT INTO helloworld.my_first_table (user_id, message, timestamp, metric) VALU
66
(101, 'Granules are the smallest chunks of data read', now() + 5, 3.14159 )
77

88
-- Format SQL:
9-
INSERT INTO TABLE helloworld.my_first_table (user_id, message, timestamp, metric) VALUES (101, 'Hello, ClickHouse!', now(), -1.0), (102, 'Insert a lot of rows per batch', yesterday(), 1.41421), (102, 'Sort your data based on your commonly-used queries', today(), 2.718), (101, 'Granules are the smallest chunks of data read', now() + 5, 3.14159);
9+
INSERT INTO helloworld.my_first_table (user_id, message, timestamp, metric) VALUES (101, 'Hello, ClickHouse!', now(), -1.0), (102, 'Insert a lot of rows per batch', yesterday(), 1.41421), (102, 'Sort your data based on your commonly-used queries', today(), 2.718), (101, 'Granules are the smallest chunks of data read', now() + 5, 3.14159);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-- Origin SQL:
2+
INSERT INTO `_test_1345# $.ДБ`.`2. Таблица №2`;
3+
INSERT INTO "db"."table_name" (col1, col2) VALUES (1, 2);
4+
INSERT INTO `_test_1345# $.ДБ`.`2. Таблица №2` (col1, col2);
5+
INSERT INTO table_name (col1, col2) VALUES (1, 2) FORMAT Native;
6+
7+
-- Format SQL:
8+
INSERT INTO `_test_1345# $.ДБ`.`2. Таблица №2`;
9+
INSERT INTO "db"."table_name" (col1, col2) VALUES (1, 2);
10+
INSERT INTO `_test_1345# $.ДБ`.`2. Таблица №2` (col1, col2);
11+
INSERT INTO table_name (col1, col2) VALUES (1, 2);

parser/testdata/dml/format/insert_with_placeholder.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ INSERT INTO test_with_typed_columns (id, created_at)
1010
VALUES ({id: Int32}, {created_at: DateTime64(6)});
1111

1212
-- Format SQL:
13-
INSERT INTO TABLE t0 (user_id, message, timestamp, metric) VALUES (?, ?, ?, ?), (?, ?, ?, ?), (?, ?, ?, ?), (?, ?, ?, ?);
14-
INSERT INTO TABLE test_with_typed_columns (id, created_at) VALUES ({id:Int32}, {created_at:DateTime64(6)});
13+
INSERT INTO t0 (user_id, message, timestamp, metric) VALUES (?, ?, ?, ?), (?, ?, ?, ?), (?, ?, ?, ?), (?, ?, ?, ?);
14+
INSERT INTO test_with_typed_columns (id, created_at) VALUES ({id:Int32}, {created_at:DateTime64(6)});

parser/testdata/dml/format/insert_with_select.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ SELECT
88
FROM test.visits;
99

1010
-- Format SQL:
11-
INSERT INTO TABLE test.visits_null SELECT CounterID, StartDate, Sign, UserID FROM test.visits;
11+
INSERT INTO test.visits_null SELECT CounterID, StartDate, Sign, UserID FROM test.visits;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
INSERT INTO `_test_1345# $.ДБ`.`2. Таблица №2`;
2+
INSERT INTO "db"."table_name" (col1, col2) VALUES (1, 2);
3+
INSERT INTO `_test_1345# $.ДБ`.`2. Таблица №2` (col1, col2);
4+
INSERT INTO table_name (col1, col2) VALUES (1, 2) FORMAT Native;

parser/testdata/dml/output/insert_values.sql.golden.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
"InsertPos": 0,
44
"Format": null,
5+
"HasTableKeyword": false,
56
"Table": {
67
"Database": {
78
"Name": "helloworld",
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
[
2+
{
3+
"InsertPos": 0,
4+
"Format": null,
5+
"HasTableKeyword": false,
6+
"Table": {
7+
"Database": {
8+
"Name": "_test_1345# $.ДБ",
9+
"QuoteType": 3,
10+
"NamePos": 13,
11+
"NameEnd": 31
12+
},
13+
"Table": {
14+
"Name": "2. Таблица №2",
15+
"QuoteType": 3,
16+
"NamePos": 34,
17+
"NameEnd": 56
18+
}
19+
},
20+
"ColumnNames": null,
21+
"Values": null,
22+
"SelectExpr": null
23+
},
24+
{
25+
"InsertPos": 59,
26+
"Format": null,
27+
"HasTableKeyword": false,
28+
"Table": {
29+
"Database": {
30+
"Name": "db",
31+
"QuoteType": 2,
32+
"NamePos": 72,
33+
"NameEnd": 74
34+
},
35+
"Table": {
36+
"Name": "table_name",
37+
"QuoteType": 2,
38+
"NamePos": 77,
39+
"NameEnd": 87
40+
}
41+
},
42+
"ColumnNames": {
43+
"LeftParenPos": 89,
44+
"RightParenPos": 100,
45+
"ColumnNames": [
46+
{
47+
"Ident": {
48+
"Name": "col1",
49+
"QuoteType": 1,
50+
"NamePos": 90,
51+
"NameEnd": 94
52+
},
53+
"DotIdent": null
54+
},
55+
{
56+
"Ident": {
57+
"Name": "col2",
58+
"QuoteType": 1,
59+
"NamePos": 96,
60+
"NameEnd": 100
61+
},
62+
"DotIdent": null
63+
}
64+
]
65+
},
66+
"Values": [
67+
{
68+
"LeftParenPos": 109,
69+
"RightParenPos": 114,
70+
"Values": [
71+
{
72+
"NumPos": 110,
73+
"NumEnd": 111,
74+
"Literal": "1",
75+
"Base": 10
76+
},
77+
{
78+
"NumPos": 113,
79+
"NumEnd": 114,
80+
"Literal": "2",
81+
"Base": 10
82+
}
83+
]
84+
}
85+
],
86+
"SelectExpr": null
87+
},
88+
{
89+
"InsertPos": 117,
90+
"Format": null,
91+
"HasTableKeyword": false,
92+
"Table": {
93+
"Database": {
94+
"Name": "_test_1345# $.ДБ",
95+
"QuoteType": 3,
96+
"NamePos": 130,
97+
"NameEnd": 148
98+
},
99+
"Table": {
100+
"Name": "2. Таблица №2",
101+
"QuoteType": 3,
102+
"NamePos": 151,
103+
"NameEnd": 173
104+
}
105+
},
106+
"ColumnNames": {
107+
"LeftParenPos": 175,
108+
"RightParenPos": 186,
109+
"ColumnNames": [
110+
{
111+
"Ident": {
112+
"Name": "col1",
113+
"QuoteType": 1,
114+
"NamePos": 176,
115+
"NameEnd": 180
116+
},
117+
"DotIdent": null
118+
},
119+
{
120+
"Ident": {
121+
"Name": "col2",
122+
"QuoteType": 1,
123+
"NamePos": 182,
124+
"NameEnd": 186
125+
},
126+
"DotIdent": null
127+
}
128+
]
129+
},
130+
"Values": null,
131+
"SelectExpr": null
132+
},
133+
{
134+
"InsertPos": 189,
135+
"Format": null,
136+
"HasTableKeyword": false,
137+
"Table": {
138+
"Database": null,
139+
"Table": {
140+
"Name": "table_name",
141+
"QuoteType": 1,
142+
"NamePos": 201,
143+
"NameEnd": 211
144+
}
145+
},
146+
"ColumnNames": {
147+
"LeftParenPos": 212,
148+
"RightParenPos": 223,
149+
"ColumnNames": [
150+
{
151+
"Ident": {
152+
"Name": "col1",
153+
"QuoteType": 1,
154+
"NamePos": 213,
155+
"NameEnd": 217
156+
},
157+
"DotIdent": null
158+
},
159+
{
160+
"Ident": {
161+
"Name": "col2",
162+
"QuoteType": 1,
163+
"NamePos": 219,
164+
"NameEnd": 223
165+
},
166+
"DotIdent": null
167+
}
168+
]
169+
},
170+
"Values": [
171+
{
172+
"LeftParenPos": 232,
173+
"RightParenPos": 237,
174+
"Values": [
175+
{
176+
"NumPos": 233,
177+
"NumEnd": 234,
178+
"Literal": "1",
179+
"Base": 10
180+
},
181+
{
182+
"NumPos": 236,
183+
"NumEnd": 237,
184+
"Literal": "2",
185+
"Base": 10
186+
}
187+
]
188+
}
189+
],
190+
"SelectExpr": null
191+
}
192+
]

parser/testdata/dml/output/insert_with_placeholder.sql.golden.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
"InsertPos": 0,
44
"Format": null,
5+
"HasTableKeyword": false,
56
"Table": {
67
"Database": null,
78
"Table": {
@@ -164,6 +165,7 @@
164165
{
165166
"InsertPos": 133,
166167
"Format": null,
168+
"HasTableKeyword": false,
167169
"Table": {
168170
"Database": null,
169171
"Table": {

0 commit comments

Comments
 (0)