suggest

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: 3 Imported by: 0

Documentation

Overview

Package suggest is the advisory surface: it maps DDL that is risky as written to the safer native form the engine would run instead, offline and without executing anything. Every safer-idiom decision yields a suggestion — a constructed rewrite carries the safer sequence with typed caveats, because a safer form is not a semantic equivalent: it reaches the same end state with different locking, transactionality, and failure modes; an operation whose rewrite the planner cannot construct carries typed guidance naming the manual path instead of staying silent. Refusals, table rewrites, and destructive drops are pkg/lint's job.

Index

Constants

View Source
const FormatVersion = 2

FormatVersion identifies the report contract (docs/suggest-report.md). A consumer must reject a report whose version it does not understand instead of guessing at the field semantics. Version 2 added name-constraint-then-validate and unique-index-then-constraint to the Guidance vocabulary.

Variables

This section is empty.

Functions

This section is empty.

Types

type Caveat

type Caveat string

Caveat is a typed condition attached to a recommendation; automation branches on it, never on prose. The caveats are independent — no caveat implies another; a sequence carries every caveat that applies to it.

const (
	// CaveatNonTransactional: the recommended sequence contains a
	// CONCURRENTLY statement, which cannot run inside a transaction
	// block.
	CaveatNonTransactional Caveat = "non-transactional"
	// CaveatSeparateTransactions: the steps must commit separately — the
	// weaker locks the sequence exists for are held to commit, so one
	// enclosing transaction reproduces the blocking the rewrite avoids.
	CaveatSeparateTransactions Caveat = "separate-transactions"
	// CaveatInvalidIndexOnFailure: a failed or cancelled concurrent build
	// leaves an INVALID index that must be detected (pg_index.indisvalid)
	// and dropped or rebuilt; the engine's executor owns that check when
	// it runs the sequence.
	CaveatInvalidIndexOnFailure Caveat = "invalid-index-on-failure"
	// CaveatDetachFinalizeOnFailure: an interrupted concurrent detach
	// leaves the partition half-detached; it must be finished with
	// DETACH PARTITION FINALIZE.
	CaveatDetachFinalizeOnFailure Caveat = "detach-finalize-on-failure"
	// CaveatValidationScan: the VALIDATE step still scans every row — the
	// rewrite trades the lock strength, not the scan.
	CaveatValidationScan Caveat = "validation-scan"
	// CaveatScaffoldConstraintOnFailure: a failed VALIDATE leaves the
	// NOT VALID constraint the sequence added on the live table, and
	// replaying the sequence then fails at the ADD CONSTRAINT step
	// (duplicate_object) — the runner must detect the leftover constraint
	// (pg_constraint) and resume from the VALIDATE step, or drop it and
	// restart.
	CaveatScaffoldConstraintOnFailure Caveat = "scaffold-constraint-on-failure"
)

The caveats a recommendation can carry.

func Caveats

func Caveats() []Caveat

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

type Guidance

type Guidance string

Guidance is the typed manual path for a risky operation whose safer form the planner cannot construct; automation branches on it, never on prose. It is what keeps the advisory surface aligned with pkg/lint: every statement lint flags blocking-idiom gets advice here — a constructed rewrite or, failing that, guidance.

const (
	// GuidanceSplitStatement: rewrites are constructed only for
	// single-operation statements — a partial rewrite of a compound ALTER
	// would be misleading. Split the statement into one operation per
	// statement and advise again.
	GuidanceSplitStatement Guidance = "split-statement"
	// GuidanceAddColumnThenConstraint: an inline UNIQUE / PRIMARY KEY /
	// FOREIGN KEY / CHECK on ADD COLUMN builds or validates under the ADD
	// COLUMN's ACCESS EXCLUSIVE lock. Add the plain column first, then
	// build the constraint with its online pattern as a separate, named
	// ADD CONSTRAINT — named, because the unnamed ADD CHECK / ADD FOREIGN
	// KEY forms are themselves refused (GuidanceNameConstraintThenValidate).
	GuidanceAddColumnThenConstraint Guidance = "add-column-then-constraint"
	// GuidancePrevalidatedCheck: ATTACH PARTITION scans the child under
	// the parent's lock unless a validated CHECK matching the partition
	// bound already exists on the child. Pre-add that CHECK (NOT VALID,
	// then VALIDATE), attach, then drop it. The planner cannot construct
	// the bound-matching CHECK from the statement alone.
	GuidancePrevalidatedCheck Guidance = "pre-add-validated-check"
	// GuidanceNotNullScaffold: prove the invariant with a NOT VALID CHECK
	// (col IS NOT NULL) plus an online VALIDATE, then the NOT NULL
	// constraint is a catalog flip — the same scaffold sequence the
	// SET NOT NULL form gets constructed.
	GuidanceNotNullScaffold Guidance = "not-null-scaffold"
	// GuidanceNameConstraintThenValidate: an unnamed ADD CHECK / ADD
	// FOREIGN KEY has no constructible rewrite because the online
	// sequence's VALIDATE CONSTRAINT step needs the constraint's name and
	// the server assigns one only at creation. Name the constraint, add
	// it NOT VALID, then VALIDATE it online.
	GuidanceNameConstraintThenValidate Guidance = "name-constraint-then-validate"
	// GuidanceUniqueIndexThenConstraint: an ADD PRIMARY KEY / ADD UNIQUE
	// whose USING INDEX rewrite could not be constructed. Build the unique
	// index with CREATE UNIQUE INDEX CONCURRENTLY, then attach it with
	// ADD CONSTRAINT … USING INDEX — the same sequence the constructed
	// rewrite emits. Keeps ManualGuidance total over every constraint kind
	// the classifier can mark safer-idiom, so a parser shape that yields
	// no rewrite becomes advice rather than a failed report.
	GuidanceUniqueIndexThenConstraint Guidance = "unique-index-then-constraint"
)

The guidance codes a suggestion can carry.

func Guidances

func Guidances() []Guidance

Guidances returns the closed set of Guidance values. It is part of the suggest-report contract (docs/suggest-report.md): the set changes only with a format_version bump, and a consumer that meets an unrecognized value must surface the suggestion as unknown rather than ignore it.

func ManualGuidance

func ManualGuidance(op statement.Op, multi bool) (Guidance, error)

ManualGuidance maps a safer-idiom operation without a constructed rewrite to the typed manual path; multi says the operation arrived in a multi-operation statement, which always advises splitting first. The plan report derives its rewrite-required guidance through this same function so the two surfaces can never disagree. A safer-idiom decision this table does not know is a contract violation — when the planner learns a new non-constructible pattern, its guidance must be recorded here before the advice ships — so it fails closed rather than staying silent about a statement lint flags.

type Report

type Report struct {
	// FormatVersion is the report contract version; always FormatVersion.
	FormatVersion int `json:"format_version"`
	// Suggestions are the advisory results in statement order, one per
	// safer-idiom decision; empty means every statement is already in its
	// safest known form or is outside the advisory surface (refusals and
	// rewrites are lint findings).
	Suggestions []Suggestion `json:"suggestions"`
}

Report is the advisory result for one script.

func Advise

func Advise(sql string) (Report, error)

Advise maps a DDL script to its advisory results: every statement is parsed with the PostgreSQL grammar and classified with zero live facts, and each risky-as-written operation yields a Suggestion — the safer sequence when the planner could construct it, typed guidance when it could not. Nothing is executed and no database is touched. A parse failure is an error.

type Suggestion

type Suggestion struct {
	// Statement is the 1-based index of the statement in the script.
	Statement int `json:"statement"`
	// Line is the 1-based source line of the statement's first token, so
	// a consumer can annotate the advice onto the file it came from.
	Line int `json:"line"`
	// Column is the 1-based source column of the statement's first token.
	Column int `json:"column"`
	// Original is the statement's verbatim source text (without the
	// trailing semicolon), so it can be found in the source by exact
	// match.
	Original string `json:"original"`
	// Operation is the operator-facing label of the risky operation
	// (display only).
	Operation string `json:"operation"`
	// Reason is the classifier's typed cause for preferring the rewrite.
	Reason planner.Reason `json:"reason"`
	// Recommended is the ordered safer SQL to run instead, present exactly
	// when the planner constructed the rewrite. Absent, Guidance names the
	// manual path.
	Recommended []string `json:"recommended,omitempty"`
	// Execution is the typed execution contract for Recommended
	// (planner.Execution), present exactly when Recommended is. A consumer
	// that runs the sequence branches on it instead of prose — it is what
	// says the steps must never be wrapped in one transaction block.
	Execution planner.Execution `json:"execution,omitempty"`
	// Caveats are the typed conditions under which the recommendation
	// differs from the original, present exactly when Recommended is and
	// never empty — a rewrite with no trade would be the same statement.
	Caveats []Caveat `json:"caveats,omitempty"`
	// Guidance is the typed manual path, present exactly when Recommended
	// is absent: the submitted form still blocks, and this names what to
	// do about it.
	Guidance Guidance `json:"guidance,omitempty"`
}

Suggestion is one advisory result: the statement as written and either the safer native sequence with its typed metadata, or typed guidance naming the manual path when no sequence could be constructed.

Jump to

Keyboard shortcuts

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