feat: ADR-0036 Phase 1 — validate advanced-mode INSERT literals + show the value

Capture literal VALUES at parse onto Command::SqlInsert (no grammar change,
no reparse); validate them against column types before the still-verbatim
insert (reusing impl_value_for for DSL-parity wording); read them in the
error enricher so a constraint error names the real value. Execution,
auto-fill, and command identity unchanged. Adds run_sql_insert_with_literals
(runtime path); run_sql_insert stays the no-capture raw entry.

Proven: malformed date 2025/01/15 now refused in advanced-mode SQL; replayed
UNIQUE shows the real value. Tests +3 (expression runs, multi-row, natural
order) + 2 flipped/strengthened. 1930 pass / 0 fail / 0 skip; clippy clean.
This commit is contained in:
claude@clouddev1
2026-05-26 21:58:25 +00:00
parent dc9a4759ce
commit 1d5534b2bd
8 changed files with 312 additions and 22 deletions
+23 -1
View File
@@ -1570,6 +1570,19 @@ fn user_value_for_column(command: &Command, column: &str) -> Option<crate::dsl::
.iter()
.find(|(c, _)| c == column)
.map(|(_, v)| v.clone()),
// ADR-0036 Phase 1: a single-row literal SQL INSERT retains its
// captured literals, so a constraint error can name the real
// value. Explicit column list only here (natural order needs the
// schema); multi-row is skipped (which row conflicted is
// ambiguous) — both degrade to the neutral "that value".
Command::SqlInsert {
listed_columns,
literal_rows,
..
} if literal_rows.len() == 1 && !listed_columns.is_empty() => {
let idx = listed_columns.iter().position(|c| c == column)?;
literal_rows[0].get(idx).cloned().flatten()
}
_ => None,
}
}
@@ -2236,8 +2249,17 @@ async fn execute_command_typed(
listed_columns,
row_source,
returning,
literal_rows,
} => database
.run_sql_insert(sql, src, target_table, listed_columns, row_source, returning)
.run_sql_insert_with_literals(
sql,
src,
target_table,
listed_columns,
row_source,
returning,
literal_rows,
)
.await
.map(CommandOutcome::Insert),
// A SQL `UPDATE` (advanced mode; ADR-0033 §2). Grammar-as-