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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -85,18 +85,21 @@ fn form_c_rejects_number_for_text_column() {
|
||||
|
||||
#[test]
|
||||
fn form_c_wrong_value_count_is_invalid() {
|
||||
// Customers Form C expects exactly two values (id:serial
|
||||
// skipped). Three values is a count mismatch — caught at
|
||||
// parse time now.
|
||||
// Customers Form C expects exactly two values (id:serial skipped).
|
||||
// Three values is a count mismatch. As of issue #17 it parses
|
||||
// structurally (so the friendly arity diagnostic fires); the
|
||||
// user-facing invalidity is the validity verdict (`[ERR]`).
|
||||
let schema = schema_serial_pk();
|
||||
let a = assess_at_end(
|
||||
"insert into Customers ('Alice', 'a@b.c', 'extra')",
|
||||
&schema,
|
||||
);
|
||||
assert!(
|
||||
!matches!(a.state, InputState::Valid),
|
||||
"Form C with too many values must be invalid, got {:?}",
|
||||
a.state,
|
||||
let input = "insert into Customers ('Alice', 'a@b.c', 'extra')";
|
||||
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 C with too many values must light the [ERR] verdict",
|
||||
);
|
||||
crate::snap!("form_c_wrong_count", a);
|
||||
}
|
||||
|
||||
+6
-23
@@ -1,37 +1,20 @@
|
||||
---
|
||||
source: tests/typing_surface/insert_form_b.rs
|
||||
assertion_line: 186
|
||||
assertion_line: 193
|
||||
description: "input=\"insert into Customers values (1, 'Alice', 'a@b.c')\" cursor=50"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "insert into Customers values (1, 'Alice', 'a@b.c')",
|
||||
cursor: 50,
|
||||
state: DefiniteErrorAt(
|
||||
30,
|
||||
),
|
||||
state: Valid,
|
||||
hint: Some(
|
||||
Prose(
|
||||
"for `Name`: Type a quoted string (e.g. 'Alice') or null (`id` auto-generated — skipped here; list columns explicitly, e.g. `insert into T (...) values (...)`, to set it.) (trying to write SQL? switch with `mode advanced`, or prefix `:` to run once)",
|
||||
"without a column list, supply 2 value(s) for `Name`, `Email` — `id` auto-generated; 3 given (trying to write SQL? switch with `mode advanced`, or prefix `:` to run once)",
|
||||
),
|
||||
),
|
||||
completion: Some(
|
||||
Completion {
|
||||
replaced_range: (
|
||||
50,
|
||||
50,
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
Candidate {
|
||||
text: "null",
|
||||
kind: Keyword,
|
||||
mode: Both,
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
parse_result: Err(
|
||||
"Invalid(definite)",
|
||||
completion: None,
|
||||
parse_result: Ok(
|
||||
"Insert",
|
||||
),
|
||||
}
|
||||
|
||||
+5
-7
@@ -1,22 +1,20 @@
|
||||
---
|
||||
source: tests/typing_surface/insert_form_b.rs
|
||||
assertion_line: 168
|
||||
assertion_line: 173
|
||||
description: "input=\"insert into Customers values ('Alice')\" cursor=38"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "insert into Customers values ('Alice')",
|
||||
cursor: 38,
|
||||
state: DefiniteErrorAt(
|
||||
37,
|
||||
),
|
||||
state: Valid,
|
||||
hint: Some(
|
||||
Prose(
|
||||
"after `insert into Customers values ('Alice'`, expected `,` — usage: insert into <Table> [(<col>[, ...])] [values] (<value>[, ...])",
|
||||
"without a column list, supply 2 value(s) for `Name`, `Email` — `id` auto-generated; 1 given",
|
||||
),
|
||||
),
|
||||
completion: None,
|
||||
parse_result: Err(
|
||||
"Invalid(definite)",
|
||||
parse_result: Ok(
|
||||
"Insert",
|
||||
),
|
||||
}
|
||||
|
||||
+21
-8
@@ -1,22 +1,35 @@
|
||||
---
|
||||
source: tests/typing_surface/insert_form_c.rs
|
||||
assertion_line: 101
|
||||
assertion_line: 104
|
||||
description: "input=\"insert into Customers ('Alice', 'a@b.c', 'extra')\" cursor=49"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "insert into Customers ('Alice', 'a@b.c', 'extra')",
|
||||
cursor: 49,
|
||||
state: DefiniteErrorAt(
|
||||
39,
|
||||
),
|
||||
state: Valid,
|
||||
hint: Some(
|
||||
Prose(
|
||||
"after `insert into Customers ('Alice', 'a@b.c'`, expected `)` — usage: insert into <Table> [(<col>[, ...])] [values] (<value>[, ...])",
|
||||
"without a column list, supply 2 value(s) for `Name`, `Email` — `id` auto-generated; 3 given",
|
||||
),
|
||||
),
|
||||
completion: None,
|
||||
parse_result: Err(
|
||||
"Invalid(definite)",
|
||||
completion: Some(
|
||||
Completion {
|
||||
replaced_range: (
|
||||
49,
|
||||
49,
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
Candidate {
|
||||
text: "values",
|
||||
kind: Keyword,
|
||||
mode: Both,
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
parse_result: Ok(
|
||||
"Insert",
|
||||
),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user