Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Constraints

Constraint kinds pgevolve models, plus their attributes.

See ../README.md for the status legend.

Kinds

KindStatusNotes
PRIMARY KEY✅ ImplementedSingle- and multi-column. INCLUDE (covering) columns supported.
Tests: tier-1: crates/pgevolve-core/src/ir/constraint.rs::tests; tier-2: parser/equivalent_pairs/0006-pk-inline-vs-table-constraint; tier-C: objects/tables/create-simple
UNIQUE✅ ImplementedSingle- and multi-column. INCLUDE and NULLS NOT DISTINCT (PG 15+) supported.
Tests: tier-1: crates/pgevolve-core/src/ir/constraint.rs::tests; tier-C: objects/tables/add-constraint-unique
FOREIGN KEY✅ ImplementedFull attribute matrix below. The forward-reference cycle case is broken by the planner's FK-extraction post-pass.
Tests: tier-1: crates/pgevolve-core/src/ir/constraint.rs::tests, plan/rewrite/fk_not_valid_validate.rs; tier-C: objects/tables/add-constraint-foreign-key, failure/ast-resolution/fk-to-missing-table
CHECK✅ ImplementedExpression preserved as canonical text; redundant string-literal casts that Postgres's deparser adds ('x'::text) are normalized away so the source matches the introspected catalog. NO INHERIT flag supported.
Tests: tier-1: crates/pgevolve-core/src/plan/rewrite/check_not_valid_validate.rs; tier-C: objects/tables/add-constraint-check, drop-constraint-check, check-string-literal
NOT NULL (column-level)✅ ImplementedModeled as Column::nullable rather than as a Constraint. The SET NOT NULL via CHECK pattern rewrite avoids long locks (see pipeline.md).
Tests: tier-1: crates/pgevolve-core/src/plan/rewrite/tests::set_not_null_on_existing_column_emits_four_steps
EXCLUSION (EXCLUDE USING gist (...))🔮 FutureUsed primarily with range types; lands alongside range-type column support.

FOREIGN KEY attributes

Tests (whole section): tier-1: crates/pgevolve-core/src/ir/constraint.rs::tests, parse/builder/create_stmt.rs::tests; tier-C: objects/tables/add-constraint-foreign-key.

AttributeStatusNotes
Local columns + referenced columns✅ ImplementedOrder significant. change_kinds: [add]
`ON UPDATE { NO ACTIONRESTRICTCASCADE
`ON DELETE { NO ACTIONRESTRICTCASCADE
SET NULL (col, …) / SET DEFAULT (col, …) (column-restricted action; PG 15+)✅ Implementedchange_kinds: [add]
MATCH SIMPLE (default)✅ Implementedchange_kinds: [add]
MATCH FULL✅ Implementedchange_kinds: [add]
MATCH PARTIAL⛔ Not plannedNever implemented by Postgres itself.
DEFERRABLE / NOT DEFERRABLE, INITIALLY DEFERRED / INITIALLY IMMEDIATE✅ Implementedchange_kinds: [add, set_deferrable]
NOT VALID + VALIDATE CONSTRAINT rewrite for adds on existing tables✅ ImplementedSee pipeline.md.
Tests: tier-1: crates/pgevolve-core/src/plan/rewrite/fk_not_valid_validate.rs
NOT VALID constraints persisted as-is⛔ Not plannedThe IR represents only fully-validated constraints; the NOT VALID state is an intermediate planner artifact.
NOT VALID drift detection and auto-resolution✅ ImplementedThe catalog reader detects pg_constraint.convalidated = false (from a partial-apply) and the differ emits Change::ValidateConstraint. The planner emits ALTER TABLE ... VALIDATE CONSTRAINT. No user action required. See pipeline.md.
Tests: tier-2: crates/pgevolve-core/tests/catalog_drift.rs

CHECK attributes

Tests (whole section): tier-1: crates/pgevolve-core/src/ir/constraint.rs::tests, plan/rewrite/check_not_valid_validate.rs; tier-C: objects/tables/add-constraint-check, drop-constraint-check.

AttributeStatusNotes
Boolean predicate✅ ImplementedPreserved as canonical text. change_kinds: [add]
NO INHERIT✅ Implementedchange_kinds: [add]
NOT VALID + VALIDATE CONSTRAINT rewrite for adds on existing tables✅ Implementedchange_kinds: [validate]
Table-level vs. column-level placement✅ ImplementedTreated identically at IR level. change_kinds: [add]

PRIMARY KEY / UNIQUE attributes

Tests (whole section): tier-1: crates/pgevolve-core/src/ir/constraint.rs::tests; tier-2: parser/equivalent_pairs/0006-pk-inline-vs-table-constraint.

AttributeStatusNotes
INCLUDE (col, …) covering columns✅ Implementedchange_kinds: [add]
NULLS NOT DISTINCT (UNIQUE only, PG 15+)✅ Implementedchange_kinds: [add]
WITH (storage_parameter = ...) (constraint reloptions)🔮 FutureThe IR doesn't yet model constraint storage parameters.
USING INDEX <name> (attach existing index)⛔ Not plannedpgevolve always creates the underlying index implicitly; reusing pre-existing indexes is an adoption-path corner case.
USING INDEX TABLESPACE <name>🔮 FutureLands with broader tablespace modeling.

Constraint-level features

FeatureStatusNotes
Constraint name preserved across diff✅ ImplementedConstraints are paired by qname; renaming a constraint registers as drop+add.
Tests: tier-1: crates/pgevolve-core/src/diff/constraints.rs::tests
Unnamed-constraint auto-naming✅ ImplementedAn unnamed constraint is auto-named exactly as Postgres's ChooseIndexName/ChooseConstraintName would, so it pairs with the introspected catalog instead of showing a spurious drop+add: {table}_pkey, {table}_{col}_key (UNIQUE), {table}_{col}_check (column CHECK) / {table}_check (table CHECK), with makeObjectName length truncation and collision counters. LIKE copies constraint names verbatim, as Postgres does. Verified byte-for-byte against live PG 14–18.
Tests: tier-1: crates/pgevolve-core/src/parse/builder/choose_name.rs::tests, parse/builder/create_stmt.rs::tests; tier-3: crates/pgevolve-core/tests/table_like_round_trip.rs
Constraint comments✅ ImplementedTests: tier-1: crates/pgevolve-core/src/parse/builder/comment_stmt.rs::tests
ALTER TABLE ... VALIDATE CONSTRAINT as an explicit step✅ ImplementedUsed by the FK / CHECK NOT VALID rewrites.
Tests: tier-1: crates/pgevolve-core/src/plan/rewrite/fk_not_valid_validate.rs, check_not_valid_validate.rs; tier-2: crates/pgevolve-core/tests/catalog_drift.rs
ALTER TABLE ... RENAME CONSTRAINT🔮 FutureToday a rename diffs as drop+add (semantically equivalent but a larger lock).
Multi-column constraint reordering⛔ Not plannedThe column order inside a constraint is semantically meaningful; changing it is a drop+add.