Skip to content

Commit f61bbc5

Browse files
committed
Add support for qualified COLUMNS matchers (test_table.COLUMNS)
- Detect qualified COLUMNS patterns when parsing dotted identifiers - Parse test_table.COLUMNS(id) as QualifiedColumnsListMatcher - Parse test_table.COLUMNS('pattern') as QualifiedColumnsRegexpMatcher - Add parseQualifiedColumnsMatcher function to handle these cases
1 parent 58fb390 commit f61bbc5

4 files changed

Lines changed: 58 additions & 15 deletions

File tree

parser/expression.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,11 @@ func (p *Parser) parseIdentifierOrFunction() ast.Expression {
512512

513513
// Check for function call after qualified name
514514
if p.currentIs(token.LPAREN) {
515+
// Special case: qualified COLUMNS matcher (e.g., test_table.COLUMNS(id))
516+
if len(parts) >= 2 && strings.ToUpper(parts[len(parts)-1]) == "COLUMNS" {
517+
qualifier := strings.Join(parts[:len(parts)-1], ".")
518+
return p.parseQualifiedColumnsMatcher(qualifier, pos)
519+
}
515520
return p.parseFunctionCall(strings.Join(parts, "."), pos)
516521
}
517522

@@ -2191,6 +2196,58 @@ func (p *Parser) parseColumnsMatcher() ast.Expression {
21912196
return matcher
21922197
}
21932198

2199+
// parseQualifiedColumnsMatcher parses qualified COLUMNS matchers like test_table.COLUMNS(id)
2200+
// The qualifier is passed in and we're already positioned at LPAREN
2201+
func (p *Parser) parseQualifiedColumnsMatcher(qualifier string, pos token.Position) ast.Expression {
2202+
matcher := &ast.ColumnsMatcher{
2203+
Position: pos,
2204+
Qualifier: qualifier,
2205+
}
2206+
2207+
p.nextToken() // skip LPAREN
2208+
2209+
// Parse the arguments - either a string pattern or a list of identifiers
2210+
if p.currentIs(token.STRING) {
2211+
// String pattern: COLUMNS('pattern')
2212+
matcher.Pattern = p.current.Value
2213+
p.nextToken()
2214+
} else {
2215+
// Column list: COLUMNS(col1, col2, ...)
2216+
for !p.currentIs(token.RPAREN) && !p.currentIs(token.EOF) {
2217+
col := p.parseExpression(LOWEST)
2218+
if col != nil {
2219+
matcher.Columns = append(matcher.Columns, col)
2220+
}
2221+
if p.currentIs(token.COMMA) {
2222+
p.nextToken()
2223+
} else {
2224+
break
2225+
}
2226+
}
2227+
}
2228+
2229+
p.expect(token.RPAREN)
2230+
2231+
// Handle EXCEPT
2232+
if p.currentIs(token.EXCEPT) {
2233+
p.nextToken()
2234+
if p.expect(token.LPAREN) {
2235+
for !p.currentIs(token.RPAREN) && !p.currentIs(token.EOF) {
2236+
if p.currentIs(token.IDENT) {
2237+
matcher.Except = append(matcher.Except, p.current.Value)
2238+
p.nextToken()
2239+
}
2240+
if p.currentIs(token.COMMA) {
2241+
p.nextToken()
2242+
}
2243+
}
2244+
p.expect(token.RPAREN)
2245+
}
2246+
}
2247+
2248+
return matcher
2249+
}
2250+
21942251
func (p *Parser) parseArrayConstructor() ast.Expression {
21952252
pos := p.current.Pos
21962253
p.nextToken() // skip ARRAY

parser/testdata/02339_analyzer_matcher_basic/metadata.json

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,6 @@
22
"explain_todo": {
33
"stmt105": true,
44
"stmt106": true,
5-
"stmt30": true,
6-
"stmt31": true,
7-
"stmt33": true,
8-
"stmt34": true,
9-
"stmt36": true,
10-
"stmt37": true,
11-
"stmt48": true,
12-
"stmt49": true,
13-
"stmt51": true,
14-
"stmt52": true,
15-
"stmt54": true,
16-
"stmt55": true,
175
"stmt63": true,
186
"stmt64": true,
197
"stmt66": true,

parser/testdata/03101_analyzer_identifiers_4/metadata.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
{
22
"explain_todo": {
33
"stmt14": true,
4-
"stmt17": true,
54
"stmt18": true,
65
"stmt7": true,
76
"stmt9": true
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{
22
"explain_todo": {
3-
"stmt3": true,
4-
"stmt4": true
3+
"stmt3": true
54
}
65
}

0 commit comments

Comments
 (0)