Skip to content

Commit 7731b59

Browse files
kyleconroyclaude
andcommitted
feat(ast): add and improve Format methods for SQL AST nodes
Add Format methods: - A_ArrayExpr: Format ARRAY[...] literals - NullIfExpr: Format NULLIF(arg1, arg2) function calls - OnConflictClause: Format ON CONFLICT ... DO UPDATE/NOTHING - InferClause: Format conflict target (columns) or ON CONSTRAINT - IndexElem: Format index elements for conflict targets - WindowDef: Format window definitions with PARTITION BY, ORDER BY, and frame clauses Improve existing Format methods: - A_Expr: Add BETWEEN, NOT BETWEEN, ILIKE, SIMILAR TO, IS DISTINCT FROM handling - A_Expr_Kind: Add all expression kind constants - CaseExpr: Handle CASE with test argument and optional ELSE - DeleteStmt: Add USING clause formatting - FuncCall: Add DISTINCT, ORDER BY, FILTER, and OVER clause support - InsertStmt: Delegate to OnConflictClause.Format - JoinExpr: Add RIGHT JOIN, FULL JOIN, NATURAL, and USING clause - LockingClause: Add OF clause, SKIP LOCKED, NOWAIT, and fix strength values - RangeFunction: Add LATERAL support and fix alias spacing - SelectStmt: Add HAVING clause formatting These changes reduce test failures from 135 to 102. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent ab9ce9e commit 7731b59

16 files changed

+350
-24
lines changed

internal/sql/ast/a_array_expr.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,12 @@ type A_ArrayExpr struct {
88
func (n *A_ArrayExpr) Pos() int {
99
return n.Location
1010
}
11+
12+
func (n *A_ArrayExpr) Format(buf *TrackedBuffer) {
13+
if n == nil {
14+
return
15+
}
16+
buf.WriteString("ARRAY[")
17+
buf.join(n.Elements, ", ")
18+
buf.WriteString("]")
19+
}

internal/sql/ast/a_expr.go

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,69 @@ func (n *A_Expr) Format(buf *TrackedBuffer) {
1616
if n == nil {
1717
return
1818
}
19-
buf.astFormat(n.Lexpr)
20-
buf.WriteString(" ")
2119
switch n.Kind {
2220
case A_Expr_Kind_IN:
21+
buf.astFormat(n.Lexpr)
2322
buf.WriteString(" IN (")
2423
buf.astFormat(n.Rexpr)
2524
buf.WriteString(")")
2625
case A_Expr_Kind_LIKE:
26+
buf.astFormat(n.Lexpr)
2727
buf.WriteString(" LIKE ")
2828
buf.astFormat(n.Rexpr)
29+
case A_Expr_Kind_ILIKE:
30+
buf.astFormat(n.Lexpr)
31+
buf.WriteString(" ILIKE ")
32+
buf.astFormat(n.Rexpr)
33+
case A_Expr_Kind_SIMILAR:
34+
buf.astFormat(n.Lexpr)
35+
buf.WriteString(" SIMILAR TO ")
36+
buf.astFormat(n.Rexpr)
37+
case A_Expr_Kind_BETWEEN:
38+
buf.astFormat(n.Lexpr)
39+
buf.WriteString(" BETWEEN ")
40+
if l, ok := n.Rexpr.(*List); ok && len(l.Items) == 2 {
41+
buf.astFormat(l.Items[0])
42+
buf.WriteString(" AND ")
43+
buf.astFormat(l.Items[1])
44+
}
45+
case A_Expr_Kind_NOT_BETWEEN:
46+
buf.astFormat(n.Lexpr)
47+
buf.WriteString(" NOT BETWEEN ")
48+
if l, ok := n.Rexpr.(*List); ok && len(l.Items) == 2 {
49+
buf.astFormat(l.Items[0])
50+
buf.WriteString(" AND ")
51+
buf.astFormat(l.Items[1])
52+
}
53+
case A_Expr_Kind_DISTINCT:
54+
buf.astFormat(n.Lexpr)
55+
buf.WriteString(" IS DISTINCT FROM ")
56+
buf.astFormat(n.Rexpr)
57+
case A_Expr_Kind_NOT_DISTINCT:
58+
buf.astFormat(n.Lexpr)
59+
buf.WriteString(" IS NOT DISTINCT FROM ")
60+
buf.astFormat(n.Rexpr)
61+
case A_Expr_Kind_OP:
62+
// Standard binary operator
63+
if set(n.Lexpr) {
64+
buf.astFormat(n.Lexpr)
65+
buf.WriteString(" ")
66+
}
67+
buf.astFormat(n.Name)
68+
if set(n.Rexpr) {
69+
buf.WriteString(" ")
70+
buf.astFormat(n.Rexpr)
71+
}
2972
default:
73+
// Fallback for other cases
74+
if set(n.Lexpr) {
75+
buf.astFormat(n.Lexpr)
76+
buf.WriteString(" ")
77+
}
3078
buf.astFormat(n.Name)
31-
buf.WriteString(" ")
32-
buf.astFormat(n.Rexpr)
79+
if set(n.Rexpr) {
80+
buf.WriteString(" ")
81+
buf.astFormat(n.Rexpr)
82+
}
3383
}
3484
}

internal/sql/ast/a_expr_kind.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,20 @@ package ast
33
type A_Expr_Kind uint
44

55
const (
6-
A_Expr_Kind_IN A_Expr_Kind = 7
7-
A_Expr_Kind_LIKE A_Expr_Kind = 8
6+
A_Expr_Kind_OP A_Expr_Kind = 1
7+
A_Expr_Kind_OP_ANY A_Expr_Kind = 2
8+
A_Expr_Kind_OP_ALL A_Expr_Kind = 3
9+
A_Expr_Kind_DISTINCT A_Expr_Kind = 4
10+
A_Expr_Kind_NOT_DISTINCT A_Expr_Kind = 5
11+
A_Expr_Kind_NULLIF A_Expr_Kind = 6
12+
A_Expr_Kind_IN A_Expr_Kind = 7
13+
A_Expr_Kind_LIKE A_Expr_Kind = 8
14+
A_Expr_Kind_ILIKE A_Expr_Kind = 9
15+
A_Expr_Kind_SIMILAR A_Expr_Kind = 10
16+
A_Expr_Kind_BETWEEN A_Expr_Kind = 11
17+
A_Expr_Kind_NOT_BETWEEN A_Expr_Kind = 12
18+
A_Expr_Kind_BETWEEN_SYM A_Expr_Kind = 13
19+
A_Expr_Kind_NOT_BETWEEN_SYM A_Expr_Kind = 14
820
)
921

1022
func (n *A_Expr_Kind) Pos() int {

internal/sql/ast/case_expr.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,14 @@ func (n *CaseExpr) Format(buf *TrackedBuffer) {
1919
return
2020
}
2121
buf.WriteString("CASE ")
22-
buf.astFormat(n.Args)
23-
buf.WriteString(" ELSE ")
24-
buf.astFormat(n.Defresult)
25-
buf.WriteString(" END ")
22+
if set(n.Arg) {
23+
buf.astFormat(n.Arg)
24+
buf.WriteString(" ")
25+
}
26+
buf.join(n.Args, " ")
27+
if set(n.Defresult) {
28+
buf.WriteString(" ELSE ")
29+
buf.astFormat(n.Defresult)
30+
}
31+
buf.WriteString(" END")
2632
}

internal/sql/ast/delete_stmt.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ func (n *DeleteStmt) Format(buf *TrackedBuffer) {
2828
buf.astFormat(n.Relations)
2929
}
3030

31+
if items(n.UsingClause) {
32+
buf.WriteString(" USING ")
33+
buf.join(n.UsingClause, ", ")
34+
}
35+
3136
if set(n.WhereClause) {
3237
buf.WriteString(" WHERE ")
3338
buf.astFormat(n.WhereClause)

internal/sql/ast/func_call.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,26 @@ func (n *FuncCall) Format(buf *TrackedBuffer) {
2424
}
2525
buf.astFormat(n.Func)
2626
buf.WriteString("(")
27+
if n.AggDistinct {
28+
buf.WriteString("DISTINCT ")
29+
}
2730
if n.AggStar {
2831
buf.WriteString("*")
2932
} else {
3033
buf.astFormat(n.Args)
3134
}
35+
if items(n.AggOrder) {
36+
buf.WriteString(" ORDER BY ")
37+
buf.join(n.AggOrder, ", ")
38+
}
3239
buf.WriteString(")")
40+
if set(n.AggFilter) {
41+
buf.WriteString(" FILTER (WHERE ")
42+
buf.astFormat(n.AggFilter)
43+
buf.WriteString(")")
44+
}
45+
if n.Over != nil {
46+
buf.WriteString(" OVER ")
47+
buf.astFormat(n.Over)
48+
}
3349
}

internal/sql/ast/index_elem.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,14 @@ type IndexElem struct {
1313
func (n *IndexElem) Pos() int {
1414
return 0
1515
}
16+
17+
func (n *IndexElem) Format(buf *TrackedBuffer) {
18+
if n == nil {
19+
return
20+
}
21+
if n.Name != nil && *n.Name != "" {
22+
buf.WriteString(*n.Name)
23+
} else if set(n.Expr) {
24+
buf.astFormat(n.Expr)
25+
}
26+
}

internal/sql/ast/infer_clause.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,21 @@ type InferClause struct {
1010
func (n *InferClause) Pos() int {
1111
return n.Location
1212
}
13+
14+
func (n *InferClause) Format(buf *TrackedBuffer) {
15+
if n == nil {
16+
return
17+
}
18+
if n.Conname != nil && *n.Conname != "" {
19+
buf.WriteString("ON CONSTRAINT ")
20+
buf.WriteString(*n.Conname)
21+
} else if items(n.IndexElems) {
22+
buf.WriteString("(")
23+
buf.join(n.IndexElems, ", ")
24+
buf.WriteString(")")
25+
if set(n.WhereClause) {
26+
buf.WriteString(" WHERE ")
27+
buf.astFormat(n.WhereClause)
28+
}
29+
}
30+
}

internal/sql/ast/insert_stmt.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ func (n *InsertStmt) Format(buf *TrackedBuffer) {
3939
}
4040

4141
if n.OnConflictClause != nil {
42-
buf.WriteString(" ON CONFLICT DO NOTHING ")
42+
buf.WriteString(" ")
43+
buf.astFormat(n.OnConflictClause)
4344
}
4445

4546
if items(n.ReturningList) {

internal/sql/ast/join_expr.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,28 @@ func (n *JoinExpr) Format(buf *TrackedBuffer) {
2020
return
2121
}
2222
buf.astFormat(n.Larg)
23+
if n.IsNatural {
24+
buf.WriteString(" NATURAL")
25+
}
2326
switch n.Jointype {
2427
case JoinTypeLeft:
2528
buf.WriteString(" LEFT JOIN ")
29+
case JoinTypeRight:
30+
buf.WriteString(" RIGHT JOIN ")
31+
case JoinTypeFull:
32+
buf.WriteString(" FULL JOIN ")
2633
case JoinTypeInner:
27-
buf.WriteString(" INNER JOIN ")
34+
buf.WriteString(" JOIN ")
2835
default:
2936
buf.WriteString(" JOIN ")
3037
}
3138
buf.astFormat(n.Rarg)
32-
buf.WriteString(" ON ")
33-
if n.Jointype == JoinTypeInner {
34-
if set(n.Quals) {
35-
buf.astFormat(n.Quals)
36-
} else {
37-
buf.WriteString("TRUE")
38-
}
39-
} else {
39+
if items(n.UsingClause) {
40+
buf.WriteString(" USING (")
41+
buf.join(n.UsingClause, ", ")
42+
buf.WriteString(")")
43+
} else if set(n.Quals) {
44+
buf.WriteString(" ON ")
4045
buf.astFormat(n.Quals)
4146
}
4247
}

0 commit comments

Comments
 (0)