feat: bring simple-mode insert arity diagnostics to parity with advanced

A wrong-count simple-mode insert now shows the friendly per-column arity
message at typing time (instead of a bare "expected `,`/`)`") and is
blocked from dispatch at submit — unifying simple and advanced mode onto
the one ADR-0027 model (structural parse + ERROR diagnostic), where they
had diverged.

Grammar: a simple-mode-only arity gate (dsl_insert_value_list) routes a
wrong-count DSL insert tuple to the type-blind fallback so it matches
structurally and the per-tuple arity diagnostic fires. The gate is gated
to simple mode, so advanced behaviour is unchanged. count_tuple_values
and the target-column selection (insert_target_columns) are now shared
by both grammars.

Diagnostic: dml_insert_arity_diagnostics is mode-aware — advanced Form B
expects all columns; simple Form B/C expects the user-fillable columns
(serial/shortid auto-fill). It counts the DSL Form A role and scans the
keyword-less Form C tuple. New catalog keys name the fillable/auto split
and the all-auto-table case.

Submit: a wrong-count DSL insert now parses Ok + carries the ERROR
diagnostic, so a unified Ok-arm pre-flight (dsl_insert_count_mismatch_notes)
blocks dispatch and teaches; the previous Err-arm note retires.
advanced_alternative_note's gate now reads the validity verdict so it
still fires for the parse-Ok-with-error shape.

Docs: ADR-0036 Amendment 2 (+ README index) and requirements.md H1a.
This commit is contained in:
claude@clouddev1
2026-05-29 20:45:21 +00:00
parent 7cccf4eabb
commit 10e5197c19
16 changed files with 812 additions and 240 deletions
+25 -18
View File
@@ -154,16 +154,21 @@ fn form_b_in_progress_without_closing_paren_is_incomplete() {
#[test]
fn form_b_with_too_few_values_is_invalid_at_close_paren() {
let schema = schema_serial_pk();
let a = assess_at_end("insert into Customers values ('Alice')", &schema);
// Only one value supplied; Form B for Customers needs two.
// The grammar's typed slot list expects another `,<value>`
// before the `)`. Classify as DefiniteError or Incomplete
// (which one depends on whether the closing `)` is past
// the missing slot).
assert!(
!matches!(a.state, InputState::Valid),
"input with too-few values must NOT be Valid, got {:?}",
a.state,
let input = "insert into Customers values ('Alice')";
let a = assess_at_end(input, &schema);
// Only one value supplied; Form B for Customers needs two. As of
// issue #17 a wrong-count tuple parses *structurally* (so the
// friendly arity diagnostic can fire) — the user-facing invalidity
// is the validity verdict (the `[ERR]` indicator), not the
// structural `state`.
assert_eq!(
rdbms_playground::dsl::walker::input_verdict_in_mode(
input,
Some(&schema),
rdbms_playground::mode::Mode::Simple,
),
Some(rdbms_playground::dsl::walker::Severity::Error),
"too-few values must light the [ERR] verdict",
);
crate::snap!("form_b_too_few_values", a);
}
@@ -174,14 +179,16 @@ fn form_b_with_extra_value_for_serial_column_is_invalid() {
// (treating it as the first slot) means an extra value
// overall — Customers has 3 columns but Form B accepts 2.
let schema = schema_serial_pk();
let a = assess_at_end(
"insert into Customers values (1, 'Alice', 'a@b.c')",
&schema,
);
assert!(
!matches!(a.state, InputState::Valid),
"Form B with a value-for-serial must be invalid, got {:?}",
a.state,
let input = "insert into Customers values (1, 'Alice', 'a@b.c')";
let a = assess_at_end(input, &schema);
assert_eq!(
rdbms_playground::dsl::walker::input_verdict_in_mode(
input,
Some(&schema),
rdbms_playground::mode::Mode::Simple,
),
Some(rdbms_playground::dsl::walker::Severity::Error),
"Form B with a value-for-serial must light the [ERR] verdict",
);
crate::snap!("form_b_extra_serial_value", a);
}