-
Notifications
You must be signed in to change notification settings - Fork 30
Description
Bug: DiffSource interface field on Diff breaks JSON deserialization in debug mode
Note: Excuse the AI written bug report. Was having some issues apply a plan in debug mode and had claude check out what could be the possible culprit. Feel free to close if this is not the case.
Summary
Plans generated with --debug produce JSON that cannot be deserialized by FromJSON(). The Diff.Source field is typed as a Go interface (DiffSource), which Go's json.Unmarshal cannot reconstruct from JSON.
Steps to Reproduce
- Create a schema change that produces diffs (e.g., alter a materialized view)
- Generate a plan with debug mode:
pgschema plan --debug -o json:plan.json ... - Attempt to apply it:
pgschema apply --plan plan.json ... - Apply fails with a JSON unmarshal error
Root Cause
In internal/diff/diff.go (line 254):
type DiffSource interface {
GetObjectName() string
}
type Diff struct {
Statements []SQLStatement `json:"statements"`
Type DiffType `json:"type"`
Operation DiffOperation `json:"operation"`
Path string `json:"path"`
Source DiffSource `json:"source,omitempty"` // <-- interface field
}The Source field holds concrete types like *viewDiff, *tableDiff, *ir.Table, etc. — all implementing DiffSource.
Marshaling works: Go's json.Marshal uses reflection to serialize the concrete type, producing nested JSON objects with Old, New, and other fields.
Unmarshaling fails: Go's json.Unmarshal encounters a JSON object for the source key and attempts to produce a map[string]interface{}, which does not implement DiffSource (no GetObjectName() method). The decoder fails.
This was introduced in commit a574dccf ("chore: make source strong type"), which changed Source any to Source DiffSource.
Impact
- Normal mode: Not affected.
ToJSON()callsToJSONWithDebug(false), which nils outSourceDiffsbefore marshaling. Thesourcefield never appears in the output JSON. - Debug mode: Affected.
ToJSONWithDebug(true)keepsSourceDiffs, and each diff'sSourcegets marshaled as a nested object that cannot be deserialized.
Suggested Fix
Change the JSON tag on Source from json:"source,omitempty" to json:"-":
Source DiffSource `json:"-"`This preserves full type safety for all in-memory usage (rewrite logic in plan/rewrite.go, summary display in plan/plan.go, dump formatting in dump/formatter.go). Only JSON serialization is affected — Source is excluded from both marshal and unmarshal, which is correct since no code path reads Source from deserialized JSON.
Relevant Files
| File | Role |
|---|---|
internal/diff/diff.go:254 |
Diff.Source field definition |
internal/plan/plan.go:67 |
SourceDiffs []diff.Diff on Plan struct |
internal/plan/plan.go:354-365 |
ToJSONWithDebug — controls whether SourceDiffs is included |
internal/plan/plan.go:380-387 |
FromJSON — deserialization entry point |
cmd/apply/apply.go:292 |
Where FromJSON is called during apply |
Environment
- pgschema version: v1.7.2 (bug present since commit
a574dccf, which predates v1.7.1) - Go version: 1.24+