Add src/dsl/sql_functions.rs (KNOWN_SQL_FUNCTIONS) as the shared source of truth at sql_expr_ident slots: - #15: offer the functions as Tab candidates under a new CandidateKind::Function + ninth Theme colour tok_function (blue, distinct from keyword/identifier/type). - #16: restore the column-typo flag the #6 fix had dropped wholesale — invalid_ident_at_cursor now bails only when the partial prefix-matches a known function, else falls through to the schema-column check. A column named like a function (e.g. `count`) is deduped (column wins). `cast` is excluded — CAST(x AS type) is not a plain-call shape. The no-validation-allowlist posture stands: the list drives completion + the typo hint only, never parse-time acceptance. Docs: ADR-0022 Amendment 6, ADR-0031 status note, README index, requirements I3/I4 + refreshed test baseline.
This commit is contained in:
@@ -686,6 +686,92 @@ can be revisited if hints routinely need more.
|
||||
full-screen snapshots (the empty-state placeholder and the `insert`
|
||||
usage hint now wrap to their full text instead of being clipped).
|
||||
|
||||
## Amendment 6 — Curated SQL function names: completion + typing-time typo hint (2026-05-30)
|
||||
|
||||
The advanced-mode SQL expression grammar (ADR-0031) accepts a
|
||||
function-call *shape* — `name(args)` — at its `sql_expr_ident` slot
|
||||
but, by design, does **not** know which names are real functions
|
||||
(ADR-0031 §1: the slot is `IdentSource::Columns`, optimised for the
|
||||
common case of a column reference; the walker is a structural matcher,
|
||||
not an evaluator). That left two gaps at this slot, raised as issues
|
||||
#15 and #16:
|
||||
|
||||
- **#16 (regression).** The earlier issue-#6 function-call validator
|
||||
fix dropped `invalid_ident_at_cursor`'s "no such column" flag at
|
||||
every `sql_expr_ident` position — necessary to stop a false positive
|
||||
on a function name like `sum`, but it also silenced the typing-time
|
||||
signal for a *genuine* column typo in an incomplete expression
|
||||
(`select Agx` before a `FROM` brings the schema-existence diagnostic
|
||||
into scope). Typing-time became *less* eager than submit-time.
|
||||
- **#15 (discovery).** Tab at a `sql_expr_ident` slot offered schema
|
||||
columns + a few expression keywords (`null`, `distinct`, `case`, …)
|
||||
but no function names, so a learner had to already know `sum` / `avg`
|
||||
/ `upper` to type them.
|
||||
|
||||
Both want the same thing: a single source of truth for *"what SQL
|
||||
function names does this playground recognise"*.
|
||||
|
||||
**Change:**
|
||||
|
||||
1. **Curated list.** New `src/dsl/sql_functions.rs` with
|
||||
`KNOWN_SQL_FUNCTIONS` (sorted, lowercase — a pinned invariant) and
|
||||
an `is_known_function_prefix()` helper. A deliberately *curated
|
||||
pedagogical set*, not "every SQLite built-in": the aggregates a
|
||||
learner meets first (`count`/`sum`/`avg`/`min`/`max`), the common
|
||||
scalars (`length`/`upper`/`lower`/`trim`/`substr`/`coalesce`/`abs`/
|
||||
`round`), and a broader scalar tier (`date`/`datetime`/`strftime`/
|
||||
`hex`/`ifnull`/`nullif`/`replace`/`instr`/`typeof`/`random`).
|
||||
**`cast` is deliberately excluded** — SQLite's `CAST(expr AS type)`
|
||||
is not a plain-call shape the expression grammar parses, so
|
||||
offering it would surface a candidate that does not complete; it
|
||||
stays out until the grammar grows a dedicated `CAST` form.
|
||||
2. **#16 — restore the typo flag, narrowly.** `invalid_ident_at_cursor`
|
||||
no longer bails wholesale at a `sql_expr_ident` slot. It bails only
|
||||
when the partial prefix-matches a known function name; otherwise it
|
||||
falls through to the existing schema-column check, which flags "no
|
||||
such column" unless the partial prefix-matches a real column. So
|
||||
`select Agx` warns again at typing time while `select sum` does not.
|
||||
The submit-time `unknown_column` diagnostic path is untouched; the
|
||||
issue-#6 lockdown tests (`genuine_column_typo_in_complete_select_…`,
|
||||
`advanced_select_partial_function_name_not_flagged_…`) still pass.
|
||||
3. **#15 — offer functions as candidates.** A new completion source
|
||||
(Source 1.8) contributes `KNOWN_SQL_FUNCTIONS` (prefix-filtered like
|
||||
every other source) whenever the expected set contains a
|
||||
`sql_expr_ident` slot, ordered after keywords/types (a learner
|
||||
reads clause keywords first, then discovers callables).
|
||||
4. **New `CandidateKind::Function` + `tok_function` colour.** Like
|
||||
Amendment 4 gave types their own class, function candidates get a
|
||||
dedicated kind and a ninth `Theme` colour field (`tok_function`,
|
||||
a blue distinct from keyword purple, identifier teal, and type
|
||||
pink/magenta in both `dark()` and `light()`) so a callable reads
|
||||
apart from a clause keyword, a column reference, and a column type.
|
||||
|
||||
**No-validation-allowlist posture stands (ADR-0031 §6/§7).** The list
|
||||
drives *completion* and the *typo hint* only — never parse-time
|
||||
acceptance. An unknown or engine-specific function still parses (the
|
||||
grammar admits the call shape generically) and surfaces an
|
||||
engine-neutral *execution* error, exactly as before.
|
||||
|
||||
**Pedagogy:** the same dedicated-colour rationale as Amendment 4 — a
|
||||
learner can tell *"this is a function"* at a glance, and Tab now
|
||||
*teaches* the function vocabulary instead of assuming it.
|
||||
|
||||
**Coverage:** `sql_functions::{list_is_sorted_and_lowercase,
|
||||
list_has_no_duplicates, cast_is_excluded, prefix_match_is_case_insensitive,
|
||||
empty_prefix_matches_all, unknown_prefix_does_not_match}`;
|
||||
`completion::{sql_expr_slot_offers_known_function_candidates,
|
||||
projection_slot_offers_known_function_candidates,
|
||||
sql_function_candidates_filter_by_prefix,
|
||||
sql_function_candidates_carry_function_kind,
|
||||
function_candidates_absent_at_non_expression_slots,
|
||||
cast_is_not_offered_as_a_function_candidate,
|
||||
invalid_ident_fires_for_genuine_typo_at_sql_expr_slot,
|
||||
invalid_ident_does_not_fire_for_function_prefix_at_sql_expr_slot,
|
||||
invalid_ident_does_not_fire_for_column_prefix_at_sql_expr_slot}`;
|
||||
`input_render::advanced_select_genuine_column_typo_before_from_warns_at_typing_time`;
|
||||
`theme::function_colour_is_distinct_from_keyword_identifier_and_type`.
|
||||
See ADR-0031's status note for the grammar-side anchor.
|
||||
|
||||
## Out of scope
|
||||
|
||||
Deliberately deferred to keep this ADR shippable as a single
|
||||
|
||||
@@ -409,3 +409,24 @@ Later phases extend the same fragment:
|
||||
set the engine-neutrality posture and the no-allowlist rule.
|
||||
- `docs/simple-mode-limitations.md` — the DSL limits this grammar
|
||||
lifts for advanced mode (§1, §4).
|
||||
|
||||
## Status note — known-function list layered on the slot (2026-05-30)
|
||||
|
||||
The `sql_expr_ident` slot is `IdentSource::Columns` and, per §1 / §5,
|
||||
does **not** itself know which identifiers are function names — it
|
||||
optimises for the common case (a column reference) and admits the
|
||||
function-call shape structurally; §5 explicitly noted "function names
|
||||
are not completed … a typed function name simply is not a candidate".
|
||||
**ADR-0022 Amendment 6** layers a curated known-function list
|
||||
(`src/dsl/sql_functions.rs`) on top of this slot, consumed two ways:
|
||||
as Tab-completion candidates so a learner can discover `sum` / `upper`
|
||||
/ … (issue #15 — softening §5's "not completed" line to "completed
|
||||
from a curated pedagogical list, not an allowlist for validation"),
|
||||
and as the allow-list that lets the typing-time column-typo hint stay
|
||||
strict at this slot — flag a partial as "no such column" only when it
|
||||
matches neither a schema column nor a known function name (issue #16).
|
||||
The grammar here is unchanged, and §6/§7's no-validation-allowlist
|
||||
posture stands: the list drives completion + the typo hint, **not**
|
||||
parse-time acceptance (an unknown function still parses and surfaces an
|
||||
engine-neutral execution error). The list sits in the completion /
|
||||
hint layer above the grammar.
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
+24
-14
@@ -26,18 +26,21 @@ repo is pushed).
|
||||
|
||||
## Test baseline
|
||||
|
||||
After the ADR-0027 highlight / hint follow-up (precise WARNING
|
||||
spans, the diagnostic overlay + hint wiring, the
|
||||
`LIKE`-on-numeric WARNING, the debounce state machine) plus
|
||||
two manual-testing bug fixes (optional trailing-flag
|
||||
completion; the `--resume` temp-project pointer):
|
||||
**1131 passing, 0 failing, 1 ignored** (`cargo test` — the one
|
||||
ignored test is a long-standing `` ```ignore `` doc-test in
|
||||
`src/friendly/mod.rs`). Clippy clean with the nursery lint
|
||||
group enabled. (Earlier reference points: 1100 after ADR-0027's
|
||||
initial ship; 1079 after ADR-0026 (complex WHERE expressions);
|
||||
1039 after ADR-0025 (indexes); 1006 after ADR-0024 + the
|
||||
handoff-14 cleanup; 449 after B2/C2.)
|
||||
After ADR-0022 Amendment 6 (the curated SQL function-name list —
|
||||
issues #15 tab-completion + #16 typing-time typo hint):
|
||||
**1538 lib unit tests passing, 0 failing, 1 ignored**
|
||||
(`cargo test --lib`; the full `cargo test` across every binary is
|
||||
2107 passing, 0 failing, 1 ignored — the one ignored is a
|
||||
long-standing `` ```ignore `` doc-test in `src/friendly/mod.rs`).
|
||||
Clippy clean with the nursery lint group enabled. (Earlier
|
||||
reference points — lib counts: 1131 after the ADR-0027 highlight /
|
||||
hint follow-up + the optional-trailing-flag / `--resume`
|
||||
manual-testing fixes; 1100 after ADR-0027's initial ship; 1079
|
||||
after ADR-0026 (complex WHERE expressions); 1039 after ADR-0025
|
||||
(indexes); 1006 after ADR-0024 + the handoff-14 cleanup; 449
|
||||
after B2/C2. Note the intervening issue fixes #8/#13/#12/#7/#9
|
||||
landed tests without a baseline bump; this is the first refresh
|
||||
since ADR-0027.)
|
||||
|
||||
---
|
||||
|
||||
@@ -105,11 +108,18 @@ handoff-14 cleanup; 449 after B2/C2.)
|
||||
rolling history is out of scope per OOS-6 / N4.)*
|
||||
- [ ] **I3** Tab completion for app commands, DSL keywords, table
|
||||
names, column names, and SQL keywords.
|
||||
*(Refinement 2026-05-30, issue #15: SQL expression slots
|
||||
(`sql_expr_ident`) now also offer a curated set of SQL function
|
||||
names — `KNOWN_SQL_FUNCTIONS` in `src/dsl/sql_functions.rs`,
|
||||
surfaced as a new `CandidateKind::Function` — ADR-0022 Amendment 6.
|
||||
The broad tab-completion goal stays open.)*
|
||||
- [ ] **I4** Syntax highlighting for both the DSL and SQL.
|
||||
*(Refinement 2026-05-29, issue #8: column data types now carry a
|
||||
dedicated `HighlightClass::Type` / `tok_type` colour, distinct from
|
||||
identifiers and clause keywords — ADR-0022 Amendment 4. The broad
|
||||
highlighting goal stays open.)*
|
||||
identifiers and clause keywords — ADR-0022 Amendment 4; a further
|
||||
refinement 2026-05-30, issue #15: SQL function-name candidates carry
|
||||
a dedicated `tok_function` colour (the ninth `Theme` token colour,
|
||||
ADR-0022 Amendment 6). The broad highlighting goal stays open.)*
|
||||
- [ ] **I5** In-flight query/command cancellation (Ctrl-C in the
|
||||
output area or input field).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user