feat: ADR-0035 4d — CREATE [UNIQUE] INDEX / DROP INDEX
Advanced-mode SQL CREATE [UNIQUE] INDEX [IF NOT EXISTS] [<name>] ON <T> (cols) -> SqlCreateIndex and DROP INDEX [IF EXISTS] <name> -> SqlDropIndex, both reusing the ADR-0025 executors (do_add_index / do_drop_index), like 4c reused do_drop_table. - CREATE UNIQUE INDEX admitted in advanced mode (ADR-0025 Amendment 1): ADR-0025 deferred UNIQUE indexes for the simple-mode DSL, but advanced mode trusts the user like SQL does. Adds an additive IndexSchema.unique flag (project.yaml, serde-default, version stays 1); rebuild re-emits CREATE UNIQUE INDEX; the redundant-set guard keys on (columns, unique). Simple-mode `add unique index` stays deferred. - IF [NOT] EXISTS on both forms reuses the 4c no-op-with-note skip (journalled, not snapshotted) via CreateIndexOutcome / DropIndexOutcome. - Unnamed CREATE INDEX auto-named (ADR-0025 convention); the [UNIQUE] prefix is a concrete-keyword Choice and the optional name an on-led-first selector (the drop-index selector precedent) — trap-safe. - create/drop each gain a second advanced node; the existing all-candidates dispatch handles it (locked by parse tests). - Unique indexes marked [unique] in the structure view and items panel. - do_add_index refuses internal __rdbms_* tables as "no such table", closing a latent exposure on both the simple `add index` and the new SQL CREATE INDEX surfaces (ADR-0025 Amendment 1). Docs: ADR-0035 status + §13 4d + 4i; ADR-0025 Amendment 1; ADR README; requirements.md Q1/C3. Plan: docs/plans/20260525-adr-0035-sql-ddl-4d.md. Tests: 1834 passing / 0 failing / 0 skipped / 1 ignored; clippy clean.
This commit is contained in:
@@ -2,7 +2,11 @@
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
Accepted. **Amendment 1 (2026-05-25):** UNIQUE indexes are admitted on
|
||||
the **advanced-mode** SQL surface (`CREATE UNIQUE INDEX`) — see
|
||||
*Amendment 1* below and ADR-0035 §4d. The original *Out of scope*
|
||||
exclusion stands for the **simple-mode DSL** (`add unique index` remains
|
||||
deferred).
|
||||
|
||||
## Context
|
||||
|
||||
@@ -335,6 +339,42 @@ here so the decision text and the code agree:
|
||||
list with each table's indexes indented beneath — the
|
||||
`S2` nested view — reading the two together.
|
||||
|
||||
## Amendment 1 — UNIQUE indexes in advanced mode (2026-05-25)
|
||||
|
||||
This ADR's *Out of scope* excluded UNIQUE indexes (`add unique index`)
|
||||
on the grounds that a unique index conflates two concepts the playground
|
||||
teaches separately — an index (a performance structure) and a UNIQUE
|
||||
*constraint* (an integrity rule, tracked as its own `C3` sub-item). That
|
||||
reasoning was written on 2026-05-16, when the **simple-mode DSL was the
|
||||
only input surface**, and it still holds there: simple mode teaches the
|
||||
two concepts separately, so `add unique index` stays deferred.
|
||||
|
||||
ADR-0035 (advanced-mode SQL DDL) introduced a second surface whose
|
||||
explicit posture is to "trust the user like SQL does" (ADR-0035 §7). On
|
||||
that surface `CREATE UNIQUE INDEX` is standard SQL a learner types
|
||||
verbatim, and the concept-separation argument does not transfer — so
|
||||
ADR-0035 §4 lists `CREATE [UNIQUE] INDEX` and **§4d supersedes this
|
||||
ADR's exclusion for the advanced surface**. The constraint track this
|
||||
ADR deferred *to* (ADR-0018 → ADR-0029 → ADR-0035 §4a.2) has since
|
||||
shipped, so there is no remaining dependency.
|
||||
|
||||
Mechanically, the index model gains an `IndexSchema.unique` flag — an
|
||||
additive, `#[serde(default)]` `project.yaml` field (`version` stays
|
||||
`1`); the engine already reports uniqueness via `pragma_index_list`
|
||||
(origin `c`), so **no `__rdbms_*` metadata table is added** (the §Storage
|
||||
decision is unchanged — the divergence from the relationship precedent
|
||||
stands). The rebuild primitive re-emits `CREATE UNIQUE INDEX`; the
|
||||
structure view and items panel mark a unique index `[unique]`
|
||||
(ADR-0035 §4d). The redundant-column-set guard keys on `(columns,
|
||||
unique)` so a plain and a unique index over the same columns are not
|
||||
mutual duplicates.
|
||||
|
||||
The amendment also hardened the shared `do_add_index` executor to refuse
|
||||
an internal `__rdbms_*` table as "no such table" (consistent with the
|
||||
app-wide opacity of internal tables) — closing a latent exposure on
|
||||
*both* the simple `add index` and the new SQL `CREATE INDEX` surfaces,
|
||||
which both reach `do_add_index`.
|
||||
|
||||
## See also
|
||||
|
||||
- ADR-0004 / ADR-0015 (project file format and storage runtime)
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
## Status
|
||||
|
||||
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**
|
||||
**validated end-to-end by sub-phases 4a / 4a.2 / 4a.3 / 4b / 4c / 4d**
|
||||
(`CREATE TABLE` with column- and table-level constraints and foreign
|
||||
keys, and `DROP TABLE [IF EXISTS]`, implemented 2026-05-25 — plans
|
||||
keys, `DROP TABLE [IF EXISTS]`, and `CREATE [UNIQUE] INDEX` /
|
||||
`DROP INDEX [IF EXISTS]`, 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`), so the
|
||||
decision is accepted while the remaining sub-phases (**4d–4i**, §13)
|
||||
`docs/plans/20260525-adr-0035-sql-ddl-4b.md`, `…-4c.md`, `…-4d.md`), so
|
||||
the decision is accepted while the remaining sub-phases (**4e–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**
|
||||
@@ -381,7 +382,25 @@ ADR-0033's structure:
|
||||
DSL drops still parse via fallback — 4i grows the surface as `DROP
|
||||
INDEX` lands in 4d.
|
||||
- **4d — `CREATE [UNIQUE] INDEX` / `DROP INDEX`** → `SqlCreateIndex`
|
||||
/ `SqlDropIndex` (ADR-0025; the `UNIQUE` flag extension if needed).
|
||||
/ `SqlDropIndex`. *(Implemented 2026-05-25 — plan
|
||||
`docs/plans/20260525-adr-0035-sql-ddl-4d.md`.)* Both reuse the ADR-0025
|
||||
executors (`do_add_index` / `do_drop_index`), like 4c reused
|
||||
`do_drop_table`. `CREATE [UNIQUE] INDEX [IF NOT EXISTS] [<name>] ON <T>
|
||||
(cols)` (the unnamed form auto-named per ADR-0025; the leading
|
||||
`[UNIQUE]` is a concrete-keyword `Choice`, the optional name an
|
||||
`on`-led-first selector mirroring the `drop index` positional
|
||||
selector) and `DROP INDEX [IF EXISTS] <name>` (name-only — the
|
||||
positional `drop index on T(…)` stays the simple form via fallback).
|
||||
`IF [NOT] EXISTS` reuses the 4c no-op-with-note skip (journalled, not
|
||||
snapshotted). **`CREATE UNIQUE INDEX` is admitted** (user-confirmed
|
||||
2026-05-25): ADR-0025 deferred UNIQUE indexes for the *simple-mode
|
||||
DSL*, but advanced mode "trusts the user like SQL does" (§7) — so the
|
||||
model gains an `IndexSchema.unique` flag (additive YAML, `version` 1;
|
||||
rebuild re-emits `CREATE UNIQUE INDEX`; the structure view + items
|
||||
panel mark `[unique]`), recorded as **ADR-0025 Amendment 1**.
|
||||
Simple-mode `add unique index` stays deferred. `create`/`drop` each
|
||||
gain a *second* advanced node, exercising the all-candidates dispatch
|
||||
(`decide` tries every advanced candidate).
|
||||
- **4e — `ALTER TABLE` add/drop/rename column.** Drop/rename column
|
||||
must guard against a **table-level CHECK that references the column**
|
||||
(4a.3): today the rebuild rejects it cleanly via the engine (the
|
||||
@@ -398,8 +417,13 @@ ADR-0033's structure:
|
||||
statement), `help`/usage for the new forms. **Carried in from earlier
|
||||
slices:** (a) refresh the `CREATE TABLE` help/usage skeleton for the
|
||||
4a.2 `DEFAULT`/`CHECK`/composite-`UNIQUE`, 4a.3 table-`CHECK`, and 4b
|
||||
FK forms (deferred from each); (b) `describe` display of table-level
|
||||
constraints (composite `UNIQUE` + table `CHECK`); (c) **4b self-ref
|
||||
FK forms (deferred from each) — **4d's index forms already carry their
|
||||
own help/usage** (`ddl.sql_create_index` / `ddl.sql_drop_index` + the
|
||||
`parse.usage.*` keys), since the nodes are new; (b) `describe` display
|
||||
of table-level constraints (composite `UNIQUE` + table `CHECK`) — note
|
||||
the **unique-*index* marker shipped in 4d** (`[unique]` in the
|
||||
structure view + items panel), so only the table-level *constraint*
|
||||
display remains here; (c) **4b self-ref
|
||||
FK indicator** — a `CREATE TABLE` with a self-referencing FK
|
||||
(`references <self>`) parses + executes correctly, but the pre-submit
|
||||
schema-existence diagnostic falsely flags the not-yet-created self
|
||||
@@ -415,7 +439,10 @@ ADR-0033's structure:
|
||||
entry word so advanced completion offers every valid continuation
|
||||
(`drop ` → table + column + relationship + index + constraint; `drop
|
||||
rel` → relationship); verify `create`/`insert`/`update`/`delete`
|
||||
completion stays sensible. (e) **Discussion flag (user, 2026-05-25):**
|
||||
completion stays sensible. **4d widened this:** `create` and `drop`
|
||||
now each have *two* advanced nodes (table + index), so a shared entry
|
||||
word's continuations now span two SQL shapes as well as the DSL ones —
|
||||
the merge matters more. (e) **Discussion flag (user, 2026-05-25):**
|
||||
before/with (d), discuss **visually distinguishing simple- vs
|
||||
advanced-mode completions in the hint UI (likely by colour)** so a
|
||||
learner can see which continuations are DSL and which are SQL — a UX
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user