feat: ADR-0035 Amendment 1 — drop composite UNIQUE; friendlier drop-column + generic-error wording
F1/F2/F3 from the whole-Phase-4 /runda (handoff-42 §3):
- F3: drop an anonymous composite UNIQUE via a derived, engine-neutral
name `unique_<cols>` — recomputed live, nothing persisted, reusing the
existing `DROP CONSTRAINT <name>` grammar (no new syntax/metadata, the
§4g anonymity decision intact). A name matching more than one UNIQUE is
refused as ambiguous, never guessed. One undo step. `describe`
annotates each composite UNIQUE with its name.
- F1: dropping a column a composite UNIQUE covers is refused up-front
with the derived name + the actionable drop command (was an unhelpful
generic engine refusal).
- F2: contextless friendly_message() no longer leaks a literal `{table}`
in the generic hint (new `error.generic.hint_no_table`, selected when
no table is in context). The table-ful path is unchanged.
Docs: ADR-0035 Amendment 1 + Status + README index + plan
docs/plans/20260526-adr-0035-composite-unique-drop-f1f2f3.md.
Tests: +5 (drop-by-name, ambiguous-refused, one-undo-step, F1 guard,
F2 no-leak) + a describe-render assertion. 1922 pass / 0 fail / 0 skip;
clippy clean.
This commit is contained in:
@@ -17,7 +17,11 @@ the CREATE-TABLE help/usage refresh — implemented 2026-05-25/26 — plans
|
||||
`…-4e.md`, `…-4f.md`, `…-4g.md`,
|
||||
`docs/plans/20260526-adr-0035-sql-ddl-4h.md`,
|
||||
`docs/plans/20260526-adr-0035-sql-ddl-4i.md`). **Phase 4 is complete**
|
||||
(4a–4i all shipped). This is **Phase 4** of the ADR-0030 roadmap (the
|
||||
(4a–4i all shipped). **Amendment 1 (2026-05-26)** adds a way to **drop a
|
||||
composite UNIQUE** via a derived, engine-neutral name (`unique_<cols>`)
|
||||
that reuses the existing `DROP CONSTRAINT <name>` grammar — no new
|
||||
syntax, no metadata, the §4g anonymity decision intact (see the amendment
|
||||
below). 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.
|
||||
@@ -551,6 +555,67 @@ ADR-0033's structure:
|
||||
read-side (completion / diagnostics / describe / help), so no undo
|
||||
steps are introduced.
|
||||
|
||||
## Amendment 1 — Dropping a composite UNIQUE constraint (2026-05-26)
|
||||
|
||||
A whole-Phase-4 `/runda` surfaced that a composite `UNIQUE(a,b)` — kept
|
||||
**anonymous** by design (§4a.2, and §4g refused a *named* UNIQUE add) —
|
||||
has **no way to be dropped**: `DROP CONSTRAINT <name>` (§4g) resolves
|
||||
only a named table-CHECK or a named FK, so recreating the table was the
|
||||
only escape. This amendment adds a drop path. Written with explicit user
|
||||
approval; the plan is
|
||||
`docs/plans/20260526-adr-0035-composite-unique-drop-f1f2f3.md`.
|
||||
**Implemented 2026-05-26.**
|
||||
|
||||
**It does not reverse the §4g anonymity decision.** Storage stays a bare
|
||||
column-list (`unique_constraints: Vec<Vec<String>>`, PRAGMA-detected) and
|
||||
a UNIQUE still **cannot be named on `ADD`**. The addition is purely a
|
||||
**derived, engine-neutral name** used to *display* and *address* the
|
||||
constraint on drop.
|
||||
|
||||
### The derived name (user-decided: derived, no storage; `unique_<cols>`)
|
||||
|
||||
The name is a deterministic function of the column list —
|
||||
`unique_<col1>_<col2>…` — recomputed live wherever it is shown or
|
||||
matched. Nothing is persisted: the constraint remains a bare column-list,
|
||||
so the name round-trips for free and needs no metadata table and no
|
||||
rebuild-arrival migration (the cost §4a.3 deliberately avoided). If a
|
||||
column in the UNIQUE is later renamed, the displayed name tracks it —
|
||||
arguably more correct than a frozen stored name. Alternatives weighed and
|
||||
rejected: naming UNIQUEs with a user-supplied name + new metadata table
|
||||
(reverses §4g; heaviest), and a positional `drop … unique (cols)` form
|
||||
(needs new grammar). The derived name **reuses the existing `DROP
|
||||
CONSTRAINT <name>` grammar — no new syntax**.
|
||||
|
||||
### Surfaces
|
||||
|
||||
- **`describe` / structure view.** The "Table constraints:" section (4i b)
|
||||
annotates each composite UNIQUE with its name: `unique_b_c: UNIQUE
|
||||
(b, c)`.
|
||||
- **`ALTER TABLE <T> DROP CONSTRAINT <name>`** (advanced-SQL only, matching
|
||||
the §4g `ADD` form). `do_drop_constraint_by_name` gains a **third
|
||||
resolution step** after named table-CHECK and named FK: it recomputes
|
||||
the derived name of each composite UNIQUE on `<T>` and matches. On a
|
||||
single match it rebuilds the table without that entry (the
|
||||
`do_alter_add_unique` rebuild in reverse). **A name matching more than
|
||||
one UNIQUE is refused as ambiguous** (e.g. a column literally named
|
||||
`b_c` colliding with `UNIQUE (b, c)`) — it never guesses which to drop.
|
||||
**Resolution order** means a user-named CHECK/FK with the same string
|
||||
shadows a derived UNIQUE name; the distinctive `unique_` prefix makes
|
||||
this unlikely and it is documented, not guarded.
|
||||
|
||||
### Dropping a *column* a composite UNIQUE covers (F1)
|
||||
|
||||
`do_drop_column` gains an up-front guard (alongside the index-covering
|
||||
and CHECK guards): a column participating in any composite UNIQUE is
|
||||
refused with the constraint's derived name and the actionable drop
|
||||
command — `cannot drop \`T.c\` … part of the UNIQUE constraint
|
||||
\`unique_b_c\` (b, c); drop that constraint first (\`alter table T drop
|
||||
constraint unique_b_c\`)`. The refusal itself is unchanged (the engine
|
||||
already refuses it); the message becomes engine-neutral and actionable.
|
||||
Single-column UNIQUE column drops are a **parallel** gap (different
|
||||
mechanism — ADR-0029 column-level `drop constraint`) and are **out of
|
||||
scope** here.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Advanced mode reaches DDL parity with simple mode and adds
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user