db: 3d fix — don't let shortid auto-fill mask INSERT arity mismatch

plan_shortid_autofill read exactly listed_columns.len() cells from
the materialised row source. When the row source produced a
different column count than the user's list, the extra columns were
silently dropped (wider → wrong data, insert succeeded) or read
out of range (narrower). Guard: if the materialised statement's
column_count differs from the listed-column count, skip auto-fill
and execute the verbatim statement so the engine reports the
mismatch — matching the non-auto-fill path. A friendly pre-flight
diagnostic remains sub-phase 3i.

Tests: VALUES with too many values; INSERT…SELECT with a wider and
a narrower projection — each rejected with nothing persisted.
This commit is contained in:
claude@clouddev1
2026-05-22 12:30:57 +00:00
parent 78ad476d24
commit 18d34d0d36
2 changed files with 62 additions and 0 deletions
+9
View File
@@ -5832,6 +5832,15 @@ fn plan_shortid_autofill(
// as concrete rows for the listed columns.
let listed_count = listed_columns.len();
let mut stmt = conn.prepare(row_source).map_err(DbError::from_rusqlite)?;
// Arity guard: if the row source's column count disagrees with
// the user's column list, do NOT auto-fill — reading
// `listed_count` cells would silently drop extra columns (or
// error opaquely on too few). Defer to the verbatim statement
// so the engine reports the mismatch as it does on the
// non-auto-fill path (a friendly pre-flight lands in 3i).
if stmt.column_count() != listed_count {
return Ok((sql.to_string(), Vec::new()));
}
let mut rows: Vec<Vec<rusqlite::types::Value>> = Vec::new();
{
let mut q = stmt.query([]).map_err(DbError::from_rusqlite)?;