Skip to content

Commit 1fb8b3e

Browse files
committed
Show enums whose zero value means something, and chain difftest edits
A dump omits zero-valued fields, which made a snapshot ambiguous wherever the zero is the answer: UNBOUNDED PRECEDING, PRIMARY KEY, the NULL literal and four others were printing as nothing at all. An enum whose zero means "absent" says so in its String -- "none", "default", "unspecified" -- which turns out to be exactly the right test. Seven lines gained across the snapshots, no noise added. difftest's -depth applied every extra round to the original mutation rather than to the previous one, so it never got past two edits. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JzBeCg7rjweVW3uGPg5G7T
1 parent c942d5c commit 1fb8b3e

7 files changed

Lines changed: 50 additions & 6 deletions

File tree

cmd/difftest/main.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,19 @@ func main() {
112112
for src := range work {
113113
for _, m := range mutations(src, *per, rng) {
114114
check(r, m, rep)
115-
// A second edit reaches shapes one cannot: two
116-
// unbalanced brackets, a keyword in a position only
117-
// another edit could open up.
115+
// Further edits reach shapes one cannot: two unbalanced
116+
// brackets, a keyword in a position only another edit
117+
// could open up. Each round builds on the last.
118+
cur := m
118119
for d := 1; d < *depth; d++ {
119-
for _, m2 := range mutations(m, 2, rng) {
120+
next := mutations(cur, 2, rng)
121+
if len(next) == 0 {
122+
break
123+
}
124+
for _, m2 := range next {
120125
check(r, m2, rep)
121126
}
127+
cur = next[0]
122128
}
123129
}
124130
}

internal/dump/dump.go

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,34 @@ type field struct {
139139
value reflect.Value
140140
}
141141

142+
// meaningfulZero reports whether a zero value still says something. Several
143+
// enums have a meaningful zero -- FrameBoundType(0) is UNBOUNDED PRECEDING,
144+
// TableConstraintKind(0) is PRIMARY KEY -- and omitting those would make a
145+
// snapshot ambiguous. An enum whose zero means "absent" says so in its
146+
// String, which is exactly the ones worth leaving out.
147+
func meaningfulZero(v reflect.Value) bool {
148+
// Only enums: a nil node pointer also implements Stringer, and calling
149+
// String on it is a crash rather than an answer.
150+
switch v.Kind() {
151+
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
152+
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
153+
default:
154+
return false
155+
}
156+
s, ok := v.Interface().(fmt.Stringer)
157+
if !ok {
158+
return false
159+
}
160+
switch s.String() {
161+
case "", "none", "default", "unspecified":
162+
return false
163+
}
164+
return true
165+
}
166+
142167
// fields selects the fields worth printing: zero values are omitted so that
143-
// a dump shows what a statement said rather than what it did not.
168+
// a dump shows what a statement said rather than what it did not, except
169+
// where the zero value is itself the statement (see meaningfulZero).
144170
func (d *dumper) fields(v reflect.Value) []field {
145171
t := v.Type()
146172
var out []field
@@ -160,7 +186,7 @@ func (d *dumper) fields(v reflect.Value) []field {
160186
if sf.Name == "Raw" && !d.opts.Raw {
161187
continue
162188
}
163-
if fv.IsZero() {
189+
if fv.IsZero() && !meaningfulZero(fv) {
164190
continue
165191
}
166192
out = append(out, field{sf.Name, fv})

parser/parser.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ func LineCol(src string, offset int) (line, col int) {
6969
}
7070

7171
// Parse reads SQL from r and returns one ast.Stmt per statement.
72+
//
73+
// The context is accepted so the signature matches the sibling parsers
74+
// sqlc uses, and is not consulted: parsing is a bounded, allocation-light
75+
// pass over the input, and the recursion limit keeps even hostile input
76+
// from taking long enough to be worth cancelling.
7277
func Parse(ctx context.Context, r io.Reader) ([]ast.Stmt, error) {
7378
src, err := io.ReadAll(r)
7479
if err != nil {

parser/testdata/ast/ddl.tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ CreateTableStmt{
198198
Name: "full"
199199
Raw: "full"
200200
}
201+
Action: ForeignKeyAction(NO ACTION)
201202
}
202203
]
203204
}
@@ -295,6 +296,7 @@ CreateTableStmt{
295296
Name: "pk"
296297
Raw: "pk"
297298
}
299+
Kind: TableConstraintKind(PRIMARY KEY)
298300
Columns: [
299301
OrderingTerm{
300302
Span: 447:448
@@ -743,6 +745,7 @@ AlterTableStmt{
743745
Raw: "t"
744746
}
745747
}
748+
Action: AlterAction(RENAME TO)
746749
NewName: Ident{
747750
Span: 1126:1127
748751
Name: "u"

parser/testdata/ast/expressions.tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,7 @@ SelectStmt{
11991199
Span: 985:989
12001200
Expr: Literal{
12011201
Span: 985:989
1202+
Kind: LiteralKind(NULL)
12021203
Value: "NULL"
12031204
Raw: "NULL"
12041205
}
@@ -1289,6 +1290,7 @@ SelectStmt{
12891290
Span: 1057:1058
12901291
Expr: BindParam{
12911292
Span: 1057:1058
1293+
Kind: ParamKind(?)
12921294
Number: 1
12931295
Raw: "?"
12941296
}

parser/testdata/ast/misc.tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
BeginStmt{
22
Span: 58:64
3+
Type: TransactionType(DEFERRED)
34
}
45
BeginStmt{
56
Span: 65:98

parser/testdata/ast/select.tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,7 @@ SelectStmt{
942942
Frame: FrameType(RANGE)
943943
StartBound: FrameBound{
944944
Span: 807:826
945+
Type: FrameBoundType(UNBOUNDED PRECEDING)
945946
}
946947
EndBound: FrameBound{
947948
Span: 831:842

0 commit comments

Comments
 (0)