grammar+walker: 3j — shared insert/update/delete entry words (ADR-0033 §2 / Amendments 1 & 3)

Wire `insert`/`update`/`delete` as shared DSL/SQL entry words through the
category-grouped dispatcher (ADR-0033 Amendment 1): the Advanced SQL nodes
move off the dev words (`sqlinsert`/`sql_update`/`sql_delete`) to the real
keywords, registered alongside the Simple DSL nodes. Remove the dev-word
scaffold; collapse build_sql_{insert,update,delete} to source.trim();
de-duplicate the two REGISTRY entry-word listing sites.

Dispatch model (ADR-0033 Amendment 3, written this round):
- A command is the mode-rooted grammar-path outcome; identity is intrinsic.
  Advanced mode tries SQL first, falling back to the Simple DSL command when
  no SQL branch matches a token (`delete … --all-rows` falls back;
  `update … --all-rows` does not — the SET expression absorbs it, harmless
  since the engine treats `--all-rows` as a comment).
- Simple mode commits the DSL candidate for a shared word, surfacing the real
  DSL error; bare "this is SQL" is reserved for SQL-only entry words
  (`select`/`with`). A content rejection on the SQL candidate (internal
  table) is committed, never masked by the DSL fallback.

Combined DSL-error + advanced-SQL pointer (ADR-0033 Amendment 3): a Simple-mode
definite DSL error that would run as SQL in advanced mode gains the
`advanced_mode.also_valid_sql` suffix — in the live hint (ambient_hint_in_mode)
and on submit (dispatch_dsl), via the shared advanced_alternative_note — so the
actionable DSL fix and the mode pointer coexist (submit covers constructs that
surface only on submit, e.g. `delete … returning`).

Internal-table rejection symmetrised (/runda finding B, ADR-0030 §6): the DSL
data-command target slots (insert/update/delete/show data/show table) gained
reject_internal_table, so `__rdbms_*` tables are refused in Simple mode too —
previously only the advanced SQL grammar rejected them.

Mode-awareness: classify_input_with_schema_in_mode and
invalid_ident_at_cursor_in_mode stop leaking the advanced SQL view into
simple-mode hints for shared words.

Tests: dev-word inputs migrated to the real words (advanced); DSL grammar /
completion / phase-D / db tests parse in Simple mode (the DSL surface); replay
keeps its advanced-mode model (one stale assertion fixed); dispatcher routing,
combined-pointer, and internal-table tests added. Suite 1626 pass / 0 fail /
1 ignored; clippy --all-targets -D warnings clean.

Defer M4 (execution-time mode side-channel; tracked in requirements.md) to its
own ADR.
This commit is contained in:
claude@clouddev1
2026-05-23 21:13:39 +00:00
parent c16196fc7f
commit d5c7f63513
22 changed files with 956 additions and 314 deletions
+13 -3
View File
@@ -9573,8 +9573,13 @@ mod tests {
/// Pull the `RowFilter` out of an `update` / `delete` parsed
/// from DSL — the readable way to build a complex `Expr`.
/// Parses in Simple mode: `update`/`delete` are shared entry
/// words since sub-phase 3j (ADR-0033 Amendment 3), so the DSL
/// `Command::Update`/`Delete` is only produced in Simple mode.
fn parse_filter(dsl: &str) -> RowFilter {
match crate::dsl::parser::parse_command(dsl).expect("filter parse") {
match crate::dsl::parser::parse_command_in_mode(dsl, crate::mode::Mode::Simple)
.expect("filter parse")
{
crate::dsl::command::Command::Update { filter, .. }
| crate::dsl::command::Command::Delete { filter, .. } => filter,
other => panic!("expected update/delete, got {other:?}"),
@@ -9775,9 +9780,14 @@ mod tests {
// --- explain / query plans (ADR-0028) -------------------
/// Parse a non-`explain` query command for use as the inner
/// command of `explain_query_plan`.
/// command of `explain_query_plan`. Simple mode: `explain`
/// wraps the DSL `show data` / `update` / `delete` commands
/// (ADR-0028; SQL DML is not explainable, ADR-0030 §13 OOS-2),
/// and `update`/`delete` only yield the DSL variant in Simple
/// mode (shared entry words since sub-phase 3j).
fn parse_inner(dsl: &str) -> Command {
crate::dsl::parser::parse_command(dsl).expect("inner command parse")
crate::dsl::parser::parse_command_in_mode(dsl, crate::mode::Mode::Simple)
.expect("inner command parse")
}
#[tokio::test]