docs: ADR-0030 — advanced mode standard-SQL surface

Decides the architecture for SQL in advanced mode (Q1/Q2/Q4):
SQL is authored as grammar within the unified grammar tree
(ADR-0024) and parsed by the existing walker — not a separate
batch parser — so SQL gets the same completion, highlighting,
hints, and parse-error reporting as the DSL. Mode gates the
SQL forms. DDL routes through the typed Command executor
(metadata and the playground type vocabulary preserved); DML
and SELECT execute as validated SQL. Engine-neutral posture;
DSL→SQL teaching echo; phased plan.

Supersedes ADR-0001's sqlparser-rs reservation. Ticks Q4;
updates the ADR index and the Q1/Q2 notes. handoff-24 orients
the implementation session at Phase 1.
This commit is contained in:
claude@clouddev1
2026-05-19 20:09:58 +00:00
parent a049ff9aa0
commit 5438ba6a47
4 changed files with 595 additions and 4 deletions
+192
View File
@@ -0,0 +1,192 @@
# Session handoff — 2026-05-19 (24)
Twenty-fourth handover. A **design-only** session: it wrote and
accepted **ADR-0030 — Advanced mode: the standard-SQL surface**.
No code changed. A fresh session implements ADR-0030 from this
file + the ADR, starting at Phase 1.
## §1. State at handoff
**Branch:** `main`. Working tree clean (after this docs
commit). The session's commits since handoff-23 are docs-only —
ADR-0030, the ADR index, the `requirements.md` updates, and
this handoff. All local; push asynchronously, not blocking.
**Tests:** unchanged — **1240 passing, 0 failing, 1 ignored**
as of `a049ff9` (ADR-0029 complete). This session touched no
code. **Clippy:** clean as of the same commit.
## §2. What was decided — ADR-0030
Read **`docs/adr/0030-advanced-mode-sql-surface.md`** — the
spec, complete and accepted. Its shape, and *why* it landed
where it did (the design went through two rounds of user
input — §3 records the reasoning so it is not relitigated):
- **SQL is grammar in the unified tree, not a separate parser
(§1).** SQL statements are authored as `CommandNode` /
`Node` grammar in the ADR-0024 tree and parsed by the
existing walker. `sqlparser-rs` is **not** used —
ADR-0001's reservation is superseded. The reason is
decisive: a batch SQL parser produces an AST and nothing
else; it cannot drive completion, highlighting, or hints.
The user requires SQL to have the *same* ambient assistance
as the DSL — and that assistance comes only from the
unified grammar tree (ADR-0022/0023/0024). So SQL must live
in the tree.
- **Mode gates the grammar (§2).** One grammar tree; simple
mode exposes the DSL subset, advanced mode adds the SQL
forms. Shared entry words (`create`, `insert`, …) get a
`Choice` of DSL + SQL forms under one `CommandNode` (how
`add` already holds four sub-commands); `select` is a new
SQL-only entry word.
- **Execution split (§4).** DDL → a typed `Command` → the
existing executor, so metadata, the playground type
vocabulary, and `STRICT` are preserved. DML and `SELECT`
executed as **validated SQL** (they change no schema, so a
typed `Command` buys nothing); the worker re-persists the
affected table after DML.
- **Engine-neutral (§5, §7).** Playground type vocabulary,
never engine storage types; no `STRICT` surfaced;
engine-neutral errors. Advanced mode is *standard SQL*, not
an engine console.
- **Assistance for free (§8).** Because SQL is in the tree,
the walker gives SQL completion, highlighting, hint prose,
the `[ERR]`/`[WRN]` indicator, and parse-error usage — the
same as the DSL, no SQL-specific assistance code.
- **The DSL→SQL teaching echo (§10).** A DSL command run in
advanced mode also prints its equivalent SQL.
- **Persistence / replay (§11).** DDL via `Command` keeps
`project.yaml` correct; DML re-persists its table;
`history.log` stores the literal line; replay re-runs lines
through the one walker.
### §3. Decisions the user made (do not relitigate)
1. **Full SQL scope** — DDL + DML + the full `SELECT` query
surface (joins, aggregates, `GROUP BY`/`HAVING`,
subqueries, `UNION`, CTEs). No pre-emptive cuts; if a slice
proves *genuinely* overwhelming, cutting it is an explicit
escalation to the user, never a silent trim.
2. **No fallback to engine types** — advanced DDL keeps the
playground's rich type vocabulary. The `SQL → Command`
path for DDL is what guarantees this.
3. **Advanced mode is standard SQL, engine-independent** — not
a SQLite console; no engine-specifics surface.
4. **SQL lives in the unified grammar tree** (not a separate
batch parser) — chosen specifically so SQL gets full
completion / highlighting / hints. The user understood and
accepted that this means authoring a SQL grammar
ourselves; `sqlparser-rs` is dropped.
5. **`Command` only where it pays.** DDL → `Command` (buys
metadata + types); DML / `SELECT` → validated SQL (a
`Command` buys nothing).
6. **The DSL→SQL echo shows in advanced mode only.**
7. The persisted / replayable representation must be
app-enterable syntax — `history.log` already stores the
literal line, so this holds.
## §4. The phased implementation plan
ADR-0030 §Implementation is canonical. Each phase is
independently shippable and test-guarded. The two large
grammar slices each warrant their **own focused ADR** when
taken up (precedent: ADR-0026 for the `WHERE` grammar).
1. **Foundations + first `SELECT`.** Mode-gate the grammar
(advanced unlocks the SQL nodes). Author the core SQL
**expression grammar** — the ADR-0026 superset
(arithmetic, function calls, `CASE`, the predicate set) —
*its own ADR*. A single-table `SELECT` (projection,
`WHERE`, `ORDER BY`, `LIMIT`) as a SQL `CommandNode`
`Command::Select` → worker `RunSelect` → the existing
data-table renderer. Replace the placeholder echo; add the
simple-mode "this is SQL" hint. Proves the path end to end
*with full walker assistance*.
2. **`SELECT` — full.** `JOIN`s, `GROUP BY`/`HAVING`,
aggregates, subqueries, `UNION`, CTEs — *its own ADR*.
3. **DML.** `INSERT`/`UPDATE`/`DELETE` grammar; the
execute-as-validated-SQL path; the worker re-persist step;
settle multi-row `INSERT` and `shortid` auto-fill on a SQL
`INSERT`.
4. **DDL.** `CREATE`/`DROP`/`ALTER TABLE`, `CREATE`/`DROP
INDEX` grammar → `Command`; the §5 type-name map; FK
clauses → `AddRelationship`; may land table-rename (`C1`).
5. **The DSL→SQL teaching echo** (§10).
6. **Polish** — `help sql`; engine-neutral error sweep;
typing-surface / matrix coverage; the `DOC1` SQL-surface
reference page.
## §5. Seams for the implementer (from a code survey)
Anchors for Phase 1:
- **The placeholder to replace:** `App::submit`
(`src/app.rs` ~9541014) — the advanced-mode branch echoes
the input with `advanced_mode.not_implemented` (~9961012)
and sends nothing to the worker. The DSL dispatch path
(`dispatch_dsl`, ~1089) is what advanced SQL joins.
- **The grammar tree:** `src/dsl/grammar/` — `mod.rs` holds
`Node`, `CommandNode`, the `REGISTRY`; `ddl.rs` / `data.rs`
hold the DSL command grammars; `expr.rs` holds the ADR-0026
expression grammar (the seed for the SQL expression
grammar); `shared.rs` shared fragments. The walker is in
`src/dsl/walker/`. SQL grammar is authored here; mode-gating
is added to the `REGISTRY` / walker.
- **Command core:** `Command` in `src/dsl/command.rs` — add a
`Select` variant. **Gotcha:** every exhaustive `match
Command` breaks (`verb` / `target_table` / `display_subject`
in `command.rs`; `execute_command_typed` in `runtime.rs`;
`build_translate_context` in `app.rs`; `command_kind_label`
in `tests/typing_surface/mod.rs`) — the ADR-0028/0029
pattern.
- **Worker:** `Request` enum + `handle_request` + the
`Database` method wrappers in `src/db.rs` (~444 on) — add
`RunSelect` (returns `DataResult`), and later a "run
validated DML + re-persist" request.
- **Result rendering:** a `Select` outcome →
`CommandOutcome::Query` → `AppEvent::DslDataSucceeded` →
`output_render::render_data_table` — all reused from `show
data`.
- **Mode:** `src/mode.rs` (`Mode::Simple`/`Advanced`);
`App::effective_mode` and the `:`-strip in `App::submit`
already work — the mode-gated grammar view plugs in there.
- **`sqlparser-rs` is not used** — do not add it (an
implementer may keep it only as a test oracle, off the
execution path).
- **Risk:** the `Node` taxonomy / walker may need extension to
carry SQL's grammar (deeper recursion for subqueries /
CTEs). Expect node-taxonomy work in Phases 12; ADR-0030
flags this.
## §6. How to take over
1. **Read this file, then `docs/adr/0030-advanced-mode-sql-
surface.md`** (the spec). Then `CLAUDE.md` (working-style
rules), `docs/requirements.md` (`Q4` ticked; `Q1`/`Q2`
unblocked), and skim `docs/simple-mode-limitations.md` —
ADR-0030 §4 commits advanced mode to lifting the
expression limits that file records.
2. **Run `cargo test`** — 1240 passing, 0 failing, 1 ignored.
3. **Run `cargo clippy --all-targets -- -D warnings`** —
clean.
4. **Start ADR-0030 Phase 1.** First sub-step is the SQL
**expression grammar** — write its focused ADR before
authoring it (ADR-0026 is the model and the seed). Then the
single-table `SELECT` end to end. Land the `Command::
Select` match-arm sweep with the worker request in one
commit (the exhaustive-match breakage forces it).
5. Escalate, do not guess, on anything ADR-0030 left per-phase
— multi-row `INSERT`, `shortid` on SQL `INSERT`, any
`Node`-taxonomy extension that changes the walker's
contract.
## §7. What else is open
ADR-0030 is the active design. Other open clusters, unchanged
from handoff-23 §5 (prioritisation is a **user decision**):
snapshot/undo `U`-series (ADR-0006 written, unbuilt); m:n
convenience `C4`; modify-relationship `C3a`; the `show` family
`V5`; rename-table `C1` (may fall out of ADR-0030 Phase 4);
friendly-error sweep `H1`; CI `TT5`; session-log / Markdown
export `V4`.