Skip to content

Commit f407c0a

Browse files
committed
Fix case/when parsing + unit tests
1 parent 4039c90 commit f407c0a

File tree

5 files changed

+89
-52
lines changed

5 files changed

+89
-52
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
node_modules
22
.DS_Store
33
*.swp
4-
4+
*.iml
5+
.idea

src/grammar.coffee

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ grammar =
6161

6262
Unions: [
6363
o 'Union', -> [$1]
64-
o 'Unions Union', -> $1.concat($3)
64+
o 'Unions Union', -> $1.concat($2)
6565
]
6666

6767
Union: [
@@ -152,17 +152,17 @@ grammar =
152152
]
153153

154154
CaseStatement: [
155-
o 'CASE CaseWhen END', -> new Case($2)
156-
o 'CASE CaseWhen CaseElse END', -> new Case($2, $3)
155+
o 'CASE CaseWhens END', -> new Case($2)
156+
o 'CASE CaseWhens CaseElse END', -> new Case($2, $3)
157157
]
158158

159159
CaseWhen: [
160160
o 'WHEN Expression THEN Expression', -> new CaseWhen($2, $4)
161161
]
162162

163163
CaseWhens: [
164-
o 'CaseWhen', -> [$1],
165-
o 'CaseWhens SEPARATOR CaseWhen', -> $1.concat($3)
164+
o 'CaseWhens CaseWhen', -> $1.concat($2)
165+
o 'CaseWhen', -> [$1]
166166
]
167167

168168
CaseElse: [

src/nodes.coffee

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,15 @@ exports.FunctionValue = class FunctionValue
101101
exports.Case = class Case
102102
constructor: (@whens, @else) ->
103103
toString: ->
104+
whensStr = @whens.map((w) -> w.toString()).join(' ')
104105
if @else
105-
"CASE #{@whens.toString()} #{@else.toString} END"
106+
"CASE #{whensStr} #{@else.toString()} END"
106107
else
107-
"CASE #{@whens.toString()} END"
108+
"CASE #{whensStr} END"
108109

109110
exports.CaseWhen = class CaseWhen
110111
constructor: (@whenCondition, @resCondition) ->
111-
toString: -> "WHEN #{@whenCondition} THEN @resCondition"
112+
toString: -> "WHEN #{@whenCondition} THEN #{@resCondition}"
112113

113114
exports.CaseElse = class CaseElse
114115
constructor: (@elseCondition) ->

test/grammar.spec.coffee

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,12 +254,15 @@ describe "SQL Grammar", ->
254254
"""
255255

256256
it "parses UNIONs", ->
257-
parse("select * from a union select * from b").toString().should.eql """
257+
parse("select * from a union select * from b union select * from c").toString().should.eql """
258258
SELECT *
259259
FROM `a`
260260
UNION
261261
SELECT *
262262
FROM `b`
263+
UNION
264+
SELECT *
265+
FROM `c`
263266
"""
264267

265268
it "parses UNION ALL", ->
@@ -360,3 +363,10 @@ describe "SQL Grammar", ->
360363
WHERE (`bar` = $12)
361364
"""
362365

366+
describe "Case When", ->
367+
it "parses case when statements", ->
368+
parse('select case when foo = \'a\' then a when foo = \'b\' then b else c end from table').toString().should.eql """
369+
SELECT CASE WHEN (`foo` = 'a') THEN `a` WHEN (`foo` = 'b') THEN `b` ELSE `c` END
370+
FROM `table`
371+
"""
372+

test/lexer.spec.coffee

Lines changed: 67 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,62 +4,87 @@ describe "SQL Lexer", ->
44
it "eats select queries", ->
55
tokens = lexer.tokenize("select * from my_table")
66
tokens.should.eql [
7-
["SELECT", "select", 1]
8-
["STAR", "*", 1]
9-
["FROM", "from", 1]
10-
["LITERAL", "my_table", 1]
11-
["EOF", "", 1]
7+
["SELECT", "select", 1, 0]
8+
["STAR", "*", 1, 7]
9+
["FROM", "from", 1, 9]
10+
["LITERAL", "my_table", 1, 14]
11+
["EOF", "", 1, 22]
1212
]
1313

1414
it "eats select queries with stars and multiplication", ->
1515
tokens = lexer.tokenize("select * from my_table where foo = 1 * 2")
1616
tokens.should.eql [
17-
["SELECT", "select", 1]
18-
["STAR", "*", 1]
19-
["FROM", "from", 1]
20-
["LITERAL", "my_table", 1]
21-
["WHERE", "where", 1]
22-
["LITERAL", "foo", 1]
23-
["OPERATOR", "=", 1]
24-
["NUMBER", "1", 1]
25-
["MATH_MULTI", "*", 1]
26-
["NUMBER", "2", 1]
27-
["EOF", "", 1]
17+
["SELECT", "select", 1, 0]
18+
["STAR", "*", 1, 7]
19+
["FROM", "from", 1, 9]
20+
["LITERAL", "my_table", 1, 14]
21+
["WHERE", "where", 1, 23]
22+
["LITERAL", "foo", 1, 29]
23+
["OPERATOR", "=", 1, 33]
24+
["NUMBER", "1", 1, 35]
25+
["MATH_MULTI", "*", 1, 37]
26+
["NUMBER", "2", 1, 39]
27+
["EOF", "", 1, 40]
2828
]
2929

3030

3131
it "eats sub selects", ->
3232
tokens = lexer.tokenize("select * from (select * from my_table) t")
3333
tokens.should.eql [
34-
["SELECT", "select", 1]
35-
["STAR", "*", 1]
36-
["FROM", "from", 1]
37-
[ 'LEFT_PAREN', '(', 1 ]
38-
[ 'SELECT', 'select', 1 ]
39-
[ 'STAR', '*', 1 ]
40-
[ 'FROM', 'from', 1 ]
41-
[ 'LITERAL', 'my_table', 1 ]
42-
[ 'RIGHT_PAREN', ')', 1 ]
43-
["LITERAL", "t", 1]
44-
["EOF", "", 1]
34+
["SELECT", "select", 1, 0]
35+
["STAR", "*", 1, 7]
36+
["FROM", "from", 1, 9]
37+
[ 'LEFT_PAREN', '(', 1, 14]
38+
[ 'SELECT', 'select', 1, 15]
39+
[ 'STAR', '*', 1, 22]
40+
[ 'FROM', 'from', 1, 24]
41+
[ 'LITERAL', 'my_table', 1, 29]
42+
[ 'RIGHT_PAREN', ')', 1, 37]
43+
["LITERAL", "t", 1, 39]
44+
["EOF", "", 1, 40]
4545
]
4646

4747
it "eats joins", ->
4848
tokens = lexer.tokenize("select * from a join b on a.id = b.id")
4949
tokens.should.eql [
50-
["SELECT", "select", 1]
51-
["STAR", "*", 1]
52-
["FROM", "from", 1]
53-
[ 'LITERAL', 'a', 1 ]
54-
[ 'JOIN', 'join', 1 ]
55-
[ 'LITERAL', 'b', 1 ]
56-
[ 'ON', 'on', 1 ]
57-
[ 'LITERAL', 'a', 1 ]
58-
[ 'DOT', '.', 1 ]
59-
[ 'LITERAL', 'id', 1 ]
60-
[ 'OPERATOR', '=', 1 ]
61-
[ 'LITERAL', 'b', 1 ]
62-
[ 'DOT', '.', 1 ]
63-
[ 'LITERAL', 'id', 1 ]
64-
["EOF", "", 1]
50+
["SELECT", "select", 1, 0]
51+
["STAR", "*", 1, 7]
52+
["FROM", "from", 1, 9]
53+
[ 'LITERAL', 'a', 1, 14]
54+
[ 'JOIN', 'join', 1, 16]
55+
[ 'LITERAL', 'b', 1, 21]
56+
[ 'ON', 'on', 1, 23]
57+
[ 'LITERAL', 'a', 1, 26]
58+
[ 'DOT', '.', 1, 27]
59+
[ 'LITERAL', 'id', 1, 28]
60+
[ 'OPERATOR', '=', 1, 31]
61+
[ 'LITERAL', 'b', 1, 33]
62+
[ 'DOT', '.', 1, 34]
63+
[ 'LITERAL', 'id', 1, 35]
64+
["EOF", "", 1, 37]
6565
]
66+
67+
it "eats case when", ->
68+
tokens = lexer.tokenize("select case when foo = 'a' then a when foo = 'b' then b else c end from table")
69+
tokens.should.eql [
70+
['SELECT', 'select', 1, 0]
71+
['CASE', 'case', 1, 7]
72+
['WHEN', 'when', 1, 12]
73+
['LITERAL', 'foo', 1, 17]
74+
['OPERATOR', '=', 1, 21]
75+
['STRING', 'a', 1, 23]
76+
['THEN', 'then', 1, 27]
77+
['LITERAL', 'a', 1, 32]
78+
['WHEN', 'when', 1, 34]
79+
['LITERAL', 'foo', 1, 39]
80+
['OPERATOR', '=', 1, 43]
81+
['STRING', 'b', 1, 45]
82+
['THEN', 'then', 1, 49]
83+
['LITERAL', 'b', 1, 54]
84+
['ELSE', 'else', 1, 56]
85+
['LITERAL', 'c', 1, 61]
86+
['END', 'end', 1, 63]
87+
['FROM', "from", 1, 67]
88+
['LITERAL', 'table', 1, 72]
89+
['EOF', '', 1, 77]
90+
]

0 commit comments

Comments
 (0)