Wire the DSL's column-typed value slots into the advanced-mode SQL
UPDATE/UPSERT `SET col = <rhs>` value position so a learner gets the same
per-column hint ("for `Email`: type a quoted string") and live numeric-
shape mismatch highlight the simple-mode DSL gives.
Discriminate literal-vs-expression with a boundary-aware lookahead
(shared::SET_VALUE), NOT the naive `Choice(typed-slot, sql_expr)` the ADR
originally sketched: the walker's Choice is first-match-wins with no
backtrack, so a typed slot would greedily match the leading `1` of `1 + 2`
and commit, regressing valid SQL (e.g. the existing `values (1, 1 + 2)`
test). The lookahead peeks the whole value position: a literal routes to
the typed slot only when it fills the position up to the next
`,`/`)`/`;`/`where`/`returning`/end; everything else falls through to the
full sql_expr grammar unchanged. The SET column ident gets
`writes_column: true` so `current_column` drives the slot + hint.
Scope: Phase 3a covers UPDATE's assignment list and INSERT's ON CONFLICT
DO UPDATE SET. Phase 3b (INSERT VALUES — needs a per-position grammar
restructure + multi-row) is deferred. Records ADR-0036 Amendment 1 with
the mechanism correction + the 3a/3b split.
Tests: 1939 passing (+5), 0 failed, 0 skipped, 1 ignored; clippy clean.
Mirror Phase 1's capture-at-parse technique on the UPDATE SET assignment
list. build_sql_update calls the new capture_set_literals (data.rs), which
walks the matched tokens (no reparse, no grammar change) and classifies
each top-level `SET col = <rhs>` as a literal (Some, incl. signed numbers)
or an expression (None), using paren depth so a comma inside a function
call or a `where` inside a scalar subquery is not mistaken for a boundary,
and the trailing top-level WHERE is excluded.
Command::SqlUpdate gains set_literals; do_sql_update validates the literals
against their column types via the shared impl_value_for before the still
verbatim update; user_value_for_column reads them so a constraint error
names the offending value. WHERE stays unvalidated; execution and command
identity are unchanged.
Also corrects the stale data.rs header comment (DSL typed slots are wired,
not "deferred") and flips ADR-0036 + README to Phases 1–2 implemented.
Tests: 1934 passing (+4), 0 failed, 0 skipped, 1 ignored; clippy clean.
Narrow ADR-0036 from "bind literals via the DSL path" to "validate literal
values (shared validators) + retain them; execute verbatim, keep auto-fill
and command identity mode-specific" — after a concrete auto-fill difference
(non-PK serial) confirmed the modes aren't identical even for single-row
literals. Augments (no longer supersedes) ADR-0030 §4 / ADR-0033 §10;
Amendment 3 stands. README + forward-notes on 0030/0033 updated. Records
requirements.md X4 (serial auto-fill — possible bug) and X5 (framework
cohesion / share-mechanics-not-commands).
Records the decision that advanced-mode SQL DML should stop handing literal
data values to the engine as text and instead parse/validate/bind them
through the DSL's proven path — closing the value-validation gap, the
hint/highlight gap, and the offending-value-in-errors gap together. Verbatim
text stays for expressions, WHERE, INSERT…SELECT, and SELECT (full SQL
surface preserved; ADR-0026's limited Expr not imposed). Narrows ADR-0030 §4
/ ADR-0033 §10 once accepted; SELECT half of §4 stands.
Includes a characterization test (tests/sql_insert.rs) proving the bind-layer
gap: the DSL rejects the malformed date 2025/01/15, advanced-mode SQL accepts
it. Forward-notes added to ADR-0030/0033; README index updated.
Status: Proposed (design + /runda done; pending go-ahead to implement).