Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions pkg/statement/ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,38 @@ const (
OpReindex
)

// OpKinds returns the closed set of operation shapes, OpUnrecognized first.
// A registry keyed on the operation shape walks it to prove every shape is
// classified, so a shape added here cannot go unclassified.
func OpKinds() []OpKind {
return []OpKind{
OpUnrecognized,
OpCreateTable,
OpAddColumn,
OpDropColumn,
OpAlterColumnType,
OpSetDefault,
OpDropDefault,
OpSetNotNull,
OpDropNotNull,
OpSetColumnOptions,
OpRenameColumn,
OpRenameTable,
OpRenameIndex,
OpSetSchema,
OpSetTablespace,
OpSetRelOptions,
OpAddConstraint,
OpValidateConstraint,
OpDropConstraint,
OpAttachPartition,
OpDetachPartition,
OpCreateIndex,
OpDropIndex,
OpReindex,
}
}

// DefaultKind classifies the DEFAULT expression shape of an added column.
// The shape alone cannot settle whether PostgreSQL rewrites the table: a
// constant still rewrites every row when the column's type is a domain with
Expand Down
40 changes: 33 additions & 7 deletions pkg/statement/statement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,32 @@ func TestKindsIsClosedAndNamed(t *testing.T) {
"Kinds() must enumerate exactly the Kind constants statement.go declares")
}

// The closed set is complete: OpKinds() enumerates exactly the OpKind
// constants the iota block in ops.go declares, and every shape other than
// OpUnrecognized has its own Describe() label rather than the catch-all.
func TestOpKindsIsClosedAndDescribed(t *testing.T) {
kinds := OpKinds()
require.Equal(t, OpUnrecognized, kinds[0], "the catch-all leads the walk")

declared := map[OpKind]struct{}{}
for v := range declaredIotaValues(t, "ops.go", "OpUnrecognized") {
declared[OpKind(v)] = struct{}{}
}
enumerated := make(map[OpKind]struct{}, len(kinds))
unrecognized := Op{Kind: OpUnrecognized}.Describe()
for _, k := range kinds {
_, dup := enumerated[k]
assert.False(t, dup, "duplicate op kind %d", k)
enumerated[k] = struct{}{}
if k != OpUnrecognized {
assert.NotEqual(t, unrecognized, Op{Kind: k}.Describe(),
"op kind %d falls through to the catch-all label", k)
}
}
assert.Equal(t, declared, enumerated,
"OpKinds() must enumerate exactly the OpKind constants ops.go declares")
}

// IndexTargets() is the closed set the eligibility registry walks, so it
// must name every declared target shape except IndexTargetNone, which
// marks statements outside index maintenance rather than a shape of one.
Expand All @@ -412,7 +438,7 @@ func TestIndexTargetsIsClosedAndNamed(t *testing.T) {
targets := IndexTargets()
assert.NotContains(t, targets, IndexTargetNone, "the non-shape is not a target the registry decides")

declared := declaredIotaValues(t, "IndexTargetNone")
declared := declaredIotaValues(t, "statement.go", "IndexTargetNone")
delete(declared, int(IndexTargetNone))
enumerated := make(map[int]struct{}, len(targets))
names := map[string]bool{}
Expand All @@ -433,23 +459,23 @@ func TestIndexTargetsIsClosedAndNamed(t *testing.T) {
// in the iota block that KindOther opens.
func declaredKinds(t *testing.T) map[Kind]struct{} {
t.Helper()
values := declaredIotaValues(t, "KindOther")
values := declaredIotaValues(t, "statement.go", "KindOther")
declared := make(map[Kind]struct{}, len(values))
for v := range values {
declared[Kind(v)] = struct{}{}
}
return declared
}

// declaredIotaValues parses statement.go and returns the value of every
// declaredIotaValues parses file and returns the value of every
// constant in the iota block that the named anchor constant opens.
func declaredIotaValues(t *testing.T, anchor string) map[int]struct{} {
func declaredIotaValues(t *testing.T, file, anchor string) map[int]struct{} {
t.Helper()
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "statement.go", nil, parser.SkipObjectResolution)
parsed, err := parser.ParseFile(fset, file, nil, parser.SkipObjectResolution)
require.NoError(t, err)

for _, decl := range file.Decls {
for _, decl := range parsed.Decls {
gen, ok := decl.(*ast.GenDecl)
if !ok || gen.Tok != token.CONST || len(gen.Specs) == 0 {
continue
Expand All @@ -474,6 +500,6 @@ func declaredIotaValues(t *testing.T, anchor string) map[int]struct{} {
}
return declared
}
t.Fatalf("statement.go declares no const block opened by %s", anchor)
t.Fatalf("%s declares no const block opened by %s", file, anchor)
return nil
}
Loading