Skip to content

Commit fc2f39c

Browse files
committed
Support ARRAY JOIN as table element in FROM clause
- Add ArrayJoin field to TablesInSelectQueryElement struct - Handle ARRAY JOIN and LEFT ARRAY JOIN in parseTableElementWithJoin - Include token.ARRAY in isJoinKeyword check - Update explain output to handle ArrayJoin in table elements This enables parsing of queries like: FROM table ARRAY JOIN col INNER JOIN other_table USING (...) where ARRAY JOIN is followed by regular JOINs.
1 parent cdf0747 commit fc2f39c

13 files changed

Lines changed: 35 additions & 96 deletions

File tree

ast/ast.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,10 @@ func (t *TablesInSelectQuery) End() token.Position { return t.Position }
126126

127127
// TablesInSelectQueryElement represents a single table element in a SELECT.
128128
type TablesInSelectQueryElement struct {
129-
Position token.Position `json:"-"`
130-
Table *TableExpression `json:"table"`
131-
Join *TableJoin `json:"join,omitempty"`
129+
Position token.Position `json:"-"`
130+
Table *TableExpression `json:"table,omitempty"`
131+
Join *TableJoin `json:"join,omitempty"`
132+
ArrayJoin *ArrayJoinClause `json:"array_join,omitempty"` // For ARRAY JOIN as table element
132133
}
133134

134135
func (t *TablesInSelectQueryElement) Pos() token.Position { return t.Position }

internal/explain/tables.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,23 @@ func explainTablesInSelectQuery(sb *strings.Builder, n *ast.TablesInSelectQuery,
1515
}
1616

1717
func explainTablesInSelectQueryElement(sb *strings.Builder, n *ast.TablesInSelectQueryElement, indent string, depth int) {
18-
children := 1 // table
18+
// If this element contains an ArrayJoin (not a table), handle it separately
19+
if n.ArrayJoin != nil {
20+
fmt.Fprintf(sb, "%sTablesInSelectQueryElement (children 1)\n", indent)
21+
explainArrayJoinClause(sb, n.ArrayJoin, indent+" ", depth+1)
22+
return
23+
}
24+
25+
children := 0
26+
if n.Table != nil {
27+
children++
28+
}
1929
if n.Join != nil {
2030
children++
2131
}
32+
if children == 0 {
33+
children = 1 // Fallback
34+
}
2235
fmt.Fprintf(sb, "%sTablesInSelectQueryElement (children %d)\n", indent, children)
2336
if n.Table != nil {
2437
Node(sb, n.Table, depth+1)

parser/parser.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -793,13 +793,10 @@ func (p *Parser) parseTablesInSelect() *ast.TablesInSelectQuery {
793793
}
794794

795795
func (p *Parser) isJoinKeyword() bool {
796-
// LEFT ARRAY JOIN is handled by parseArrayJoin, not as a regular join
797-
if p.currentIs(token.LEFT) && p.peekIs(token.ARRAY) {
798-
return false
799-
}
800796
switch p.current.Token {
801797
case token.JOIN, token.INNER, token.LEFT, token.RIGHT, token.FULL, token.CROSS,
802-
token.GLOBAL, token.ANY, token.ALL, token.ASOF, token.SEMI, token.ANTI, token.PASTE:
798+
token.GLOBAL, token.ANY, token.ALL, token.ASOF, token.SEMI, token.ANTI, token.PASTE,
799+
token.ARRAY:
803800
return true
804801
case token.COMMA:
805802
return true
@@ -837,6 +834,12 @@ func (p *Parser) parseTableElementWithJoin() *ast.TablesInSelectQueryElement {
837834
return elem
838835
}
839836

837+
// Handle ARRAY JOIN or LEFT ARRAY JOIN
838+
if p.currentIs(token.ARRAY) || (p.currentIs(token.LEFT) && p.peekIs(token.ARRAY)) {
839+
elem.ArrayJoin = p.parseArrayJoin()
840+
return elem
841+
}
842+
840843
// Parse JOIN
841844
join := &ast.TableJoin{
842845
Position: p.current.Pos,
Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
{
22
"explain_todo": {
3-
"stmt16": true,
4-
"stmt17": true,
5-
"stmt18": true,
6-
"stmt19": true,
7-
"stmt20": true,
8-
"stmt21": true,
93
"stmt22": true,
10-
"stmt23": true,
11-
"stmt3": true
4+
"stmt23": true
125
}
136
}
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt3": true
4-
}
5-
}
1+
{}
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
{
22
"explain_todo": {
3-
"stmt14": true,
4-
"stmt29": true,
53
"stmt32": true
64
}
75
}
Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt15": true,
4-
"stmt17": true,
5-
"stmt19": true,
6-
"stmt21": true,
7-
"stmt23": true,
8-
"stmt25": true,
9-
"stmt27": true,
10-
"stmt29": true,
11-
"stmt31": true,
12-
"stmt33": true,
13-
"stmt35": true,
14-
"stmt37": true,
15-
"stmt39": true,
16-
"stmt41": true,
17-
"stmt43": true,
18-
"stmt45": true,
19-
"stmt47": true,
20-
"stmt49": true,
21-
"stmt51": true,
22-
"stmt53": true,
23-
"stmt55": true,
24-
"stmt57": true,
25-
"stmt59": true,
26-
"stmt61": true,
27-
"stmt63": true,
28-
"stmt65": true,
29-
"stmt67": true
30-
}
31-
}
1+
{}
Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt6": true,
4-
"stmt7": true,
5-
"stmt8": true
6-
}
7-
}
1+
{}
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt8": true
4-
}
5-
}
1+
{}
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt4": true,
4-
"stmt5": true
5-
}
6-
}
1+
{}

0 commit comments

Comments
 (0)