feat: ADR-0036 Phase 3a — live typed-slot hints + highlighting for SQL SET values
Wire the DSL's column-typed value slots into the advanced-mode SQL
UPDATE/UPSERT `SET col = <rhs>` value position so a learner gets the same
per-column hint ("for `Email`: type a quoted string") and live numeric-
shape mismatch highlight the simple-mode DSL gives.
Discriminate literal-vs-expression with a boundary-aware lookahead
(shared::SET_VALUE), NOT the naive `Choice(typed-slot, sql_expr)` the ADR
originally sketched: the walker's Choice is first-match-wins with no
backtrack, so a typed slot would greedily match the leading `1` of `1 + 2`
and commit, regressing valid SQL (e.g. the existing `values (1, 1 + 2)`
test). The lookahead peeks the whole value position: a literal routes to
the typed slot only when it fills the position up to the next
`,`/`)`/`;`/`where`/`returning`/end; everything else falls through to the
full sql_expr grammar unchanged. The SET column ident gets
`writes_column: true` so `current_column` drives the slot + hint.
Scope: Phase 3a covers UPDATE's assignment list and INSERT's ON CONFLICT
DO UPDATE SET. Phase 3b (INSERT VALUES — needs a per-position grammar
restructure + multi-row) is deferred. Records ADR-0036 Amendment 1 with
the mechanism correction + the 3a/3b split.
Tests: 1939 passing (+5), 0 failed, 0 skipped, 1 ignored; clippy clean.
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
//! `--all-rows` rail — a SQL `UPDATE` without `WHERE` runs as
|
||||
//! written (ADR-0030 §12). `RETURNING` (3g) lands later.
|
||||
|
||||
use crate::dsl::grammar::sql_expr;
|
||||
use crate::dsl::grammar::shared::SET_VALUE;
|
||||
use crate::dsl::grammar::sql_select::{RETURNING_CLAUSE, WHERE_CLAUSE, reject_internal_table};
|
||||
use crate::dsl::grammar::{IdentSource, Node, Word};
|
||||
|
||||
@@ -44,28 +44,33 @@ const TARGET_TABLE: Node = Node::Ident {
|
||||
};
|
||||
|
||||
/// The column on the left of one `SET col = expr` assignment.
|
||||
///
|
||||
/// `writes_column: true` resolves the column's type into
|
||||
/// `current_column` (and frames the value-slot hint via
|
||||
/// `pending_value_column`), so the RHS `SET_VALUE` lookahead can
|
||||
/// dispatch the column-typed slot for a lone literal (ADR-0036
|
||||
/// Phase 3a).
|
||||
const ASSIGN_COLUMN: Node = Node::Ident {
|
||||
source: IdentSource::Columns,
|
||||
role: "update_set_column",
|
||||
validator: None,
|
||||
highlight_override: None,
|
||||
writes_table: false,
|
||||
writes_column: false,
|
||||
writes_column: true,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
writes_cte_name: false,
|
||||
writes_projection_alias: false,
|
||||
};
|
||||
|
||||
/// `column_name '=' sql_expr` — the RHS reuses the shared
|
||||
/// expression grammar (ADR-0031), so literals, operators, `CASE`,
|
||||
/// function calls, and scalar subqueries are all admitted; the
|
||||
/// engine evaluates them at execution time.
|
||||
static ASSIGNMENT_NODES: &[Node] = &[
|
||||
ASSIGN_COLUMN,
|
||||
Node::Punct('='),
|
||||
Node::Subgrammar(&sql_expr::SQL_OR_EXPR),
|
||||
];
|
||||
/// `column_name '=' <value>` — the RHS is the boundary-aware
|
||||
/// `SET_VALUE` slot (ADR-0036 Phase 3a): a lone literal routes to the
|
||||
/// column-typed slot (live per-column hint + numeric-shape highlight,
|
||||
/// shared with the DSL), while any expression — arithmetic, a
|
||||
/// literal-prefixed form, `CASE`, function calls, scalar subqueries —
|
||||
/// falls through to the full `sql_expr` grammar (ADR-0031), which the
|
||||
/// engine evaluates at execution time.
|
||||
static ASSIGNMENT_NODES: &[Node] = &[ASSIGN_COLUMN, Node::Punct('='), SET_VALUE];
|
||||
static ASSIGNMENT: Node = Node::Seq(ASSIGNMENT_NODES);
|
||||
|
||||
/// `assignment ( ',' assignment )*`.
|
||||
|
||||
Reference in New Issue
Block a user