Skip to content

Commit c35e37e

Browse files
authored
Allow ARRAY JOIN to be interleaved with other JOIN statements (#230)
1 parent 264c312 commit c35e37e

File tree

86 files changed

+782
-460
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+782
-460
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515

1616
strategy:
1717
matrix:
18-
go-version: [1.18.x]
18+
go-version: [1.21.x]
1919
os: [ ubuntu-latest ]
2020
runs-on: ${{ matrix.os }}
2121

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/AfterShip/clickhouse-sql-parser
22

3-
go 1.18
3+
go 1.21.0
44

55
require (
66
github.com/sebdah/goldie/v2 v2.5.3

parser/ast.go

Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6422,6 +6422,13 @@ func (j *JoinExpr) Pos() Pos {
64226422
}
64236423

64246424
func (j *JoinExpr) End() Pos {
6425+
// Return the rightmost position
6426+
if j.Right != nil {
6427+
return j.Right.End()
6428+
}
6429+
if j.Constraints != nil {
6430+
return j.Constraints.End()
6431+
}
64256432
return j.Left.End()
64266433
}
64276434

@@ -7237,36 +7244,6 @@ func (f *WindowFrameParam) Accept(visitor ASTVisitor) error {
72377244
return visitor.VisitWindowFrameParam(f)
72387245
}
72397246

7240-
type ArrayJoinClause struct {
7241-
ArrayPos Pos
7242-
Type string
7243-
Expr Expr
7244-
}
7245-
7246-
func (a *ArrayJoinClause) Pos() Pos {
7247-
return a.ArrayPos
7248-
}
7249-
7250-
func (a *ArrayJoinClause) End() Pos {
7251-
return a.Expr.End()
7252-
}
7253-
7254-
func (a *ArrayJoinClause) String() string {
7255-
if a.Type != "" {
7256-
return a.Type + " ARRAY JOIN " + a.Expr.String()
7257-
}
7258-
return "ARRAY JOIN " + a.Expr.String()
7259-
}
7260-
7261-
func (a *ArrayJoinClause) Accept(visitor ASTVisitor) error {
7262-
visitor.Enter(a)
7263-
defer visitor.Leave(a)
7264-
if err := a.Expr.Accept(visitor); err != nil {
7265-
return err
7266-
}
7267-
return visitor.VisitArrayJoinExpr(a)
7268-
}
7269-
72707247
type SelectQuery struct {
72717248
SelectPos Pos
72727249
StatementEnd Pos
@@ -7276,7 +7253,6 @@ type SelectQuery struct {
72767253
DistinctOn *DistinctOn
72777254
SelectItems []*SelectItem
72787255
From *FromClause
7279-
ArrayJoin []*ArrayJoinClause
72807256
Window *WindowClause
72817257
Prewhere *PrewhereClause
72827258
Where *WhereClause
@@ -7337,10 +7313,6 @@ func (s *SelectQuery) String() string { // nolint: funlen
73377313
builder.WriteString(" ")
73387314
builder.WriteString(s.From.String())
73397315
}
7340-
for _, arrayJoin := range s.ArrayJoin {
7341-
builder.WriteString(" ")
7342-
builder.WriteString(arrayJoin.String())
7343-
}
73447316
if s.Window != nil {
73457317
builder.WriteString(" ")
73467318
builder.WriteString(s.Window.String())
@@ -7419,11 +7391,6 @@ func (s *SelectQuery) Accept(visitor ASTVisitor) error {
74197391
return err
74207392
}
74217393
}
7422-
for _, arrayJoin := range s.ArrayJoin {
7423-
if err := arrayJoin.Accept(visitor); err != nil {
7424-
return err
7425-
}
7426-
}
74277394
if s.Window != nil {
74287395
if err := s.Window.Accept(visitor); err != nil {
74297396
return err

parser/ast_visitor.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,6 @@ type ASTVisitor interface {
153153
VisitWindowFrameUnbounded(expr *WindowFrameUnbounded) error
154154
VisitWindowFrameNumber(expr *WindowFrameNumber) error
155155
VisitWindowFrameParam(expr *WindowFrameParam) error
156-
VisitArrayJoinExpr(expr *ArrayJoinClause) error
157156
VisitSelectQuery(expr *SelectQuery) error
158157
VisitSubQueryExpr(expr *SubQuery) error
159158
VisitNotExpr(expr *NotExpr) error
@@ -1270,13 +1269,6 @@ func (v *DefaultASTVisitor) VisitWindowFrameParam(expr *WindowFrameParam) error
12701269
return nil
12711270
}
12721271

1273-
func (v *DefaultASTVisitor) VisitArrayJoinExpr(expr *ArrayJoinClause) error {
1274-
if v.Visit != nil {
1275-
return v.Visit(expr)
1276-
}
1277-
return nil
1278-
}
1279-
12801272
func (v *DefaultASTVisitor) VisitSelectQuery(expr *SelectQuery) error {
12811273
if v.Visit != nil {
12821274
return v.Visit(expr)

parser/parser_query.go

Lines changed: 44 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package parser
33
import (
44
"errors"
55
"fmt"
6+
7+
"slices"
68
)
79

810
func (p *Parser) tryParseWithClause(pos Pos) (*WithClause, error) {
@@ -196,11 +198,11 @@ func (p *Parser) parseJoinOp(_ Pos) []string {
196198
case p.matchKeyword(KeywordInner):
197199
modifiers = append(modifiers, p.last().String)
198200
_ = p.lexer.consumeToken()
199-
if p.matchKeyword(KeywordAll) || p.matchKeyword(KeywordAny) || p.matchKeyword(KeywordAsof) {
201+
if p.matchKeyword(KeywordAll) || p.matchKeyword(KeywordAny) || p.matchKeyword(KeywordAsof) || p.matchKeyword(KeywordArray) {
200202
modifiers = append(modifiers, p.last().String)
201203
_ = p.lexer.consumeToken()
202204
}
203-
case p.matchKeyword(KeywordLeft), p.matchKeyword(KeywordRight):
205+
case p.matchKeyword(KeywordLeft):
204206
modifiers = append(modifiers, p.last().String)
205207
_ = p.lexer.consumeToken()
206208
if p.matchKeyword(KeywordOuter) {
@@ -213,6 +215,19 @@ func (p *Parser) parseJoinOp(_ Pos) []string {
213215
modifiers = append(modifiers, p.last().String)
214216
_ = p.lexer.consumeToken()
215217
}
218+
case p.matchKeyword(KeywordRight):
219+
modifiers = append(modifiers, p.last().String)
220+
_ = p.lexer.consumeToken()
221+
if p.matchKeyword(KeywordOuter) {
222+
modifiers = append(modifiers, p.last().String)
223+
_ = p.lexer.consumeToken()
224+
}
225+
if p.matchKeyword(KeywordSemi) || p.matchKeyword(KeywordAnti) ||
226+
p.matchKeyword(KeywordAny) || p.matchKeyword(KeywordAll) ||
227+
p.matchKeyword(KeywordAsof) {
228+
modifiers = append(modifiers, p.last().String)
229+
_ = p.lexer.consumeToken()
230+
}
216231
case p.matchKeyword(KeywordFull):
217232
modifiers = append(modifiers, p.last().String)
218233
_ = p.lexer.consumeToken()
@@ -224,6 +239,9 @@ func (p *Parser) parseJoinOp(_ Pos) []string {
224239
modifiers = append(modifiers, p.last().String)
225240
_ = p.lexer.consumeToken()
226241
}
242+
case p.matchKeyword(KeywordArray):
243+
modifiers = append(modifiers, p.last().String)
244+
_ = p.lexer.consumeToken()
227245
}
228246
return modifiers
229247
}
@@ -281,6 +299,30 @@ func (p *Parser) parseJoinRightExpr(pos Pos) (expr Expr, err error) {
281299
}
282300

283301
modifiers = append(modifiers, KeywordJoin)
302+
303+
// Check if this is an ARRAY JOIN
304+
if slices.Contains(modifiers, KeywordArray) {
305+
// For ARRAY JOIN, parse column expression list instead of table expression
306+
expr, err = p.parseColumnExprList(p.Pos())
307+
if err != nil {
308+
return nil, err
309+
}
310+
311+
// ARRAY JOIN doesn't have constraints (ON/USING)
312+
// try parse next join
313+
rightExpr, err = p.parseJoinRightExpr(p.Pos())
314+
if err != nil {
315+
return nil, err
316+
}
317+
return &JoinExpr{
318+
JoinPos: pos,
319+
Left: expr,
320+
Right: rightExpr,
321+
Modifiers: modifiers,
322+
Constraints: nil,
323+
}, nil
324+
}
325+
284326
expr, err = p.parseJoinTableExpr(p.Pos())
285327
if err != nil {
286328
return nil, err
@@ -852,41 +894,6 @@ func (p *Parser) parseWindowClause(pos Pos) (*WindowClause, error) {
852894
}, nil
853895
}
854896

855-
func (p *Parser) tryParseArrayJoinClause(pos Pos) (*ArrayJoinClause, error) {
856-
if !p.matchKeyword(KeywordLeft) && !p.matchKeyword(KeywordInner) && !p.matchKeyword(KeywordArray) {
857-
return nil, nil
858-
}
859-
return p.parseArrayJoinClause(pos)
860-
}
861-
862-
func (p *Parser) parseArrayJoinClause(_ Pos) (*ArrayJoinClause, error) {
863-
var typ string
864-
switch {
865-
case p.matchKeyword(KeywordLeft), p.matchKeyword(KeywordInner):
866-
typ = p.last().String
867-
_ = p.lexer.consumeToken()
868-
}
869-
arrayPos := p.Pos()
870-
if err := p.expectKeyword(KeywordArray); err != nil {
871-
return nil, err
872-
}
873-
874-
if err := p.expectKeyword(KeywordJoin); err != nil {
875-
return nil, err
876-
}
877-
878-
expr, err := p.parseColumnExprList(p.Pos())
879-
if err != nil {
880-
return nil, err
881-
}
882-
883-
return &ArrayJoinClause{
884-
ArrayPos: arrayPos,
885-
Type: typ,
886-
Expr: expr,
887-
}, nil
888-
}
889-
890897
func (p *Parser) tryParseHavingClause(pos Pos) (*HavingClause, error) {
891898
if !p.matchKeyword(KeywordHaving) {
892899
return nil, nil
@@ -1009,18 +1016,6 @@ func (p *Parser) parseSelectStmt(pos Pos) (*SelectQuery, error) { // nolint: fun
10091016
if from != nil {
10101017
statementEnd = from.End()
10111018
}
1012-
var arrayJoins []*ArrayJoinClause
1013-
for {
1014-
arrayJoin, err := p.tryParseArrayJoinClause(p.Pos())
1015-
if err != nil {
1016-
return nil, err
1017-
}
1018-
if arrayJoin == nil {
1019-
break
1020-
}
1021-
arrayJoins = append(arrayJoins, arrayJoin)
1022-
statementEnd = arrayJoin.End()
1023-
}
10241019
prewhere, err := p.tryParsePrewhereClause(p.Pos())
10251020
if err != nil {
10261021
return nil, err
@@ -1129,7 +1124,6 @@ func (p *Parser) parseSelectStmt(pos Pos) (*SelectQuery, error) { // nolint: fun
11291124
DistinctOn: distinctOn,
11301125
SelectItems: selectItems,
11311126
From: from,
1132-
ArrayJoin: arrayJoins,
11331127
Window: window,
11341128
Prewhere: prewhere,
11351129
Where: where,

parser/parser_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ func TestParser_InvalidSyntax(t *testing.T) {
141141
"SELECT n FROM t ORDER BY n WITH FILL INTERPOLATE (x", // Missing closing paren
142142
"SELECT n FROM t ORDER BY n WITH FILL INTERPOLATE x AS x + 1", // Missing parens around column list
143143
"ALTER TABLE foo_mv MODIFY QUERY AS SELECT * FROM baz", // MODIFY QUERY followed by an invalid query
144+
// Invalid ARRAY JOIN types (only ARRAY JOIN, LEFT ARRAY JOIN, and INNER ARRAY JOIN are valid)
145+
"SELECT * FROM t RIGHT ARRAY JOIN arr AS a", // RIGHT ARRAY JOIN not supported
146+
"SELECT * FROM t FULL ARRAY JOIN arr AS a", // FULL ARRAY JOIN not supported
144147
}
145148
for _, sql := range invalidSQLs {
146149
parser := NewParser(sql)

parser/testdata/basic/output/quantile_functions.sql.golden.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@
108108
}
109109
],
110110
"From": null,
111-
"ArrayJoin": null,
112111
"Window": null,
113112
"Prewhere": null,
114113
"Where": null,

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,6 @@
523523
"HasFinal": false
524524
}
525525
},
526-
"ArrayJoin": null,
527526
"Window": null,
528527
"Prewhere": null,
529528
"Where": {

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@
119119
"HasFinal": false
120120
}
121121
},
122-
"ArrayJoin": null,
123122
"Window": null,
124123
"Prewhere": null,
125124
"Where": null,

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,6 @@
490490
"HasFinal": false
491491
}
492492
},
493-
"ArrayJoin": null,
494493
"Window": null,
495494
"Prewhere": null,
496495
"Where": {

0 commit comments

Comments
 (0)