Skip to content

core/translate: SQLite-compatible affinity check for index seeks#7385

Open
jussisaurio wants to merge 1 commit into
mainfrom
sqlite-index-affinity-ok-for-seeks
Open

core/translate: SQLite-compatible affinity check for index seeks#7385
jussisaurio wants to merge 1 commit into
mainfrom
sqlite-index-affinity-ok-for-seeks

Conversation

@jussisaurio

@jussisaurio jussisaurio commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Fixes #7373, #7374, and a related IN-list/IN-subquery affinity bypass surfaced during review.

Bug

Index seeks applied the indexed column's affinity to the seek key. SQLite applies the comparison's resolved affinity (sqlite3CompareAffinity). The two diverge for cross-type comparisons:

  -- l.txt TEXT, r.flag INTEGER, the empty string '' stored in l.
  SELECT l.txt, r.flag FROM l JOIN r ON l.txt <= r.flag;

Comparison affinity here is NUMERIC. The TEXT index forced TEXT onto the integer probe ('0') and the b-tree comparator then ranked '' below '0' lexicographically rather than SQLite's type ordering (INTEGER < TEXT) that puts every integer below every text. False match.

The same shape broke WHERE x IN (SELECT y ...) with TEXT x / INTEGER y: the IN ephemeral stored integers, the TEXT index never coerced them, and the b-tree sorted every integer below every text key — zero rows.

Fix

  • Port SQLite's sqlite3IndexAffinityOk (Affinity::index_affinity_ok)
  • cache the resolved comparison affinity on each Constraint at construction,
  • consult it at every index-selection site (regular b-tree, auto-index, ephemeral-subquery, IN-seek) so incompatible indexes are rejected up front. At emission, index_seek_affinities now reads the cached comparison affinity instead of the column's.

Snapshot churn: 7 files. All are affinity-character shifts (D→C for cross-type numeric comparisons) or removal of redundant TEXT-on-TEXT Affinity opcodes that SQLite never emits either.

Fixes #7373.
Fixes #7374.

@jussisaurio jussisaurio changed the title core/translate: SQLite-compatible affinity check for index seeks wip/core/translate: SQLite-compatible affinity check for index seeks Jun 5, 2026
@jussisaurio jussisaurio changed the title wip/core/translate: SQLite-compatible affinity check for index seeks wip/fix/core/translate: SQLite-compatible affinity check for index seeks Jun 5, 2026
@jussisaurio jussisaurio force-pushed the sqlite-index-affinity-ok-for-seeks branch 3 times, most recently from 27fc1b0 to eecf763 Compare June 5, 2026 10:14
Fixes #7373, #7374, and a related IN-list/IN-subquery affinity bypass
surfaced during review.

The bug. Index-driven seeks applied the indexed column's affinity to
the seek key. SQLite applies the comparison's resolved affinity
(`sqlite3CompareAffinity`). The two diverge for cross-type comparisons:

  -- l.txt TEXT, r.flag INTEGER, '' stored in l.
  SELECT l.txt, r.flag FROM l JOIN r ON l.txt <= r.flag;

Comparison affinity here is NUMERIC. The TEXT index forced TEXT onto
the integer probe ('0') and the b-tree comparator then ranked '' below
'0' lexicographically — rather than SQLite's type ordering (INTEGER <
TEXT) that puts every integer below every text. False match.

Same shape broke `WHERE x IN (SELECT y ...)` with TEXT x / INTEGER y:
the IN ephemeral stored integers, the TEXT index never coerced them,
and the b-tree sorted every integer below every text key — zero rows.

The fix. Port SQLite's `sqlite3IndexAffinityOk` (`Affinity::index_affinity_ok`),
cache the resolved comparison affinity on each `Constraint` at
construction, and consult it at every index-selection site (regular
b-tree, auto-index, ephemeral-subquery, IN-seek) so incompatible
indexes are rejected up front. At emission, `index_seek_affinities`
now reads the cached comparison affinity instead of the column's.

Snapshot churn: 7 files. All are affinity-character shifts (D→C for
cross-type numeric comparisons) or removal of redundant TEXT-on-TEXT
Affinity opcodes that SQLite never emits either.

Fixes #7373.
Fixes #7374.
@jussisaurio jussisaurio force-pushed the sqlite-index-affinity-ok-for-seeks branch from eecf763 to 8f58d4f Compare June 5, 2026 10:20
@jussisaurio jussisaurio marked this pull request as ready for review June 5, 2026 10:21
@jussisaurio jussisaurio changed the title wip/fix/core/translate: SQLite-compatible affinity check for index seeks fix/core/translate: SQLite-compatible affinity check for index seeks Jun 5, 2026
@penberg penberg changed the title fix/core/translate: SQLite-compatible affinity check for index seeks core/translate: SQLite-compatible affinity check for index seeks Jun 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

R1 - RIGHT JOIN differs from CROSS selection on mixed-affinity predicate R1 - Derived-table JOIN ON applies wrong affinity for TEXT <= INTEGER

1 participant