Skip to content

Commit 78ff34b

Browse files
authored
Fix typed placeholder lookahead; parse WHERE in SELECT without FROM (#202)
1 parent 61525d5 commit 78ff34b

13 files changed

+482
-2
lines changed

parser/parser_column.go

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,40 @@ func (p *Parser) peekKeyword(keyword string) bool {
344344
return token.Kind == TokenKindKeyword && strings.EqualFold(token.String, keyword)
345345
}
346346

347+
// isSelectItemTerminatorKeyword checks whether the current token is a keyword
348+
// that begins a clause following the SELECT item list. When true, we should not
349+
// treat the keyword itself as a bare alias.
350+
func (p *Parser) isSelectItemTerminatorKeyword() bool {
351+
switch {
352+
case p.matchKeyword(KeywordFrom):
353+
return true
354+
case p.matchKeyword(KeywordWhere):
355+
return true
356+
case p.matchKeyword(KeywordPrewhere):
357+
return true
358+
case p.matchKeyword(KeywordGroup):
359+
return true
360+
case p.matchKeyword(KeywordHaving):
361+
return true
362+
case p.matchKeyword(KeywordWindow):
363+
return true
364+
case p.matchKeyword(KeywordOrder):
365+
return true
366+
case p.matchKeyword(KeywordLimit):
367+
return true
368+
case p.matchKeyword(KeywordSettings):
369+
return true
370+
case p.matchKeyword(KeywordFormat):
371+
return true
372+
case p.matchKeyword(KeywordUnion):
373+
return true
374+
case p.matchKeyword(KeywordExcept):
375+
return true
376+
default:
377+
return false
378+
}
379+
}
380+
347381
func (p *Parser) parseColumnExpr(pos Pos) (Expr, error) { //nolint:funlen
348382
// Should parse the keyword as an identifier if the keyword is followed by one of comma, `AS`.
349383
// For example: `SELECT 1 as interval GROUP BY interval` is a valid syntax in ClickHouse.
@@ -391,7 +425,9 @@ func (p *Parser) parseColumnExpr(pos Pos) (Expr, error) { //nolint:funlen
391425
case p.matchTokenKind(TokenKindLBrace):
392426
// The map literal string also starts with '{', so we need to check the next token
393427
// to determine if it is a map literal or a query param.
394-
if p.peekTokenKind(TokenKindIdent) {
428+
// Treat both identifiers and keywords as identifier-like for placeholders.
429+
// parseIdent accepts keywords-as-ident, so this is safe.
430+
if p.peekTokenKind(TokenKindIdent) || p.peekTokenKind(TokenKindKeyword) {
395431
return p.parseQueryParam(p.Pos())
396432
}
397433
return p.parseMapLiteral(p.Pos())
@@ -752,7 +788,7 @@ func (p *Parser) parseSelectItem() (*SelectItem, error) {
752788
if err != nil {
753789
return nil, err
754790
}
755-
case p.lastTokenKind() == TokenKindKeyword && !p.matchKeyword(KeywordFrom):
791+
case p.lastTokenKind() == TokenKindKeyword && !p.isSelectItemTerminatorKeyword():
756792
alias, err = p.parseIdent()
757793
if err != nil {
758794
return nil, err
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- Origin SQL:
2+
INSERT INTO t (c) SELECT 1 WHERE 1 = 1;
3+
4+
5+
6+
-- Format SQL:
7+
INSERT INTO t (c) SELECT 1 WHERE 1 = 1;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- Origin SQL:
2+
INSERT INTO t (c) VALUES ({name :String});
3+
4+
5+
6+
-- Format SQL:
7+
INSERT INTO t (c) VALUES ({name:String});
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
INSERT INTO t (c) SELECT 1 WHERE 1 = 1;
2+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
INSERT INTO t (c) VALUES ({name :String});
2+
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
[
2+
{
3+
"InsertPos": 0,
4+
"Format": null,
5+
"HasTableKeyword": false,
6+
"Table": {
7+
"Database": null,
8+
"Table": {
9+
"Name": "t",
10+
"QuoteType": 1,
11+
"NamePos": 12,
12+
"NameEnd": 13
13+
}
14+
},
15+
"ColumnNames": {
16+
"LeftParenPos": 14,
17+
"RightParenPos": 16,
18+
"ColumnNames": [
19+
{
20+
"Ident": {
21+
"Name": "c",
22+
"QuoteType": 1,
23+
"NamePos": 15,
24+
"NameEnd": 16
25+
},
26+
"DotIdent": null
27+
}
28+
]
29+
},
30+
"Values": null,
31+
"SelectExpr": {
32+
"SelectPos": 18,
33+
"StatementEnd": 38,
34+
"With": null,
35+
"Top": null,
36+
"HasDistinct": false,
37+
"DistinctOn": null,
38+
"SelectItems": [
39+
{
40+
"Expr": {
41+
"NumPos": 25,
42+
"NumEnd": 26,
43+
"Literal": "1",
44+
"Base": 10
45+
},
46+
"Modifiers": [],
47+
"Alias": null
48+
}
49+
],
50+
"From": null,
51+
"ArrayJoin": null,
52+
"Window": null,
53+
"Prewhere": null,
54+
"Where": {
55+
"WherePos": 27,
56+
"Expr": {
57+
"LeftExpr": {
58+
"NumPos": 33,
59+
"NumEnd": 34,
60+
"Literal": "1",
61+
"Base": 10
62+
},
63+
"Operation": "=",
64+
"RightExpr": {
65+
"NumPos": 37,
66+
"NumEnd": 38,
67+
"Literal": "1",
68+
"Base": 10
69+
},
70+
"HasGlobal": false,
71+
"HasNot": false
72+
}
73+
},
74+
"GroupBy": null,
75+
"WithTotal": false,
76+
"Having": null,
77+
"OrderBy": null,
78+
"LimitBy": null,
79+
"Limit": null,
80+
"Settings": null,
81+
"Format": null,
82+
"UnionAll": null,
83+
"UnionDistinct": null,
84+
"Except": null
85+
}
86+
}
87+
]
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
[
2+
{
3+
"InsertPos": 0,
4+
"Format": null,
5+
"HasTableKeyword": false,
6+
"Table": {
7+
"Database": null,
8+
"Table": {
9+
"Name": "t",
10+
"QuoteType": 1,
11+
"NamePos": 12,
12+
"NameEnd": 13
13+
}
14+
},
15+
"ColumnNames": {
16+
"LeftParenPos": 14,
17+
"RightParenPos": 16,
18+
"ColumnNames": [
19+
{
20+
"Ident": {
21+
"Name": "c",
22+
"QuoteType": 1,
23+
"NamePos": 15,
24+
"NameEnd": 16
25+
},
26+
"DotIdent": null
27+
}
28+
]
29+
},
30+
"Values": [
31+
{
32+
"LeftParenPos": 25,
33+
"RightParenPos": 40,
34+
"Values": [
35+
{
36+
"LeftBracePos": 26,
37+
"RightBracePos": 40,
38+
"Name": {
39+
"Name": "name",
40+
"QuoteType": 1,
41+
"NamePos": 27,
42+
"NameEnd": 31
43+
},
44+
"Type": {
45+
"Name": {
46+
"Name": "String",
47+
"QuoteType": 1,
48+
"NamePos": 33,
49+
"NameEnd": 39
50+
}
51+
}
52+
}
53+
]
54+
}
55+
],
56+
"SelectExpr": null
57+
}
58+
]
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-- Origin SQL:
2+
SELECT {name :String};
3+
SELECT toString({name :String});
4+
5+
6+
7+
-- Format SQL:
8+
SELECT {name: String};
9+
SELECT toString({name: String});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-- Origin SQL:
2+
SELECT 1 WHERE 1 = 1;
3+
SELECT {p :UInt8} WHERE {p :UInt8} = 1;
4+
5+
6+
7+
-- Format SQL:
8+
SELECT 1 WHERE 1 = 1;
9+
SELECT {p: UInt8} WHERE {p: UInt8} = 1;
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
[
2+
{
3+
"SelectPos": 0,
4+
"StatementEnd": 20,
5+
"With": null,
6+
"Top": null,
7+
"HasDistinct": false,
8+
"DistinctOn": null,
9+
"SelectItems": [
10+
{
11+
"Expr": {
12+
"LBracePos": 7,
13+
"RBracePos": 20,
14+
"Name": {
15+
"Name": "name",
16+
"QuoteType": 1,
17+
"NamePos": 8,
18+
"NameEnd": 12
19+
},
20+
"Type": {
21+
"Name": {
22+
"Name": "String",
23+
"QuoteType": 1,
24+
"NamePos": 14,
25+
"NameEnd": 20
26+
}
27+
}
28+
},
29+
"Modifiers": [],
30+
"Alias": null
31+
}
32+
],
33+
"From": null,
34+
"ArrayJoin": null,
35+
"Window": null,
36+
"Prewhere": null,
37+
"Where": null,
38+
"GroupBy": null,
39+
"WithTotal": false,
40+
"Having": null,
41+
"OrderBy": null,
42+
"LimitBy": null,
43+
"Limit": null,
44+
"Settings": null,
45+
"Format": null,
46+
"UnionAll": null,
47+
"UnionDistinct": null,
48+
"Except": null
49+
},
50+
{
51+
"SelectPos": 23,
52+
"StatementEnd": 53,
53+
"With": null,
54+
"Top": null,
55+
"HasDistinct": false,
56+
"DistinctOn": null,
57+
"SelectItems": [
58+
{
59+
"Expr": {
60+
"Name": {
61+
"Name": "toString",
62+
"QuoteType": 1,
63+
"NamePos": 30,
64+
"NameEnd": 38
65+
},
66+
"Params": {
67+
"LeftParenPos": 38,
68+
"RightParenPos": 53,
69+
"Items": {
70+
"ListPos": 39,
71+
"ListEnd": 52,
72+
"HasDistinct": false,
73+
"Items": [
74+
{
75+
"Expr": {
76+
"LBracePos": 39,
77+
"RBracePos": 52,
78+
"Name": {
79+
"Name": "name",
80+
"QuoteType": 1,
81+
"NamePos": 40,
82+
"NameEnd": 44
83+
},
84+
"Type": {
85+
"Name": {
86+
"Name": "String",
87+
"QuoteType": 1,
88+
"NamePos": 46,
89+
"NameEnd": 52
90+
}
91+
}
92+
},
93+
"Alias": null
94+
}
95+
]
96+
},
97+
"ColumnArgList": null
98+
}
99+
},
100+
"Modifiers": [],
101+
"Alias": null
102+
}
103+
],
104+
"From": null,
105+
"ArrayJoin": null,
106+
"Window": null,
107+
"Prewhere": null,
108+
"Where": null,
109+
"GroupBy": null,
110+
"WithTotal": false,
111+
"Having": null,
112+
"OrderBy": null,
113+
"LimitBy": null,
114+
"Limit": null,
115+
"Settings": null,
116+
"Format": null,
117+
"UnionAll": null,
118+
"UnionDistinct": null,
119+
"Except": null
120+
}
121+
]

0 commit comments

Comments
 (0)