Skip to content

fix(sql): validate table qualifiers in FTS function column args (SQLR-15)#168

Merged
joaoh82 merged 1 commit into
mainfrom
fix/sqlr-15-fts-qualifier-validation
Jun 11, 2026
Merged

fix(sql): validate table qualifiers in FTS function column args (SQLR-15)#168
joaoh82 merged 1 commit into
mainfrom
fix/sqlr-15-fts-qualifier-validation

Conversation

@joaoh82

@joaoh82 joaoh82 commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Problem

SQLR-14 (#167) made single-table scope reject unknown table qualifiers in plain column references, but fts_match / bm25_score extract their column argument syntactically (they need the column name to find the FTS index, not its value), so the qualifier never went through RowScope::lookup:

SELECT id FROM docs WHERE fts_match(bogus.body, 'rust');          -- worked; 'bogus.' silently dropped
SELECT id FROM docs ORDER BY bm25_score(bogus.body, 'rust') DESC; -- same (scalar-eval path)

This was the explicitly-deferred gap in #167's "Known gaps".

Fix

  • Add scope_name(&self) -> Option<&str> to the RowScope trait — Some(alias-or-table-name) for SingleTableScope, None for JoinedScope (per-qualifier resolution happens in lookup) and GroupRowScope (output rows carry no qualifier).
  • resolve_fts_args now runs check_single_scope_qualifier on a two-part CompoundIdentifier column arg, producing the SQLR-14 wording: unknown table qualifier '<q>' in column reference '<q>.<col>'. Identifiers with 3+ parts now error like the main evaluator instead of silently truncating.

The optimizer probes (try_fts_probe, identify_indexed_arg_and_literal) only match bare identifiers, so qualified args always take the scalar path — which now validates.

Audit of other syntactic extraction sites (per acceptance criteria)

  • JSON functions, vec_distance_* — no gap: their args are evaluated through eval_expr_scopescope.lookup, which already runs the SQLR-14 check.
  • Pager catalog re-parses (pager/mod.rs) — trusted engine-written sqlrite_master SQL, not user input. No change.
  • Two real gaps in other statement contexts, filed as follow-ups: marvinapp SQLR Rename crate to sqlrite-engine on crates.io #17 (CREATE INDEX ix ON t (bogus.col) strips qualifier) and SQLR Release v0.1.2 #18 (aggregating-path ORDER BY bogus.name strips qualifier when matching output columns).

Tests

7 new end-to-end tests in src/sql/mod.rs covering the acceptance matrix: matching table-name qualifier (incl. case-insensitive), matching alias, alias-shadows-table-name, bogus qualifier in WHERE (fts_match), WHERE-comparison and ORDER BY (bm25_score), and a ranking test proving qualified args still produce correct top-k via the scalar path. The three negative tests fail on the unfixed executor and pass with the fix.

Full verification: workspace test suite (CI exclusions) all green, cargo fmt --check clean, clippy/doc warnings unchanged (pre-existing, none on changed code). REPL + file-backed save/reopen smoke checks pass.

Docs

  • docs/supported-sql.md — qualified-references bullet now covers FTS function args (SQLR-15)
  • docs/fts.md — qualified-arg behavior documented for both functions, plus a note that qualified args skip the optimizer probe
  • docs/sql-engine.md — expression-evaluator section mentions the RowScope::scope_name() path

🤖 Generated with Claude Code

…-15)

fts_match() / bm25_score() extract their column argument syntactically
(they need the column name to find the index, not its value), so the
qualifier never passed through RowScope::lookup and SQLR-14's check.
A bogus qualifier was silently dropped: fts_match(bogus.body, 'q') ran
as fts_match(body, 'q').

Add RowScope::scope_name() (Some(name) for single-table scope, None for
joined / group-row scopes) and run check_single_scope_qualifier in
resolve_fts_args when the column arg is a CompoundIdentifier. Error
wording matches SQLR-14. 3+-part identifiers now error like the main
evaluator instead of being silently truncated.

Audited the other syntactic extraction sites: JSON and vec_distance
helpers evaluate args through eval_expr_scope (already covered);
pager catalog re-parses are trusted engine-written SQL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
rust-sqlite Ready Ready Preview, Comment Jun 11, 2026 2:36pm

Request Review

@joaoh82 joaoh82 merged commit 8d23155 into main Jun 11, 2026
21 checks passed
@joaoh82 joaoh82 deleted the fix/sqlr-15-fts-qualifier-validation branch June 11, 2026 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant