Skip to content

Commit 3ed41f6

Browse files
committed
Add SKIP and SKIP REGEXP support for JSON type parameters
This adds proper parsing and formatting for SKIP clauses in JSON type parameters, including: - SKIP path (e.g., SKIP a.b for dotted paths) - SKIP REGEXP 'pattern' (for regex-based path matching) These are used in JSON type casts like: json::JSON(SKIP a.b, max_dynamic_paths=2) json::JSON(SKIP REGEXP '.*a.*', max_dynamic_paths=2)
1 parent 4aa3243 commit 3ed41f6

26 files changed

Lines changed: 92 additions & 146 deletions

File tree

internal/explain/format.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,19 @@ func FormatDataType(dt *ast.DataType) string {
237237
} else if binExpr, ok := p.(*ast.BinaryExpr); ok {
238238
// Binary expression (e.g., 'hello' = 1 for Enum types)
239239
params = append(params, formatBinaryExprForType(binExpr))
240+
} else if fn, ok := p.(*ast.FunctionCall); ok {
241+
// Function call (e.g., SKIP for JSON types)
242+
if fn.Name == "SKIP" && len(fn.Arguments) > 0 {
243+
if ident, ok := fn.Arguments[0].(*ast.Identifier); ok {
244+
params = append(params, "SKIP "+ident.Name())
245+
}
246+
} else if fn.Name == "SKIP REGEXP" && len(fn.Arguments) > 0 {
247+
if lit, ok := fn.Arguments[0].(*ast.Literal); ok {
248+
params = append(params, fmt.Sprintf("SKIP REGEXP \\\\\\'%s\\\\\\'", lit.Value))
249+
}
250+
} else {
251+
params = append(params, fmt.Sprintf("%v", p))
252+
}
240253
} else {
241254
params = append(params, fmt.Sprintf("%v", p))
242255
}

parser/parser.go

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3266,6 +3266,64 @@ func (p *Parser) parseDataType() *ast.DataType {
32663266

32673267
// Parse type parameters, but stop on keywords that can't be part of type params
32683268
for !p.currentIs(token.RPAREN) && !p.currentIs(token.EOF) && !p.currentIs(token.COLLATE) {
3269+
var param ast.Expression
3270+
3271+
// Special handling for SKIP in JSON/OBJECT types: SKIP path or SKIP REGEXP 'pattern'
3272+
if isObjectType && (p.currentIs(token.IDENT) || p.current.Token.IsKeyword()) && strings.ToUpper(p.current.Value) == "SKIP" {
3273+
pos := p.current.Pos
3274+
p.nextToken() // consume SKIP
3275+
3276+
// Check for SKIP REGEXP 'pattern'
3277+
if p.currentIs(token.REGEXP) || (p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "REGEXP") {
3278+
p.nextToken() // consume REGEXP
3279+
// Parse the pattern string
3280+
if p.currentIs(token.STRING) {
3281+
pattern := p.current.Value
3282+
p.nextToken()
3283+
param = &ast.FunctionCall{
3284+
Position: pos,
3285+
Name: "SKIP REGEXP",
3286+
Arguments: []ast.Expression{&ast.Literal{Position: pos, Value: pattern, Type: ast.LiteralString}},
3287+
}
3288+
}
3289+
} else {
3290+
// Parse dotted path: a, a.b, a.b.c, etc.
3291+
var pathParts []string
3292+
for {
3293+
if p.currentIs(token.IDENT) || p.current.Token.IsKeyword() {
3294+
pathParts = append(pathParts, p.current.Value)
3295+
p.nextToken()
3296+
}
3297+
if p.currentIs(token.DOT) {
3298+
p.nextToken() // consume dot
3299+
} else {
3300+
break
3301+
}
3302+
}
3303+
if len(pathParts) > 0 {
3304+
param = &ast.FunctionCall{
3305+
Position: pos,
3306+
Name: "SKIP",
3307+
Arguments: []ast.Expression{&ast.Identifier{Position: pos, Parts: pathParts}},
3308+
}
3309+
}
3310+
}
3311+
// Wrap in ObjectTypeArgument
3312+
if param != nil {
3313+
param = &ast.ObjectTypeArgument{
3314+
Position: param.Pos(),
3315+
Expr: param,
3316+
}
3317+
dt.Parameters = append(dt.Parameters, param)
3318+
}
3319+
if p.currentIs(token.COMMA) {
3320+
p.nextToken()
3321+
} else {
3322+
break
3323+
}
3324+
continue
3325+
}
3326+
32693327
// Check if this is a named parameter: identifier followed by a type name
32703328
// e.g., "a UInt32" where "a" is the name and "UInt32" is the type
32713329
isNamedParam := false
@@ -3291,7 +3349,6 @@ func (p *Parser) parseDataType() *ast.DataType {
32913349
}
32923350
}
32933351

3294-
var param ast.Expression
32953352
if isNamedParam {
32963353
// Parse as name + type pair
32973354
pos := p.current.Pos

parser/testdata/00564_temporary_table_management/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
"stmt3": true,
4-
"stmt4": true,
54
"stmt5": true,
65
"stmt7": true
76
}
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt6": true
4-
}
5-
}
1+
{}
Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt20": true,
4-
"stmt24": true,
5-
"stmt26": true,
6-
"stmt28": true,
7-
"stmt30": true,
8-
"stmt32": true,
9-
"stmt34": true,
10-
"stmt38": true,
11-
"stmt42": true,
12-
"stmt46": true,
13-
"stmt49": true,
14-
"stmt61": true
15-
}
16-
}
1+
{}
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt4": 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-
"stmt7": true,
4-
"stmt8": true
5-
}
6-
}
1+
{}
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
{
22
"explain_todo": {
33
"stmt107": true,
4-
"stmt114": true,
54
"stmt26": true,
65
"stmt56": true,
76
"stmt61": true,
8-
"stmt69": true,
97
"stmt73": true
108
}
119
}
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt3": true,
4-
"stmt4": true
5-
}
6-
}
1+
{}
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt2": true,
4-
"stmt4": true
5-
}
6-
}
1+
{}

0 commit comments

Comments
 (0)