WHERE expressions: wire into update/delete/show data + SQL gen (ADR-0026 steps 3-4)

Wires the stratified WHERE-expression fragment into the three
filter commands and compiles the resulting Expr to SQL.

Grammar (data.rs): the `update` / `delete` `where` clause is
now the expression fragment (`Subgrammar(&expr::OR_EXPR)`) in
place of the single `col = val` slot; `show data` gains an
optional `where <expr>` and an optional `limit <n>` (a
non-negative integer, validated at parse time). The
expression's right-hand operands are a schema-aware
`DynamicSubgrammar` so the hint panel still narrows to the
left column's type (ADR-0026 §8) — but the inner grammar is
permissive: a type-mismatched literal still parses (§7).

AST: `RowFilter::Where{column,value}` -> `RowFilter::Where(Expr)`;
`ShowData` gains `filter: Option<Expr>` and `limit: Option<u64>`.
A `RowFilter::eq` convenience constructor keeps simple-equality
call sites and tests readable.

SQL (db.rs): `compile_expr` lowers an `Expr` to a
parameterised WHERE — every literal a `?` placeholder,
identifiers `quote_ident`-quoted, `<>` for inequality. A
literal compared against a column binds through that column's
type where compatible and falls back to its syntactic shape on
a mismatch (§7 — permissive). `show data ... limit n` emits
`LIMIT ?` with an implicit primary-key `ORDER BY`, so it is a
stable "first n by primary key".

completion.rs: `invalid_ident_at_cursor` no longer mis-flags a
digit-led literal (`1`) as an unknown column now that the
WHERE operand slot also accepts a column reference; a
`ProseOnly` slot suppresses keyword candidates even when the
expected set also carries a column ident.

11 db integration tests cover AND / OR / NOT, BETWEEN, IN,
LIKE, filtered `show data`, and limit ordering; walker and
expr unit tests cover the parse surface. Type-mismatch /
`= NULL` diagnostic flagging (§7 highlight + hint) is the
remaining ADR-0026 piece.
This commit is contained in:
claude@clouddev1
2026-05-18 23:12:33 +00:00
parent 59e6a541bf
commit f75f71bbe4
22 changed files with 1013 additions and 193 deletions
@@ -24,6 +24,26 @@ Assessment {
text: "null",
kind: Keyword,
},
Candidate {
text: "true",
kind: Keyword,
},
Candidate {
text: "false",
kind: Keyword,
},
Candidate {
text: "Email",
kind: Identifier,
},
Candidate {
text: "Name",
kind: Identifier,
},
Candidate {
text: "id",
kind: Identifier,
},
],
},
),
@@ -10,6 +10,26 @@ Assessment {
hint: Some(
Candidates {
items: [
Candidate {
text: "not",
kind: Keyword,
},
Candidate {
text: "null",
kind: Keyword,
},
Candidate {
text: "true",
kind: Keyword,
},
Candidate {
text: "false",
kind: Keyword,
},
Candidate {
text: "(",
kind: Punct,
},
Candidate {
text: "Name",
kind: Identifier,
@@ -30,6 +50,26 @@ Assessment {
),
partial_prefix: "",
candidates: [
Candidate {
text: "not",
kind: Keyword,
},
Candidate {
text: "null",
kind: Keyword,
},
Candidate {
text: "true",
kind: Keyword,
},
Candidate {
text: "false",
kind: Keyword,
},
Candidate {
text: "(",
kind: Punct,
},
Candidate {
text: "Name",
kind: Identifier,
@@ -24,6 +24,54 @@ Assessment {
text: "null",
kind: Keyword,
},
Candidate {
text: "true",
kind: Keyword,
},
Candidate {
text: "false",
kind: Keyword,
},
Candidate {
text: "auto",
kind: Identifier,
},
Candidate {
text: "b",
kind: Identifier,
},
Candidate {
text: "d",
kind: Identifier,
},
Candidate {
text: "data",
kind: Identifier,
},
Candidate {
text: "dt",
kind: Identifier,
},
Candidate {
text: "k",
kind: Identifier,
},
Candidate {
text: "note",
kind: Identifier,
},
Candidate {
text: "r",
kind: Identifier,
},
Candidate {
text: "sid",
kind: Identifier,
},
Candidate {
text: "ts",
kind: Identifier,
},
],
},
),
@@ -24,6 +24,26 @@ Assessment {
text: "null",
kind: Keyword,
},
Candidate {
text: "true",
kind: Keyword,
},
Candidate {
text: "false",
kind: Keyword,
},
Candidate {
text: "Email",
kind: Identifier,
},
Candidate {
text: "Name",
kind: Identifier,
},
Candidate {
text: "id",
kind: Identifier,
},
],
},
),
@@ -10,6 +10,26 @@ Assessment {
hint: Some(
Candidates {
items: [
Candidate {
text: "not",
kind: Keyword,
},
Candidate {
text: "null",
kind: Keyword,
},
Candidate {
text: "true",
kind: Keyword,
},
Candidate {
text: "false",
kind: Keyword,
},
Candidate {
text: "(",
kind: Punct,
},
Candidate {
text: "Name",
kind: Identifier,
@@ -30,6 +50,26 @@ Assessment {
),
partial_prefix: "",
candidates: [
Candidate {
text: "not",
kind: Keyword,
},
Candidate {
text: "null",
kind: Keyword,
},
Candidate {
text: "true",
kind: Keyword,
},
Candidate {
text: "false",
kind: Keyword,
},
Candidate {
text: "(",
kind: Punct,
},
Candidate {
text: "Name",
kind: Identifier,