Skip to content

Commit 6fcbda2

Browse files
authored
Fix cannot parse multi JOIN expressions (#56)
1 parent 16a015a commit 6fcbda2

29 files changed

+1379
-642
lines changed

parser/ast.go

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,46 @@ func (p *BinaryExpr) Accept(visitor ASTVisitor) error {
133133
return visitor.VisitBinaryExpr(p)
134134
}
135135

136+
type JoinTableExpr struct {
137+
Table *TableExpr
138+
StatementEnd Pos
139+
SampleRatio *SampleRatioExpr
140+
HasFinal bool
141+
}
142+
143+
func (j *JoinTableExpr) Accept(visitor ASTVisitor) error {
144+
visitor.enter(j)
145+
defer visitor.leave(j)
146+
if err := j.Table.Accept(visitor); err != nil {
147+
return err
148+
}
149+
if j.SampleRatio != nil {
150+
return j.SampleRatio.Accept(visitor)
151+
}
152+
return visitor.VisitJoinTableExpr(j)
153+
}
154+
155+
func (j *JoinTableExpr) Pos() Pos {
156+
return j.Table.Pos()
157+
}
158+
159+
func (j *JoinTableExpr) End() Pos {
160+
return j.StatementEnd
161+
}
162+
163+
func (j *JoinTableExpr) String(level int) string {
164+
var builder strings.Builder
165+
builder.WriteString(j.Table.String(level))
166+
if j.SampleRatio != nil {
167+
builder.WriteByte(' ')
168+
builder.WriteString(j.SampleRatio.String(level))
169+
}
170+
if j.HasFinal {
171+
builder.WriteString(" FINAL")
172+
}
173+
return builder.String()
174+
}
175+
136176
type AlterTableExpr interface {
137177
Expr
138178
AlterType() string
@@ -3760,7 +3800,6 @@ type JoinExpr struct {
37603800
Left Expr
37613801
Right Expr
37623802
Modifiers []string
3763-
SampleRatio *SampleRatioExpr
37643803
Constraints Expr
37653804
}
37663805

@@ -3772,20 +3811,36 @@ func (j *JoinExpr) End() Pos {
37723811
return j.Left.End()
37733812
}
37743813

3775-
func (j *JoinExpr) String(level int) string {
3776-
var builder strings.Builder
3777-
builder.WriteString(j.Left.String(level))
3778-
if len(j.Modifiers) != 0 {
3779-
builder.WriteByte(' ')
3780-
builder.WriteString(strings.Join(j.Modifiers, " "))
3781-
} else {
3814+
func buildJoinString(builder *strings.Builder, expr Expr, level int) {
3815+
joinExpr, ok := expr.(*JoinExpr)
3816+
if !ok {
37823817
builder.WriteString(",")
3818+
builder.WriteString(expr.String(level))
3819+
return
37833820
}
3784-
builder.WriteByte(' ')
3785-
builder.WriteString(j.Right.String(level))
3786-
if j.Constraints != nil {
3821+
3822+
if len(joinExpr.Modifiers) == 0 {
3823+
builder.WriteString(",")
3824+
} else {
3825+
builder.WriteString(NewLine(level))
3826+
builder.WriteString(strings.Join(joinExpr.Modifiers, " "))
37873827
builder.WriteByte(' ')
3788-
builder.WriteString(j.Constraints.String(level))
3828+
}
3829+
builder.WriteString(joinExpr.Left.String(level))
3830+
if joinExpr.Constraints != nil {
3831+
builder.WriteByte(' ')
3832+
builder.WriteString(joinExpr.Constraints.String(level))
3833+
}
3834+
if joinExpr.Right != nil {
3835+
buildJoinString(builder, joinExpr.Right, level)
3836+
}
3837+
}
3838+
3839+
func (j *JoinExpr) String(level int) string {
3840+
var builder strings.Builder
3841+
builder.WriteString(j.Left.String(level))
3842+
if j.Right != nil {
3843+
buildJoinString(&builder, j.Right, level)
37893844
}
37903845
return builder.String()
37913846
}
@@ -3796,11 +3851,8 @@ func (j *JoinExpr) Accept(visitor ASTVisitor) error {
37963851
if err := j.Left.Accept(visitor); err != nil {
37973852
return err
37983853
}
3799-
if err := j.Right.Accept(visitor); err != nil {
3800-
return err
3801-
}
3802-
if j.SampleRatio != nil {
3803-
if err := j.SampleRatio.Accept(visitor); err != nil {
3854+
if j.Right != nil {
3855+
if err := j.Right.Accept(visitor); err != nil {
38043856
return err
38053857
}
38063858
}

parser/ast_visitor.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ type ASTVisitor interface {
9191
VisitUsingExpr(expr *UsingExpr) error
9292
VisitJoinExpr(expr *JoinExpr) error
9393
VisitJoinConstraintExpr(expr *JoinConstraintExpr) error
94+
VisitJoinTableExpr(expr *JoinTableExpr) error
9495
VisitFromExpr(expr *FromExpr) error
9596
VisitIsNullExpr(expr *IsNullExpr) error
9697
VisitIsNotNullExpr(expr *IsNotNullExpr) error
@@ -175,6 +176,13 @@ func (v *DefaultASTVisitor) VisitBinaryExpr(expr *BinaryExpr) error {
175176
return nil
176177
}
177178

179+
func (v *DefaultASTVisitor) VisitJoinTableExpr(expr *JoinTableExpr) error {
180+
if v.Visit != nil {
181+
return v.Visit(expr)
182+
}
183+
return nil
184+
}
185+
178186
func (v *DefaultASTVisitor) VisitAlterTable(expr *AlterTable) error {
179187
if v.Visit != nil {
180188
return v.Visit(expr)

parser/parser_query.go

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,11 @@ func (p *Parser) tryParseJoinConstraints(pos Pos) (Expr, error) {
123123
return nil, nil
124124
}
125125

126-
func (p *Parser) parseJoinOp(_ Pos) (Expr, []string, error) {
126+
func (p *Parser) parseJoinOp(_ Pos) []string {
127127
var modifiers []string
128128
switch {
129129
case p.tryConsumeKeyword(KeywordCross) != nil: // cross join
130130
modifiers = append(modifiers, KeywordCross)
131-
case p.tryConsumeTokenKind(",") != nil:
132-
expr, err := p.parseJoinExpr(p.Pos())
133-
if err != nil {
134-
return nil, nil, err
135-
}
136-
return expr, nil, nil
137131
case p.matchKeyword(KeywordAny), p.matchKeyword(KeywordAll):
138132
modifiers = append(modifiers, p.last().String)
139133
_ = p.lexer.consumeToken()
@@ -188,62 +182,106 @@ func (p *Parser) parseJoinOp(_ Pos) (Expr, []string, error) {
188182
_ = p.lexer.consumeToken()
189183
}
190184
}
191-
192-
if p.tryConsumeKeyword(KeywordJoin) != nil {
193-
modifiers = append(modifiers, KeywordJoin)
194-
expr, err := p.parseJoinExpr(p.Pos())
195-
if err != nil {
196-
return nil, nil, err
197-
}
198-
return expr, modifiers, nil
199-
}
200-
return nil, modifiers, nil
185+
return modifiers
201186
}
202187

203-
func (p *Parser) parseJoinExpr(pos Pos) (expr Expr, err error) {
204-
var sampleRatio *SampleRatioExpr
188+
func (p *Parser) parseJoinTableExpr(_ Pos) (Expr, error) {
205189
switch {
206-
case p.matchTokenKind(TokenString), p.matchTokenKind(TokenIdent), p.matchTokenKind("("):
207-
expr, err = p.parseTableExpr(p.Pos())
190+
case p.matchTokenKind(TokenIdent), p.matchTokenKind("("):
191+
tableExpr, err := p.parseTableExpr(p.Pos())
208192
if err != nil {
209193
return nil, err
210194
}
211-
_ = p.tryConsumeKeyword(KeywordFinal)
195+
statementEnd := tableExpr.End()
196+
197+
hasFinal := p.matchKeyword(KeywordFinal)
198+
if hasFinal {
199+
statementEnd = p.last().End
200+
_ = p.lexer.consumeToken()
201+
}
212202

213-
sampleRatio, err = p.tryParseSampleRationExpr(p.Pos())
203+
sampleRatio, err := p.tryParseSampleRatioExpr(p.Pos())
214204
if err != nil {
215205
return nil, err
216206
}
207+
if sampleRatio != nil {
208+
statementEnd = sampleRatio.End()
209+
}
210+
return &JoinTableExpr{
211+
Table: tableExpr,
212+
SampleRatio: sampleRatio,
213+
HasFinal: hasFinal,
214+
StatementEnd: statementEnd,
215+
}, nil
217216
default:
218217
return nil, fmt.Errorf("expected table name or subquery, got %s", p.last().Kind)
219218
}
219+
}
220220

221-
// TODO: store global/local in AST
222-
if p.matchKeyword(KeywordGlobal) || p.matchKeyword(KeywordLocal) {
223-
_ = p.lexer.consumeToken()
221+
func (p *Parser) parseJoinRightExpr(pos Pos) (expr Expr, err error) {
222+
var rightExpr Expr
223+
var modifiers []string
224+
switch {
225+
case p.tryConsumeKeyword(KeywordGlobal) != nil:
226+
case p.tryConsumeKeyword(KeywordLocal) != nil:
227+
case p.tryConsumeTokenKind(",") != nil:
228+
return p.parseJoinExpr(p.Pos())
229+
default:
230+
modifiers = p.parseJoinOp(p.Pos())
224231
}
225-
rightExpr, modifiers, err := p.parseJoinOp(p.Pos())
226232
if err != nil {
227233
return nil, err
228234
}
229-
// rightExpr is nil means no join op
230-
if rightExpr == nil {
231-
return
235+
236+
if len(modifiers) != 0 && !p.matchKeyword(KeywordJoin) {
237+
return nil, fmt.Errorf("expected JOIN, got %s", p.lastTokenKind())
238+
}
239+
if p.tryConsumeKeyword(KeywordJoin) == nil {
240+
return nil, nil
241+
}
242+
243+
modifiers = append(modifiers, KeywordJoin)
244+
expr, err = p.parseJoinTableExpr(p.Pos())
245+
if err != nil {
246+
return nil, err
232247
}
233248
constrains, err := p.tryParseJoinConstraints(p.Pos())
234249
if err != nil {
235250
return nil, err
236251
}
252+
253+
// try parse next join
254+
rightExpr, err = p.parseJoinRightExpr(p.Pos())
255+
if err != nil {
256+
return nil, err
257+
}
237258
return &JoinExpr{
238259
JoinPos: pos,
239260
Left: expr,
240261
Right: rightExpr,
241262
Modifiers: modifiers,
242-
SampleRatio: sampleRatio,
243263
Constraints: constrains,
244264
}, nil
245265
}
246266

267+
func (p *Parser) parseJoinExpr(pos Pos) (expr Expr, err error) {
268+
if expr, err = p.parseJoinTableExpr(p.Pos()); err != nil {
269+
return nil, err
270+
}
271+
rightExpr, err := p.parseJoinRightExpr(p.Pos())
272+
if err != nil {
273+
return nil, err
274+
}
275+
if rightExpr == nil {
276+
return expr, nil
277+
}
278+
return &JoinExpr{
279+
JoinPos: pos,
280+
Left: expr,
281+
Right: rightExpr,
282+
}, nil
283+
}
284+
247285
func (p *Parser) parseTableExpr(pos Pos) (*TableExpr, error) {
248286
var expr Expr
249287
var err error
@@ -295,7 +333,7 @@ func (p *Parser) parseTableExpr(pos Pos) (*TableExpr, error) {
295333
if asToken := p.tryConsumeKeyword(KeywordFinal); asToken != nil {
296334
switch expr.(type) {
297335
case *TableFunctionExpr:
298-
return nil, errors.New("tablefunction doesn't support FINAL")
336+
return nil, errors.New("table function doesn't support FINAL")
299337
case *SelectQuery:
300338
return nil, errors.New("subquery doesn't support FINAL")
301339
}
@@ -941,7 +979,7 @@ func (p *Parser) tryParseColumnAliases() ([]*Ident, error) {
941979
return aliasList, nil
942980
}
943981

944-
func (p *Parser) tryParseSampleRationExpr(pos Pos) (*SampleRatioExpr, error) {
982+
func (p *Parser) tryParseSampleRatioExpr(pos Pos) (*SampleRatioExpr, error) {
945983
if !p.matchKeyword(KeywordSample) {
946984
return nil, nil
947985
}

parser/testdata/ddl/output/bug_001.sql.golden.json

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -433,23 +433,28 @@
433433
"From": {
434434
"FromPos": 592,
435435
"Expr": {
436-
"TablePos": 597,
437-
"TableEnd": 605,
438-
"Alias": null,
439-
"Expr": {
440-
"Database": {
441-
"Name": "db",
442-
"QuoteType": 1,
443-
"NamePos": 597,
444-
"NameEnd": 599
436+
"Table": {
437+
"TablePos": 597,
438+
"TableEnd": 605,
439+
"Alias": null,
440+
"Expr": {
441+
"Database": {
442+
"Name": "db",
443+
"QuoteType": 1,
444+
"NamePos": 597,
445+
"NameEnd": 599
446+
},
447+
"Table": {
448+
"Name": "table",
449+
"QuoteType": 1,
450+
"NamePos": 600,
451+
"NameEnd": 605
452+
}
445453
},
446-
"Table": {
447-
"Name": "table",
448-
"QuoteType": 1,
449-
"NamePos": 600,
450-
"NameEnd": 605
451-
}
454+
"HasFinal": false
452455
},
456+
"StatementEnd": 605,
457+
"SampleRatio": null,
453458
"HasFinal": false
454459
}
455460
},

parser/testdata/ddl/output/create_live_view_basic.sql.golden.json

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -93,18 +93,23 @@
9393
"From": {
9494
"FromPos": 88,
9595
"Expr": {
96-
"TablePos": 93,
97-
"TableEnd": 101,
98-
"Alias": null,
99-
"Expr": {
100-
"Database": null,
101-
"Table": {
102-
"Name": "my_table",
103-
"QuoteType": 1,
104-
"NamePos": 93,
105-
"NameEnd": 101
106-
}
96+
"Table": {
97+
"TablePos": 93,
98+
"TableEnd": 101,
99+
"Alias": null,
100+
"Expr": {
101+
"Database": null,
102+
"Table": {
103+
"Name": "my_table",
104+
"QuoteType": 1,
105+
"NamePos": 93,
106+
"NameEnd": 101
107+
}
108+
},
109+
"HasFinal": false
107110
},
111+
"StatementEnd": 101,
112+
"SampleRatio": null,
108113
"HasFinal": false
109114
}
110115
},

0 commit comments

Comments
 (0)