grammar+db: 3e — SQL UPDATE grammar + execution (ADR-0033 §2)

New src/dsl/grammar/sql_update.rs: SQL_UPDATE_SHAPE =
<table> SET col = sql_expr (',' …)* [WHERE sql_expr] [';'], the
__rdbms_* target rejection, and the shared sql_expr on both the
assignment RHS and the predicate. No --all-rows rail — a SQL
UPDATE without WHERE runs as written (ADR-0030 §12). Reuses
sql_select::WHERE_CLAUSE (now pub(crate)) so the predicate
diagnostics are identical. The target uses the shared `table_name`
ident role (not a bespoke one) so the Phase-2 schema-existence and
predicate-warning passes collect it as a scope binding and check
the SET / WHERE columns for free — a bespoke role left them
unchecked (the cross-cut tests caught this).

Command::SqlUpdate { sql, target_table }; Request::RunSqlUpdate +
do_sql_update (execute validated SQL via execute_with_fk_enrichment,
re-persist the target CSV, append history.log). 3e surfaces the
affected-row count only; precise row output is RETURNING (3g), so
the update-success render skips a column-less data set rather than
showing a misleading "(no rows)" band. Behind the dev `sql_update`
entry word until 3j.

Tests: grammar accept/reject; integration (single/multi-col,
no-WHERE all-rows, sql_expr in SET, scalar subquery in SET,
zero-match success, history); walker cross-cut (unknown SET column
→ unknown_column, `= NULL` in WHERE → eq_null warning); app-level
render-guard both ways (column-less → count only; with columns →
table renders). 1524 green, clippy clean.
This commit is contained in:
claude@clouddev1
2026-05-22 13:57:21 +00:00
parent 18d34d0d36
commit 53808ed9d7
11 changed files with 646 additions and 5 deletions
+4 -1
View File
@@ -461,7 +461,10 @@ static WHERE_CLAUSE_NODES: &[Node] = &[
Node::Word(Word::keyword("where")),
Node::Subgrammar(&sql_expr::SQL_OR_EXPR),
];
static WHERE_CLAUSE: Node = Node::Seq(WHERE_CLAUSE_NODES);
/// `WHERE sql_expr`. `pub(crate)` so the SQL DML statements
/// (ADR-0033 — UPDATE / DELETE) reuse the exact same predicate
/// clause, keeping the Phase-2 predicate diagnostics identical.
pub(crate) static WHERE_CLAUSE: Node = Node::Seq(WHERE_CLAUSE_NODES);
static GROUP_BY_CLAUSE_NODES: &[Node] = &[
Node::Word(Word::keyword("group")),