plan

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 19, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package plan defines the machine-readable dry-run plan report: the stable JSON contract an operator or orchestrator consumes to decide whether and how a change would execute. Both front doors emit it — the imperative migrate --dry-run path and the declarative diff path — so a consumer parses one shape regardless of how the plan was derived.

Index

Constants

View Source
const FormatVersion = 2

FormatVersion identifies the report contract. A consumer must reject a report whose version it does not understand instead of guessing at the field semantics. Version 2 added the guidance field on rewrite-required statements.

Variables

This section is empty.

Functions

func Fingerprint

func Fingerprint(statements []Statement) string

Fingerprint computes the plan's stable identity: "sha256:" plus the hex digest over what would execute — each statement's canonical SQL, route, backend, disposition, and exec_sql, in plan order. Explanatory fields (decisions, kind, destructive) are excluded, so a reworded reason does not change identity but a rerouted or resequenced plan does. The exact serialization is part of the contract (docs/plan-report.md) and changes only with a format_version bump. This is a plan identity, not a schema fingerprint: it never participates in schema-state comparison.

func RefuseUnsupportedPartitionedParent

func RefuseUnsupportedPartitionedParent(report *Report, refused []bool)

RefuseUnsupportedPartitionedParent marks executable statements as refused when partition-aware admission rejects their execution steps.

Types

type Report

type Report struct {
	// FormatVersion is the report contract version; always FormatVersion.
	FormatVersion int `json:"format_version"`
	// Source is the front door that derived the plan.
	Source Source `json:"source"`
	// Schema is the target schema; empty when the submitted statement did
	// not qualify one.
	Schema string `json:"schema,omitempty"`
	// Table is the target table; empty when the statement has no single
	// table target (index maintenance).
	Table string `json:"table,omitempty"`
	// ServerVersion is the PostgreSQL server_version the plan was derived
	// against. Classification is version-sensitive, so a stored or
	// forwarded report names the server whose rules produced it; empty
	// only for sources that never connected.
	ServerVersion string `json:"server_version,omitempty"`
	// TableExists reports whether the live table was found. Set by every
	// source that introspects the target (diff, and the alter dry run);
	// nil means the plan has no single table target to introspect. For
	// diff, false means the statements are the full desired schema; for
	// an alter dry run, false means the plan was classified from zero
	// facts and executing it would fail.
	TableExists *bool `json:"table_exists,omitempty"`
	// Disposition is the aggregate disposition across all statements:
	// what would happen if the engine executed this plan now.
	Disposition router.Disposition `json:"disposition"`
	// Reason is the typed refusal cause when target facts make an otherwise
	// executable routed plan unsafe.
	Reason verdict.Reason `json:"reason,omitempty"`
	// Fingerprint is the plan's stable identity (see Fingerprint). An
	// approver pins it when the plan is reviewed; an executor recomputes it
	// at apply time and refuses on mismatch — that is how "the plan a
	// reviewer approves is the plan that executes" is enforced across
	// storage and forwarding.
	Fingerprint string `json:"fingerprint"`
	// Statements is the ordered plan; empty means there is nothing to do.
	Statements []Statement `json:"statements"`
}

Report is the dry-run plan for one change against one table.

func NewReport

func NewReport(source Source) Report

NewReport returns an empty report for source with the contract version stamped and Statements non-nil, so an empty plan serializes as [] rather than null.

type Source

type Source string

Source identifies which front door derived the plan.

const (
	// SourceAlter marks a plan derived from a submitted DDL statement
	// (migrate --alter --dry-run): the classify-and-route pipeline with
	// the diff step skipped.
	SourceAlter Source = "alter"
	// SourceDiff marks a plan derived from a desired-state schema diff
	// (diff --desired): the ordered statements that converge the live
	// table on the desired schema.
	SourceDiff Source = "diff"
)

func Sources

func Sources() []Source

Sources returns the closed set of Source values. It is part of the plan-report contract (docs/plan-report.md): the set changes only with a format_version bump, and a consumer that meets an unrecognized value must treat the report as unknown and refuse it.

type Statement

type Statement struct {
	// SQL is the statement in the engine's canonical rendering: parsed and
	// reprinted through the PostgreSQL deparser, whichever front door
	// derived it. It is never a verbatim echo of the submitted text, so
	// the same change carries the same string through either door.
	SQL string `json:"sql"`
	// Kind classifies a diff-derived statement so a consumer can gate
	// whole classes of change (see schemadiff.ChangeKind). Empty for the
	// alter source: a submitted statement may carry several operations and
	// has no single kind.
	Kind schemadiff.ChangeKind `json:"kind,omitempty"`
	// Destructive marks statements that discard live structure — a dropped
	// column, constraint, or index. It is derived from the classifier's
	// decisions, so both sources report it identically; it is always
	// emitted, never omitted, because a safety flag a consumer gates on
	// must be explicit even when false.
	Destructive bool `json:"destructive"`
	// Route is the planner's aggregate route for the statement.
	Route planner.Route `json:"route"`
	// Backend is the assigned execution strategy; empty for refusals.
	Backend router.Backend `json:"backend,omitempty"`
	// Disposition is what execution would do with the statement now.
	Disposition router.Disposition `json:"disposition"`
	// Reason is the typed cause when target facts refuse this statement.
	Reason verdict.Reason `json:"reason,omitempty"`
	// Decisions are the planner's per-operation classifications.
	Decisions []planner.Decision `json:"decisions"`
	// ExecSQL is the ordered SQL the native backend would run — the safer
	// sequence when the planner constructed one. Empty for non-native
	// routes.
	ExecSQL []string `json:"exec_sql,omitempty"`
	// Execution is the typed execution contract for ExecSQL
	// (planner.Execution), present exactly when ExecSQL is. A consumer
	// that runs the statements itself branches on it — it is what says
	// each step runs in its own implicit transaction, never inside an
	// enclosing transaction block. It is derived from ExecSQL's presence,
	// so it is excluded from the fingerprint like the other explanatory
	// fields.
	Execution planner.Execution `json:"execution,omitempty"`
	// Guidance is the typed manual path for a rewrite-required refusal,
	// drawn from the suggest contract's Guidance vocabulary
	// (docs/suggest-report.md): the engine will not run the statement, and
	// this names what to do instead. Present exactly when Disposition is
	// rewrite-required. Explanatory, so it is excluded from the
	// fingerprint.
	Guidance suggest.Guidance `json:"guidance,omitempty"`
}

Statement is one planned statement: the SQL, its classification, and what execution would do with it now.

func FromRouted

func FromRouted(rs router.Statement) (Statement, error)

FromRouted converts one routed statement into a plan statement. Destructive is derived from the classifier's decisions — one destructive operation makes the statement destructive — so every source that routes through the planner reports it identically by construction. A rewrite-required statement additionally carries the typed manual path (Guidance), derived through the same mapping the suggest report uses; a rewrite-required decision with no known guidance is a contract violation and fails closed.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL