feat: ADR-0036 Phase 2 — validate advanced-mode UPDATE SET literals + retain the value

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.
This commit is contained in:
claude@clouddev1
2026-05-26 22:20:12 +00:00
parent 2f0af31b3b
commit 8c3b13b313
10 changed files with 413 additions and 27 deletions
+20 -4
View File
@@ -11,8 +11,13 @@ consolidating the two modes and a concrete auto-fill difference confirmed
that even the single-row literal case is **not** identical across modes).
**Phase 1 implemented 2026-05-26** (`INSERT … VALUES` literal validation +
offending-value retention; capture-at-parse, no grammar change, execution
unchanged). Phases 2 (`UPDATE … SET` literals) and 3 (completion
hinting/highlighting) pending.
unchanged). **Phase 2 implemented 2026-05-26** (`UPDATE … SET` literal
validation + offending-value retention; the same capture-at-parse technique
on the SET assignment list — `capture_set_literals` in `data.rs`
classifying each top-level RHS literal-vs-expression, validating literals in
`do_sql_update`, and reading them in `user_value_for_column`; `WHERE` is not
validated, execution stays verbatim). Phase 3 (completion
hinting/highlighting — the only part needing a grammar change) pending.
**Augments** **ADR-0030 §4** and **ADR-0033 §10** — it does **not**
supersede them and does **not** change the execution model. Advanced-mode
@@ -246,8 +251,19 @@ execution), only its `Result` is used.
the verbatim insert; the enricher reads them. Covers single- and
multi-row, with or without `RETURNING`/`ON CONFLICT`, because execution
is untouched.
- **Phase 2 — `UPDATE … SET` literal validation** (same technique on the
SET assignment list).
- **Phase 2 (implemented 2026-05-26) — `UPDATE … SET` literal
validation.** The same capture-at-parse technique on the SET assignment
list: `build_sql_update` calls `capture_set_literals`, which walks the
matched tokens (no reparse) and classifies each *top-level* `SET col =
<rhs>` into `(col, Some(Value))` for a bare literal (incl. a signed
number) or `(col, None)` for an expression — using paren depth so a comma
inside a function call or a `where` inside a scalar subquery is never
mistaken for an assignment/clause boundary, and so the trailing top-level
`WHERE` predicate is excluded. `Command::SqlUpdate` gains a
`set_literals` payload; `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` is deliberately not validated (§2).
- **Phase 3 — completion hinting / highlighting.** This is the *only*
part that needs a grammar change: a `Choice(typed-literal-slot,
sql_expr)` at each value position (reusing the DSL's live
+1 -1
View File
File diff suppressed because one or more lines are too long