Skip to content

Commit 21f020a

Browse files
committed
Add ColumnsListMatcher support for COLUMNS(id, col) syntax
Previously the parser only handled COLUMNS('pattern') as a regex matcher. Now it also handles COLUMNS(col1, col2) as a list matcher, which is output as ColumnsListMatcher in EXPLAIN AST. Changes: - Added Columns field to ColumnsMatcher AST type - Added Qualifier field for qualified matchers like table.COLUMNS(...) - Updated parseColumnsMatcher to parse column lists - Added explainColumnsMatcher function for proper EXPLAIN output
1 parent 0955c9a commit 21f020a

5 files changed

Lines changed: 59 additions & 12 deletions

File tree

ast/ast.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,11 +1137,15 @@ type ReplaceExpr struct {
11371137
func (r *ReplaceExpr) Pos() token.Position { return r.Position }
11381138
func (r *ReplaceExpr) End() token.Position { return r.Position }
11391139

1140-
// ColumnsMatcher represents COLUMNS('pattern') expression.
1140+
// ColumnsMatcher represents COLUMNS('pattern') or COLUMNS(col1, col2) expression.
1141+
// When Pattern is set, it's a regex matcher (ColumnsRegexpMatcher in explain).
1142+
// When Columns is set, it's a list matcher (ColumnsListMatcher in explain).
11411143
type ColumnsMatcher struct {
1142-
Position token.Position `json:"-"`
1143-
Pattern string `json:"pattern"`
1144-
Except []string `json:"except,omitempty"`
1144+
Position token.Position `json:"-"`
1145+
Pattern string `json:"pattern,omitempty"`
1146+
Columns []Expression `json:"columns,omitempty"` // For COLUMNS(id, name) syntax
1147+
Except []string `json:"except,omitempty"`
1148+
Qualifier string `json:"qualifier,omitempty"` // For qualified matchers like table.COLUMNS(...)
11451149
}
11461150

11471151
func (c *ColumnsMatcher) Pos() token.Position { return c.Position }

internal/explain/explain.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func Node(sb *strings.Builder, node interface{}, depth int) {
7474
case *ast.Asterisk:
7575
explainAsterisk(sb, n, indent, depth)
7676
case *ast.ColumnsMatcher:
77-
fmt.Fprintf(sb, "%sColumnsRegexpMatcher\n", indent)
77+
explainColumnsMatcher(sb, n, indent, depth)
7878

7979
// Functions
8080
case *ast.FunctionCall:

internal/explain/expressions.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,41 @@ func explainColumnsTransformers(sb *strings.Builder, n *ast.Asterisk, indent str
585585
}
586586
}
587587

588+
func explainColumnsMatcher(sb *strings.Builder, n *ast.ColumnsMatcher, indent string, depth int) {
589+
// Determine the matcher type based on whether it's a pattern or a list
590+
if len(n.Columns) > 0 {
591+
// ColumnsListMatcher for COLUMNS(col1, col2, ...)
592+
typeName := "ColumnsListMatcher"
593+
if n.Qualifier != "" {
594+
typeName = "QualifiedColumnsListMatcher"
595+
}
596+
if n.Qualifier != "" {
597+
// QualifiedColumnsListMatcher has qualifier as a child
598+
fmt.Fprintf(sb, "%s%s (children %d)\n", indent, typeName, 2)
599+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Qualifier)
600+
} else {
601+
fmt.Fprintf(sb, "%s%s (children %d)\n", indent, typeName, 1)
602+
}
603+
// Output the columns as ExpressionList
604+
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(n.Columns))
605+
for _, col := range n.Columns {
606+
Node(sb, col, depth+2)
607+
}
608+
} else {
609+
// ColumnsRegexpMatcher for COLUMNS('pattern')
610+
typeName := "ColumnsRegexpMatcher"
611+
if n.Qualifier != "" {
612+
typeName = "QualifiedColumnsRegexpMatcher"
613+
}
614+
if n.Qualifier != "" {
615+
fmt.Fprintf(sb, "%s%s (children %d)\n", indent, typeName, 1)
616+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Qualifier)
617+
} else {
618+
fmt.Fprintf(sb, "%s%s\n", indent, typeName)
619+
}
620+
}
621+
}
622+
588623
func explainWithElement(sb *strings.Builder, n *ast.WithElement, indent string, depth int) {
589624
// For WITH elements, we need to show the underlying expression with the name as alias
590625
// When name is empty, don't show the alias part

parser/expression.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1868,10 +1868,24 @@ func (p *Parser) parseColumnsMatcher() ast.Expression {
18681868
return nil
18691869
}
18701870

1871-
// Parse the pattern (string)
1871+
// Parse the arguments - either a string pattern or a list of identifiers
18721872
if p.currentIs(token.STRING) {
1873+
// String pattern: COLUMNS('pattern')
18731874
matcher.Pattern = p.current.Value
18741875
p.nextToken()
1876+
} else {
1877+
// Column list: COLUMNS(col1, col2, ...)
1878+
for !p.currentIs(token.RPAREN) && !p.currentIs(token.EOF) {
1879+
col := p.parseExpression(LOWEST)
1880+
if col != nil {
1881+
matcher.Columns = append(matcher.Columns, col)
1882+
}
1883+
if p.currentIs(token.COMMA) {
1884+
p.nextToken()
1885+
} else {
1886+
break
1887+
}
1888+
}
18751889
}
18761890

18771891
p.expect(token.RPAREN)

parser/testdata/02339_analyzer_matcher_basic/metadata.json

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@
44
"stmt103": true,
55
"stmt105": true,
66
"stmt106": true,
7-
"stmt18": true,
8-
"stmt19": true,
9-
"stmt21": true,
10-
"stmt22": true,
117
"stmt30": true,
128
"stmt31": true,
139
"stmt33": true,
@@ -20,15 +16,13 @@
2016
"stmt52": true,
2117
"stmt54": true,
2218
"stmt55": true,
23-
"stmt6": true,
2419
"stmt60": true,
2520
"stmt61": true,
2621
"stmt63": true,
2722
"stmt64": true,
2823
"stmt66": true,
2924
"stmt67": true,
3025
"stmt69": true,
31-
"stmt7": true,
3226
"stmt70": true,
3327
"stmt79": true,
3428
"stmt80": true,

0 commit comments

Comments
 (0)