Skip to content

Commit 3956e3f

Browse files
kyleconroyclaude
andcommitted
Preserve tuple SpacedCommas flag in EXPLAIN AST output
Track whether tuples have spaces after commas in the source code and preserve this formatting when outputting EXPLAIN AST. This is needed for Ring/Polygon/MultiPolygon type literals to match ClickHouse output. Changes: - Add formatTupleAsStringFromLiteral to respect SpacedCommas flag - Track spacedCommas when parsing tuples in parseGroupedOrTuple - Fixes 00727_concat/stmt44 and 02935_format_with_arbitrary_types/stmt44 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent a874217 commit 3956e3f

File tree

4 files changed

+33
-15
lines changed

4 files changed

+33
-15
lines changed

internal/explain/format.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ func formatExprAsString(expr ast.Expression) string {
576576
case ast.LiteralArray:
577577
return formatArrayAsStringFromLiteral(e)
578578
case ast.LiteralTuple:
579-
return formatTupleAsString(e.Value)
579+
return formatTupleAsStringFromLiteral(e)
580580
default:
581581
return fmt.Sprintf("%v", e.Value)
582582
}
@@ -659,6 +659,24 @@ func formatArrayAsString(val interface{}) string {
659659
return "[" + strings.Join(parts, ", ") + "]"
660660
}
661661

662+
// formatTupleAsStringFromLiteral formats a tuple literal as a string for :: cast syntax
663+
// respecting the SpacedCommas flag to preserve original formatting
664+
func formatTupleAsStringFromLiteral(lit *ast.Literal) string {
665+
exprs, ok := lit.Value.([]ast.Expression)
666+
if !ok {
667+
return "()"
668+
}
669+
var parts []string
670+
for _, e := range exprs {
671+
parts = append(parts, formatElementAsString(e))
672+
}
673+
separator := ","
674+
if lit.SpacedCommas {
675+
separator = ", "
676+
}
677+
return "(" + strings.Join(parts, separator) + ")"
678+
}
679+
662680
// formatTupleAsString formats a tuple literal as a string for :: cast syntax
663681
func formatTupleAsString(val interface{}) string {
664682
exprs, ok := val.([]ast.Expression)
@@ -707,7 +725,7 @@ func formatElementAsString(expr ast.Expression) string {
707725
case ast.LiteralArray:
708726
return formatArrayAsStringFromLiteral(e)
709727
case ast.LiteralTuple:
710-
return formatTupleAsString(e.Value)
728+
return formatTupleAsStringFromLiteral(e)
711729
default:
712730
return fmt.Sprintf("%v", e.Value)
713731
}

parser/expression.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,8 +1238,15 @@ func (p *Parser) parseGroupedOrTuple() ast.Expression {
12381238
// Check if it's a tuple
12391239
if p.currentIs(token.COMMA) {
12401240
elements := []ast.Expression{first}
1241+
spacedCommas := false
12411242
for p.currentIs(token.COMMA) {
1243+
commaPos := p.current.Pos.Offset
12421244
p.nextToken()
1245+
// Check if there's whitespace between comma and next token
1246+
// A comma is 1 byte, so if offset difference > 1, there's whitespace
1247+
if p.current.Pos.Offset > commaPos+1 {
1248+
spacedCommas = true
1249+
}
12431250
// Handle trailing comma: (1,) should create tuple with single element
12441251
if p.currentIs(token.RPAREN) {
12451252
break
@@ -1248,9 +1255,10 @@ func (p *Parser) parseGroupedOrTuple() ast.Expression {
12481255
}
12491256
p.expect(token.RPAREN)
12501257
return &ast.Literal{
1251-
Position: pos,
1252-
Type: ast.LiteralTuple,
1253-
Value: elements,
1258+
Position: pos,
1259+
Type: ast.LiteralTuple,
1260+
Value: elements,
1261+
SpacedCommas: spacedCommas,
12541262
}
12551263
}
12561264

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt44": true
4-
}
5-
}
1+
{}
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt44": true
4-
}
5-
}
1+
{}

0 commit comments

Comments
 (0)