Skip to content

Commit da1392e

Browse files
authored
Fix wrongly parse +1 as a number in expression: 1+1 (#91)
1 parent 0c87106 commit da1392e

File tree

9 files changed

+153
-18
lines changed

9 files changed

+153
-18
lines changed

parser/ast.go

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3766,13 +3766,10 @@ func (w *WhenClause) End() Pos {
37663766
func (w *WhenClause) String(level int) string {
37673767
var builder strings.Builder
37683768
builder.WriteString("WHEN ")
3769-
builder.WriteString(NewLine(level + 1))
37703769
builder.WriteString(w.When.String(level))
3771-
builder.WriteString(NewLine(level + 1))
37723770
builder.WriteString(" THEN ")
37733771
builder.WriteString(w.Then.String(level))
37743772
if w.Else != nil {
3775-
builder.WriteString(NewLine(level + 1))
37763773
builder.WriteString(" ELSE ")
37773774
builder.WriteString(w.Else.String(level))
37783775
}
@@ -3799,7 +3796,7 @@ func (w *WhenClause) Accept(visitor ASTVisitor) error {
37993796
type CaseExpr struct {
38003797
CasePos Pos
38013798
EndPos Pos
3802-
Expr Expr
3799+
Expr Expr // optional
38033800
Whens []*WhenClause
38043801
ElsePos Pos
38053802
Else Expr
@@ -3816,27 +3813,27 @@ func (c *CaseExpr) End() Pos {
38163813
func (c *CaseExpr) String(level int) string {
38173814
var builder strings.Builder
38183815
builder.WriteString("CASE ")
3819-
builder.WriteString(NewLine(level))
3820-
builder.WriteString(c.Expr.String(level))
3816+
if c.Expr != nil {
3817+
builder.WriteString(c.Expr.String(level))
3818+
}
38213819
for _, when := range c.Whens {
3822-
builder.WriteString(NewLine(level))
38233820
builder.WriteString(when.String(level))
38243821
}
38253822
if c.Else != nil {
3826-
builder.WriteString("ELSE ")
3827-
builder.WriteString(NewLine(level))
3823+
builder.WriteString(" ELSE ")
38283824
builder.WriteString(c.Else.String(level))
38293825
}
3830-
builder.WriteString(NewLine(level))
3831-
builder.WriteString("END")
3826+
builder.WriteString(" END")
38323827
return builder.String()
38333828
}
38343829

38353830
func (c *CaseExpr) Accept(visitor ASTVisitor) error {
38363831
visitor.enter(c)
38373832
defer visitor.leave(c)
3838-
if err := c.Expr.Accept(visitor); err != nil {
3839-
return err
3833+
if c.Expr != nil {
3834+
if err := c.Expr.Accept(visitor); err != nil {
3835+
return err
3836+
}
38403837
}
38413838
for _, when := range c.Whens {
38423839
if err := when.Accept(visitor); err != nil {

parser/lexer.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,17 @@ func (l *Lexer) peekToken() (*Token, error) {
256256
return token, nil
257257
}
258258

259+
func (l *Lexer) hasPrecedenceToken(last *Token) bool {
260+
return last != nil && (last.Kind == TokenIdent ||
261+
last.Kind == TokenKeyword ||
262+
last.Kind == TokenInt ||
263+
last.Kind == TokenFloat ||
264+
last.Kind == TokenString)
265+
}
266+
259267
func (l *Lexer) consumeToken() error {
260268
// clear last token
269+
lastToken := l.lastToken
261270
l.lastToken = nil
262271
l.skipComments()
263272
l.skipSpace()
@@ -281,7 +290,8 @@ func (l *Lexer) consumeToken() error {
281290
}
282291

283292
case '+', '-':
284-
if l.peekOk(1) && IsDigit(l.peekN(1)) {
293+
// hasPrecedenceToken is used to distinguish between unary and binary operators
294+
if !l.hasPrecedenceToken(lastToken) && l.peekOk(1) && IsDigit(l.peekN(1)) {
285295
return l.consumeNumber()
286296
} else if l.peekOk(1) && l.peekN(1) == '>' {
287297
l.lastToken = &Token{

parser/parser_column.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -591,11 +591,14 @@ func (p *Parser) parseColumnCaseExpr(pos Pos) (*CaseExpr, error) {
591591
return nil, err
592592
}
593593

594-
expr, err := p.parseExpr(p.Pos())
595-
if err != nil {
596-
return nil, err
594+
// case expr is optional
595+
if !p.matchKeyword(KeywordWhen) {
596+
expr, err := p.parseExpr(p.Pos())
597+
if err != nil {
598+
return nil, err
599+
}
600+
caseExpr.Expr = expr
597601
}
598-
caseExpr.Expr = expr
599602

600603
// WHEN expr THEN expr
601604
whenClauses := make([]*WhenClause, 0)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- Origin SQL:
2+
SELECT 1+1
3+
4+
-- Format SQL:
5+
6+
SELECT
7+
1 + 1;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- Origin SQL:
2+
select case when false then 'hello' else 'world' end;
3+
4+
-- Format SQL:
5+
6+
SELECT
7+
CASE WHEN false THEN 'hello' ELSE 'world' END;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
[
2+
{
3+
"SelectPos": 0,
4+
"StatementEnd": 10,
5+
"With": null,
6+
"Top": null,
7+
"SelectColumns": {
8+
"ListPos": 7,
9+
"ListEnd": 10,
10+
"HasDistinct": false,
11+
"Items": [
12+
{
13+
"LeftExpr": {
14+
"NumPos": 7,
15+
"NumEnd": 8,
16+
"Literal": "1",
17+
"Base": 10
18+
},
19+
"Operation": "+",
20+
"RightExpr": {
21+
"NumPos": 9,
22+
"NumEnd": 10,
23+
"Literal": "1",
24+
"Base": 10
25+
},
26+
"HasGlobal": false,
27+
"HasNot": false
28+
}
29+
]
30+
},
31+
"From": null,
32+
"ArrayJoin": null,
33+
"Window": null,
34+
"Prewhere": null,
35+
"Where": null,
36+
"GroupBy": null,
37+
"WithTotal": false,
38+
"Having": null,
39+
"OrderBy": null,
40+
"LimitBy": null,
41+
"Limit": null,
42+
"Settings": null,
43+
"Format": null,
44+
"UnionAll": null,
45+
"UnionDistinct": null,
46+
"Except": null
47+
}
48+
]
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
[
2+
{
3+
"SelectPos": 0,
4+
"StatementEnd": 0,
5+
"With": null,
6+
"Top": null,
7+
"SelectColumns": {
8+
"ListPos": 7,
9+
"ListEnd": 0,
10+
"HasDistinct": false,
11+
"Items": [
12+
{
13+
"CasePos": 7,
14+
"EndPos": 0,
15+
"Expr": null,
16+
"Whens": [
17+
{
18+
"WhenPos": 12,
19+
"ThenPos": 23,
20+
"When": {
21+
"Name": "false",
22+
"QuoteType": 1,
23+
"NamePos": 17,
24+
"NameEnd": 22
25+
},
26+
"Then": {
27+
"LiteralPos": 29,
28+
"LiteralEnd": 34,
29+
"Literal": "hello"
30+
},
31+
"ElsePos": 0,
32+
"Else": null
33+
}
34+
],
35+
"ElsePos": 36,
36+
"Else": {
37+
"LiteralPos": 42,
38+
"LiteralEnd": 47,
39+
"Literal": "world"
40+
}
41+
}
42+
]
43+
},
44+
"From": null,
45+
"ArrayJoin": null,
46+
"Window": null,
47+
"Prewhere": null,
48+
"Where": null,
49+
"GroupBy": null,
50+
"WithTotal": false,
51+
"Having": null,
52+
"OrderBy": null,
53+
"LimitBy": null,
54+
"Limit": null,
55+
"Settings": null,
56+
"Format": null,
57+
"UnionAll": null,
58+
"UnionDistinct": null,
59+
"Except": null
60+
}
61+
]

parser/testdata/query/select_expr.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SELECT 1+1
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
select case when false then 'hello' else 'world' end;

0 commit comments

Comments
 (0)