feat: ADR-0035 4g — ALTER TABLE add/drop constraint + add FK
ALTER TABLE <T> ADD [CONSTRAINT <name>] (CHECK | UNIQUE | FOREIGN KEY)
and DROP CONSTRAINT <name>. ADD = table-CHECK + composite UNIQUE + FK
(ADD PRIMARY KEY and a named UNIQUE refused — composite UNIQUE is
anonymous in our model). Each ADD reuses a low-level path with a dry-run
guard (table-CHECK/UNIQUE rebuild; FK -> add_relationship, bare
REFERENCES -> parent single PK). DROP CONSTRAINT resolves the name to a
named table-CHECK then a child-side FK, else refuses. One undo step each.
Named table-CHECKs round-trip: a nullable `name` column on
__rdbms_playground_table_checks (rebuild-only arrival; a named add on a
pre-4g project is refused with a "rebuild first" hint) plus a project.yaml
check_constraints {expr, name} extension (bare-string form still reads).
The internal-__rdbms_* guard was folded into do_add_constraint /
do_add_relationship, completing that guard class.
Grammar: the action Choice keeps one branch per verb (add/drop/rename/
alter) with an inner Choice fanning out on the distinct second keyword,
since the walker's Choice does not backtrack between same-led branches.
Tests: 7 Tier-1 parse + 2 yaml round-trip + 1 internal-guard + 9 Tier-3
e2e. Help/usage refreshed; ADR-0035 §13 4g + README + requirements.md in
lockstep.
This commit is contained in:
@@ -4,14 +4,15 @@
|
||||
|
||||
Accepted. Design agreed with the user (2026-05-24); the approach is
|
||||
**validated end-to-end by sub-phases 4a / 4a.2 / 4a.3 / 4b / 4c / 4d /
|
||||
4e / 4f** (`CREATE TABLE` with column- and table-level constraints and
|
||||
foreign keys, `DROP TABLE [IF EXISTS]`, `CREATE [UNIQUE] INDEX` /
|
||||
`DROP INDEX [IF EXISTS]`, `ALTER TABLE` add/drop/rename column, and
|
||||
`ALTER TABLE … ALTER COLUMN TYPE`, implemented 2026-05-25 — plans
|
||||
4e / 4f / 4g** (`CREATE TABLE` with column- and table-level constraints
|
||||
and foreign keys, `DROP TABLE [IF EXISTS]`, `CREATE [UNIQUE] INDEX` /
|
||||
`DROP INDEX [IF EXISTS]`, `ALTER TABLE` add/drop/rename column,
|
||||
`ALTER TABLE … ALTER COLUMN TYPE`, and `ALTER TABLE` add/drop constraint
|
||||
+ add foreign key, implemented 2026-05-25 — plans
|
||||
`docs/plans/20260524-adr-0035-sql-ddl-4a.md`, `…-4a2.md`, `…-4a3.md`,
|
||||
`docs/plans/20260525-adr-0035-sql-ddl-4b.md`, `…-4c.md`, `…-4d.md`,
|
||||
`…-4e.md`, `…-4f.md`), so the decision is accepted while the remaining
|
||||
sub-phases (**4g–4i**, §13) continue. This is **Phase 4** of the ADR-0030 roadmap (the
|
||||
`…-4e.md`, `…-4f.md`, `…-4g.md`), so the decision is accepted while the
|
||||
remaining sub-phases (**4h–4i**, §13) continue. This is **Phase 4** of the ADR-0030 roadmap (the
|
||||
advanced-mode SQL surface), the peer of ADR-0031 (expression grammar),
|
||||
ADR-0032 (`SELECT`), and ADR-0033 (DML). It **clarifies ADR-0030 §4**
|
||||
on how DDL is represented and executed.
|
||||
@@ -456,6 +457,30 @@ ADR-0033's structure:
|
||||
exposure too. *(The remaining internal-table guard on
|
||||
`do_add_constraint` / `do_add_relationship` rides in 4g.)*
|
||||
- **4g — `ALTER TABLE` add/drop constraint, add foreign key.**
|
||||
*(Implemented 2026-05-25 — plan
|
||||
`docs/plans/20260525-adr-0035-sql-ddl-4g.md`.)* `ALTER TABLE <T> ADD
|
||||
[CONSTRAINT <name>] (CHECK (…) | UNIQUE (…) | FOREIGN KEY (…)
|
||||
REFERENCES …)` and `DROP CONSTRAINT <name>`. **ADD scope (user-
|
||||
confirmed):** CHECK + composite UNIQUE + FK; `ADD PRIMARY KEY` is
|
||||
refused (every table already has a PK) and a **named UNIQUE** is
|
||||
refused (composite UNIQUE is anonymous in our model — PRAGMA-detected,
|
||||
§4a.2). Each ADD reuses an existing low-level path: table-CHECK and
|
||||
composite-UNIQUE rebuild the table (dry-run guards reject existing
|
||||
rows that would violate), FK decomposes to `add_relationship` (the
|
||||
same machinery `add 1:n relationship` uses — bare `REFERENCES <P>`
|
||||
resolves to the parent's single PK; `create_fk = false` as the column
|
||||
must exist). **DROP CONSTRAINT (user-confirmed)** resolves the name to
|
||||
a named table-CHECK then a named FK whose child is `<T>`, else refuses.
|
||||
**Named table-CHECK round-trip (user-confirmed):** the `CHECK_TABLE`
|
||||
metadata gains a nullable `name` column (**rebuild-only** arrival — a
|
||||
pre-4g project gains it on `rebuild`; a named CHECK add on an
|
||||
un-upgraded project is refused with a friendly "rebuild first"
|
||||
message), and `project.yaml`'s `check_constraints` is **extended** to
|
||||
carry the name (`{expr, name}` mapping; the bare-string form still
|
||||
reads, name = `None`) so a named CHECK survives a rebuild — `rebuild`
|
||||
reconstructs from the yaml. The internal-`__rdbms_*` guard was folded
|
||||
into `do_add_constraint` / `do_add_relationship`, completing the
|
||||
4d/4e/4f guard class. One undo step per statement.
|
||||
- **4h — `ALTER TABLE … RENAME TO`** (the §6 new low-level op).
|
||||
- **4i — Verification sweep.** Typing-surface + matrix coverage,
|
||||
engine-neutral error pass, undo-parity check (one step per
|
||||
|
||||
Reference in New Issue
Block a user