Documentation
¶
Overview ¶
Package statement parses SQL through the real PostgreSQL grammar (wasilibs/go-pgquery, Wasm libpg_query) and reports the facts the engine's front door needs. In Phase 1 that is a statement-type gate only: which kind of statement this is and, for ALTER TABLE, which table it targets. No schema model, no classification.
Two canonical forms coexist deliberately: this package's deparser prints grammar-canonical SQL (e.g. varchar(50)) for formatting, while pkg/schemadiff models carry server-decompiled text (character varying(50)) for comparison. The two canons never mix: models only ever compare server output against server output, and deparser output must not feed a model comparison or a schema fingerprint.
Index ¶
- Variables
- func AddNotValid(sql string) (rewritten, constraint string, err error)
- func Canonical(sql string) (string, error)
- func CheckNoComments(sql string) error
- func Concurrently(sql string) (string, error)
- func Qualify(sql, schema string) (string, error)
- type ConstraintKind
- type DefaultKind
- type DesiredSchema
- type Kind
- type Op
- type OpKind
- type SourceStatement
- type Statement
Constants ¶
This section is empty.
Variables ¶
var ( // ErrEmptyDesired is returned when the input contains no statements. ErrEmptyDesired = errors.New("desired schema contains no statements") // ErrNoCreateTable is returned when the input has no CREATE TABLE. ErrNoCreateTable = errors.New("desired schema must contain a CREATE TABLE") // ErrMultipleCreateTables is returned for more than one CREATE TABLE: // the engine is single-table scoped. ErrMultipleCreateTables = errors.New("desired schema must contain exactly one CREATE TABLE") // ErrDisallowedStatement is returned for any statement kind other than // CREATE TABLE / CREATE INDEX. The desired file is executed verbatim on // a scratch schema, so only pure schema definition is admitted. ErrDisallowedStatement = errors.New("statement kind not allowed in a desired schema") // ErrQualifiedName is returned when a statement schema-qualifies its // target. Desired files are schema-relative; the live schema comes from // the caller, and qualification could escape the scratch schema. ErrQualifiedName = errors.New("desired schema statements must use unqualified names") // ErrConcurrentIndex is returned for CREATE INDEX CONCURRENTLY, which // cannot run inside the scratch transaction. ErrConcurrentIndex = errors.New("CONCURRENTLY cannot be used in a desired schema") // ErrForeignKey is returned when the CREATE TABLE carries a REFERENCES // clause. The scratch transaction cannot faithfully execute a foreign // key: an unqualified reference resolves against the scratch // search_path, not the target schema, so it either fails or silently // binds to the wrong table. Foreign-key support needs its own design // (cross-file ordering, lock behavior, qualification policy); until // then the admission gate refuses it. ErrForeignKey = errors.New("foreign keys are not supported in a desired schema") // ErrWrongIndexTarget is returned when an index targets a table other // than the desired CREATE TABLE. ErrWrongIndexTarget = errors.New("index must target the desired table") )
Typed refusals for desired-state schema files. Each names one rule of the declarative front door; the caller branches with errors.Is, never on text.
var ( // ErrNotRewritable is returned when the statement kind has no // CONCURRENTLY form to rewrite to. ErrNotRewritable = errors.New("statement has no concurrent form") // ErrNotValidNotApplicable is returned when the statement is not a // single named ADD CHECK / ADD FOREIGN KEY that could take NOT VALID. ErrNotValidNotApplicable = errors.New("statement cannot take NOT VALID") )
Typed refusals for advisory rewrites. The rewriters flip one syntactic flag and deparse — no semantics are derived; a statement that cannot be rewritten that way is refused with one of these.
var ErrCommentLoss = errors.New("input contains comments, which formatting would discard")
ErrCommentLoss is returned when an operation that reprints SQL through the deparser would silently discard comments. The parser drops comments at parse time, so a formatter cannot carry them; refusing is the fail-closed alternative to destroying documentation in a source-of-truth file.
var ErrNotOneStatement = errors.New("input must contain exactly one SQL statement")
ErrNotOneStatement is returned by ParseOne when the input does not contain exactly one SQL statement.
Functions ¶
func AddNotValid ¶
AddNotValid rewrites a single-command ALTER TABLE ... ADD CONSTRAINT (named CHECK or FOREIGN KEY) to its NOT VALID form and returns the rewritten statement plus the constraint name for the follow-up VALIDATE CONSTRAINT step.
func Canonical ¶
Canonical reprints one statement through the PostgreSQL deparser: grammar-canonical spelling, unnecessary quoting dropped. Commented input is refused (ErrCommentLoss): the parser drops comments, and reprinting must never silently discard content. It is the rendering the plan report carries, so both front doors describe the same change with the same string. Deparser output never feeds a model comparison or a schema fingerprint (see the package comment); the plan report's fingerprint is a plan identity, not a schema fingerprint.
func CheckNoComments ¶
CheckNoComments scans sql with the PostgreSQL lexer and returns ErrCommentLoss when it contains any SQL (--) or C-style (/* */) comment. A scan failure is surfaced to the caller, never guessed around.
func Concurrently ¶
Concurrently returns sql rewritten to its CONCURRENTLY form: CREATE INDEX, DROP INDEX, REINDEX, or ALTER TABLE ... DETACH PARTITION. The rewrite flips the grammar's concurrency flag and deparses — nothing else changes. A statement already concurrent comes back canonicalized.
func Qualify ¶
Qualify returns sql with its target relation qualified by schema; an empty schema strips an existing qualification instead. It supports exactly one CREATE TABLE, CREATE INDEX, or ALTER TABLE statement. This touches qualification only — no semantics are ever derived or transformed at the AST level (that is the scratch database's job).
Types ¶
type ConstraintKind ¶
type ConstraintKind int
ConstraintKind names the constraint families the classifier routes differently.
const ( ConstraintUnrecognized ConstraintKind = iota ConstraintPrimaryKey ConstraintUnique ConstraintCheck ConstraintForeignKey ConstraintNotNull )
The constraint families ParseOps distinguishes. ConstraintUnrecognized (e.g. EXCLUDE) has no known safe pattern and is refused.
type DefaultKind ¶
type DefaultKind int
DefaultKind classifies the DEFAULT expression shape of an added column. Only a provable constant qualifies for PostgreSQL's fast default; any other expression is treated as volatile, conservatively.
const ( // DefaultNone: no DEFAULT clause. DefaultNone DefaultKind = iota // DefaultConstant: a literal (possibly type-cast) — fast-default safe. DefaultConstant // DefaultExpression: anything else — function calls, identity, serial. // The engine does not evaluate volatility offline; it assumes the worst. DefaultExpression )
The default shapes an added column can carry.
type DesiredSchema ¶
type DesiredSchema struct {
// contains filtered or unexported fields
}
DesiredSchema is a validated desired-state schema file: exactly one CREATE TABLE plus any number of CREATE INDEX statements on that table. Statement SQL is canonical (parsed and deparsed through the PostgreSQL grammar), in input order, one statement per entry.
Only ParseDesired produces a non-zero value, so holding one is proof the set-level admission rules held: a single unqualified CREATE TABLE, every index on that table, none of them CONCURRENTLY.
func ParseDesired ¶
func ParseDesired(sql string) (DesiredSchema, error)
ParseDesired parses a desired-state schema file and admits only what the declarative front door can execute on a scratch schema: one unqualified CREATE TABLE and unqualified, non-concurrent CREATE INDEX statements on it. Anything else is refused with a typed error.
func (DesiredSchema) Statements ¶
func (ds DesiredSchema) Statements() []Statement
Statements returns the admitted statements in input order, the CREATE TABLE among them. The slice is a copy: mutating it cannot invalidate the admission proof the value carries.
func (DesiredSchema) Table ¶
func (ds DesiredSchema) Table() string
Table returns the unqualified name of the single CREATE TABLE target.
type Kind ¶
type Kind int
Kind is the statement-type bucket the Phase 1 gate branches on.
type Op ¶
type Op struct {
// Kind is the operation shape.
Kind OpKind
// Column is the target column for column operations.
Column string
// Name is the constraint or index name where the operation has one,
// or the new name for renames.
Name string
// Columns are the plain key columns of an ADD PRIMARY KEY / UNIQUE;
// empty when the keys are expressions.
Columns []string
// Constraint is the constraint family for OpAddConstraint.
Constraint ConstraintKind
// NotValid is true for ADD CONSTRAINT ... NOT VALID.
NotValid bool
// UsingIndex is true for ADD CONSTRAINT ... USING INDEX.
UsingIndex bool
// Concurrent is true when the statement carries CONCURRENTLY.
Concurrent bool
// Unique is true for CREATE UNIQUE INDEX.
Unique bool
// IfNotExists is true for CREATE INDEX IF NOT EXISTS.
IfNotExists bool
// GeneratedStored is true for ADD COLUMN ... GENERATED ... STORED.
GeneratedStored bool
// InlineConstraints are the table-constraint families an added column
// carries inline (UNIQUE, PRIMARY KEY, REFERENCES, CHECK) — each does
// the same index build or validation scan as its ADD CONSTRAINT form.
// An inline constraint the engine does not model is reported as
// ConstraintUnrecognized so the classifier can refuse it.
InlineConstraints []ConstraintKind
// PartitionOf is true for CREATE TABLE ... PARTITION OF, which locks
// the partitioned parent, not just the new relation.
PartitionOf bool
// Default is the DEFAULT shape for OpAddColumn.
Default DefaultKind
// NewType is the target type for OpAlterColumnType and the column type
// for OpAddColumn, as the bare grammar type name (e.g. "varchar",
// "numeric") without the pg_catalog qualification.
NewType string
// NewTypeMods are the target type's modifiers (e.g. 50 in varchar(50),
// 12 and 2 in numeric(12,2)); empty when unconstrained.
NewTypeMods []int32
// HasUsing is true for ALTER COLUMN TYPE ... USING <expr>, which always
// means a conversion, never a binary-coercible relabel.
HasUsing bool
}
Op is one parsed operation: the shape facts the classifier needs, nothing executable. Fields beyond Kind are populated only where meaningful for that kind; see each field's comment.
func ParseOps ¶
ParseOps parses one SQL statement and returns its typed operations. An ALTER TABLE yields one Op per subcommand; every other supported statement yields exactly one. Statements and subcommands the engine does not recognize come back as OpUnrecognized — never an error — so the classifier can refuse them with context. A parse failure is surfaced to the caller.
type OpKind ¶
type OpKind int
OpKind names one operation shape the classifier distinguishes. A single ALTER TABLE statement yields one Op per subcommand; index, rename, and schema statements yield exactly one.
const ( OpUnrecognized OpKind = iota OpCreateTable OpAddColumn OpDropColumn OpAlterColumnType OpSetDefault OpDropDefault OpSetNotNull OpDropNotNull OpSetColumnOptions OpRenameColumn OpRenameTable OpRenameIndex OpSetSchema OpSetTablespace OpSetRelOptions OpAddConstraint OpValidateConstraint OpDropConstraint OpAttachPartition OpDetachPartition OpCreateIndex OpDropIndex OpReindex )
The operation shapes ParseOps reports. OpUnrecognized is everything the engine does not recognize; the classifier refuses it.
type SourceStatement ¶
type SourceStatement struct {
// SQL is the statement's verbatim source text, without the trailing
// semicolon, so it can be found in the source by exact match.
SQL string
// Line is the 1-based source line of the statement's first token.
Line int
// Column is the 1-based source column of the statement's first token.
Column int
}
SourceStatement is one script statement located in its source: the verbatim text plus the position of its first token, so a consumer can point a finding back at the exact place in the file it came from.
func Split ¶
func Split(sql string) ([]SourceStatement, error)
Split parses a script with the PostgreSQL grammar and returns each statement with its verbatim text and source position, in input order. Every statement is also reprinted through the deparser as a validation gate: a statement the grammar cannot roundtrip is an error, never a guess. An empty script returns no statements; a parse failure anywhere in the script is surfaced to the caller, never guessed around.
type Statement ¶
type Statement struct {
// contains filtered or unexported fields
}
Statement is one parsed SQL statement plus the facts the gate needs. It can only be constructed by ParseOne, so holding one proves the SQL parsed as exactly one statement through the PostgreSQL grammar — the proof the executor requires before running anything (invariant ST-7).
func ParseOne ¶
ParseOne parses sql with the PostgreSQL grammar and requires exactly one statement. A parse failure is surfaced to the caller, never guessed around.
func (Statement) BuildsIndex ¶
BuildsIndex reports whether executing the statement creates a new index: every CREATE INDEX, and the ALTER TABLE shapes that build one as a side effect — ADD CONSTRAINT UNIQUE / PRIMARY KEY / EXCLUDE without USING INDEX, and ADD COLUMN with an inline UNIQUE or PRIMARY KEY. The server requires CREATE on the schema for these (the engine-role contract's index build tier), unlike in-place ALTERs — including USING INDEX adoption and rewrites that rebuild existing indexes, which are owner-gated only.
func (Statement) Concurrent ¶
Concurrent reports whether an index statement used its CONCURRENTLY form. It is always false for non-index kinds.