Skip to content

Schema change: add_unique, add_check and drop_constraint - #5

Merged
swsarancodes merged 2 commits into
mainfrom
feat/schema-change-constraints
Oct 25, 2026
Merged

swsarancodes merged 2 commits into
mainfrom
feat/schema-change-constraints

Conversation

@swsarancodes

Copy link
Copy Markdown
Owner

What changed

Three new plan operations for the in-place PostgreSQL schema change compiler, with the same online-safe patterns as the existing ones.

  • add_unique (table, optional constraint name, columns): CREATE UNIQUE INDEX CONCURRENTLY, then ADD CONSTRAINT … UNIQUE USING INDEX. Says so when the columns are already unique, and refuses unknown or repeated columns and a name that is taken.
  • add_check (table, optional name, expression): ADD CONSTRAINT … CHECK (…) NOT VALID, then VALIDATE CONSTRAINT (one step on an empty table). The expression must be one plain boolean expression (no ;, comments or subqueries) and may call only immutable functions. PostgreSQL is asked to plan SELECT 1 FROM table WHERE (expression) (EXPLAIN, never run) so an unknown column or a result that is not boolean is refused with PostgreSQL's own message. The compiler records which columns the expression uses, so a later drop_column in the same plan names the check it takes with it.
  • drop_constraint (table, constraint name): drops a unique or check constraint. Refuses the primary key (not compiled), a foreign key (use drop_relationship), an unknown name, and a unique key another table's foreign key points at. Notes when the index being dropped is in use and gives the statement to put the constraint back.

The planner prompt, the plan normalizer, the structured-output schema and the plan list in the UI know the three operations. The CHECK probe is a new request kind alongside the default-expression probe in facts.ts.

How it was checked

  • 13 new unit tests (constraints.test.ts) and an updated probe test; the repo suite passes (1,736 tests) and both type-checks are clean.
  • Against real PostgreSQL, new scenarios in scripts/schema-change-pg16.ts: add a unique constraint (a duplicate is then refused), add a check on a medium table (a violating row is then refused), add a check the data violates (left NOT VALID), drop a check and a unique constraint, and add a check and a unique constraint then drop a column the check uses. The compiler's predicted end state matches the catalog in all of them, and the new CHECK probe accepts and rejects what it should. All 14 sections of that script pass on PostgreSQL 12, 13, 14, 15, 16, 17 and 18.
  • OpenAI accepts the new plan schema (npm run check:openai), and the real model, asked to make customer emails unique and require a premium to be positive, produced add_unique and add_check, which compiled to four steps with no lint violations.

Reviewer notes

  • Not included: add_primary_key (it needs NOT NULL columns first), enums, identity and sequences.
  • scripts/schema-change-pg16.ts and scripts/lib/ are still not in the repository, so the scenarios above ran from a local copy.

add_unique builds the unique index without blocking writes and attaches it as the constraint. add_check adds the check NOT VALID and then validates it; the expression must be one plain boolean expression with immutable functions only, and PostgreSQL is asked to plan it against the real table, with its own message shown when it refuses. drop_constraint drops a unique or check constraint and refuses the primary key, a foreign key, and a unique key another table points at. The compiler records the columns a check uses so a later column drop names it. The planner prompt and the structured-output schema know the three operations.
@swsarancodes
swsarancodes merged commit f604302 into main Oct 25, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant