Skip to content

Commit daedb68

Browse files
committed
extended sql parser
1 parent f06afed commit daedb68

7 files changed

Lines changed: 60 additions & 11 deletions

File tree

expr/collate.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package expr
2+
3+
import "github.com/viant/sqlparser/node"
4+
5+
// Collate represents collate expression.
6+
type Collate struct {
7+
X node.Node
8+
Collation string
9+
}

expr/ident.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@ package expr
22

33
import "github.com/viant/sqlparser/node"
44

5-
//Ident represent an identifier
5+
// Ident represent an identifier
66
type Ident struct {
77
Name string
88
}
99

10-
//Identity returns ident or selector expr
10+
// Identity returns ident or selector expr
1111
func Identity(node node.Node) node.Node {
1212
switch actual := node.(type) {
1313
case *Ident:
1414
return actual
1515
case *Selector:
1616
return actual
17+
case *Collate:
18+
return Identity(actual.X)
1719
}
1820
return nil
1921
}

expr/values.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ func NewValues(n node.Node) (*Values, error) {
105105
}
106106

107107
return NewValues(actual.Y)
108+
case *Collate:
109+
return NewValues(actual.X)
108110
case *Literal:
109111
switch actual.Kind {
110112
case "int":

lex.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ const (
7676
typeKeyword
7777
ttlKeyword
7878
truncateTableKeyword
79+
collateKeyword
7980
)
8081

8182
var whitespaceMatcher = parsly.NewToken(whitespaceCode, "whitespace", matcher.NewWhiteSpace())
@@ -116,6 +117,7 @@ var keyMatcher = parsly.NewToken(keyTokenCode, "[RANGE|HASH|PRIMARY] KEY", match
116117
}, &option.Case{}))
117118

118119
var onKeywordMatcher = parsly.NewToken(onKeyword, "ON", matcher.NewKeyword("on", &option.Case{}))
120+
var collateKeywordMatcher = parsly.NewToken(collateKeyword, "COLLATE", matcher.NewKeyword("collate", &option.Case{}))
119121

120122
var whereKeywordMatcher = parsly.NewToken(whereKeyword, "WHERE", matcher.NewKeyword("where", &option.Case{}))
121123
var groupByMatcher = parsly.NewToken(groupByKeyword, "GROUP BY", matcher.NewSpacedFragment("group by", &option.Case{}))

operand.go

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ func discoverAlias(cursor *parsly.Cursor) string {
2626
func expectOperand(cursor *parsly.Cursor) (node.Node, error) {
2727
literal, err := TryParseLiteral(cursor)
2828
if literal != nil || err != nil {
29-
return literal, err
29+
if err != nil {
30+
return literal, err
31+
}
32+
return applyCollate(cursor, literal)
3033
}
3134

3235
match := cursor.MatchAfterOptional(whitespaceMatcher,
@@ -64,7 +67,7 @@ func expectOperand(cursor *parsly.Cursor) (node.Node, error) {
6467
if err != nil {
6568
return nil, err
6669
}
67-
return &expr.Call{X: selector, Raw: raw, Args: args}, nil
70+
return applyCollate(cursor, &expr.Call{X: selector, Raw: raw, Args: args})
6871

6972
case exceptKeyword:
7073
return parseStarExpr(cursor, selRaw, selector)
@@ -75,15 +78,15 @@ func expectOperand(cursor *parsly.Cursor) (node.Node, error) {
7578
if match.Code == commentBlock {
7679
comments = match.Text(cursor)
7780
}
78-
return expr.NewStar(selector, comments), nil
81+
return applyCollate(cursor, expr.NewStar(selector, comments))
7982
}
80-
return selector, nil
83+
return applyCollate(cursor, selector)
8184
case exceptKeyword:
8285
return nil, cursor.NewError(selectorMatcher)
8386
case nullTokenCode:
84-
return expr.NewNullLiteral(match.Text(cursor)), nil
87+
return applyCollate(cursor, expr.NewNullLiteral(match.Text(cursor)))
8588
case caseBlock:
86-
return &expr.Switch{Raw: match.Text(cursor)}, nil
89+
return applyCollate(cursor, &expr.Switch{Raw: match.Text(cursor)})
8790
case starTokenCode:
8891
selRaw := match.Text(cursor)
8992
selector := expr.NewSelector(selRaw)
@@ -97,7 +100,7 @@ func expectOperand(cursor *parsly.Cursor) (node.Node, error) {
97100
case exceptKeyword:
98101
return parseStarExpr(cursor, selRaw, selector)
99102
}
100-
return expr.NewStar(selector, comments), err
103+
return applyCollate(cursor, expr.NewStar(selector, comments))
101104
case parenthesesCode:
102105
raw := match.Text(cursor)
103106
result := expr.NewParenthesis(raw)
@@ -139,13 +142,13 @@ func expectOperand(cursor *parsly.Cursor) (node.Node, error) {
139142
}
140143

141144
}
142-
return result, nil
145+
return applyCollate(cursor, result)
143146
case notOperator:
144147
unary := expr.NewUnary(match.Text(cursor))
145148
if unary.X, err = expectOperand(cursor); unary.X == nil || err != nil {
146149
return nil, cursor.NewError(selectorMatcher)
147150
}
148-
return unary, nil
151+
return applyCollate(cursor, unary)
149152
case commentBlock:
150153
return expectOperand(cursor)
151154
case asKeyword, orderByKeyword, onKeyword, fromKeyword, whereKeyword, joinToken, groupByKeyword, havingKeyword, windowTokenCode, nextCode:
@@ -154,6 +157,23 @@ func expectOperand(cursor *parsly.Cursor) (node.Node, error) {
154157
return nil, nil
155158
}
156159

160+
func applyCollate(cursor *parsly.Cursor, n node.Node) (node.Node, error) {
161+
if n == nil {
162+
return nil, nil
163+
}
164+
pos := cursor.Pos
165+
match := cursor.MatchAfterOptional(whitespaceMatcher, collateKeywordMatcher)
166+
if match.Code != collateKeyword {
167+
cursor.Pos = pos
168+
return n, nil
169+
}
170+
match = cursor.MatchAfterOptional(whitespaceMatcher, identifierMatcher)
171+
if match.Code != identifierCode {
172+
return nil, cursor.NewError(identifierMatcher)
173+
}
174+
return &expr.Collate{X: n, Collation: match.Text(cursor)}, nil
175+
}
176+
157177
func parseCallArguments(cursor *parsly.Cursor, raw string, pos int) ([]node.Node, error) {
158178
var args []node.Node
159179
if len(raw) > 0 {

query_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,16 @@ func TestParseSelect(t *testing.T) {
253253
SQL: "SELECT t.* FROM x1 t join x2 z ON t.ID = z.ID",
254254
expect: "SELECT t.* FROM x1 t join x2 z ON t.ID = z.ID",
255255
},
256+
{
257+
description: "JOIN select with collate",
258+
SQL: "SELECT * FROM a JOIN b ON a.brand COLLATE utf8mb4_bin = b.brand COLLATE utf8mb4_bin",
259+
expect: "SELECT * FROM a JOIN b ON a.brand COLLATE utf8mb4_bin = b.brand COLLATE utf8mb4_bin",
260+
},
261+
{
262+
description: "JOIN select with collate parenthesis",
263+
SQL: "SELECT * FROM a JOIN b ON (a.brand COLLATE utf8mb4_bin) = (b.brand COLLATE utf8mb4_bin)",
264+
expect: "SELECT * FROM a JOIN b ON (a.brand COLLATE utf8mb4_bin) = (b.brand COLLATE utf8mb4_bin)",
265+
},
256266
{
257267
description: "JOIN select",
258268
SQL: "SELECT t.* FROM x1 t join x2 z ON t.ID = z.ID LEFT JOIN x3 y ON t.ID = x3.ID",

stringify.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ func stringify(n node.Node, builder *bytes.Buffer) {
157157
builder.WriteString(actual.Raw)
158158
builder.WriteString(" ")
159159
builder.WriteString(actual.Unparsed)
160+
case *expr.Collate:
161+
stringify(actual.X, builder)
162+
builder.WriteString(" COLLATE ")
163+
builder.WriteString(actual.Collation)
160164
case *query.From:
161165
if actual.X == nil {
162166
return

0 commit comments

Comments
 (0)