Skip to content

fix: read indexed column from index cursor in range-seek fast paths - #669

Merged
iheitlager merged 1 commit into
mainfrom
fix/664-indexed-range-column-reuse
Aug 30, 2026
Merged

fix: read indexed column from index cursor in range-seek fast paths#669
iheitlager merged 1 commit into
mainfrom
fix/664-indexed-range-column-reuse

Conversation

@iheitlager

Copy link
Copy Markdown
Member

Summary

  • Closes perf: read_indexed_range codegen re-reads table column already present in index key #664
  • The BETWEEN/LIKE-prefix/IN/forward-comparison index-seek fast paths (src/codegen/select/range_scan.rs) all re-read the seeked-on indexed column from the table row after IdxRowid+SeekRowid, even though that column's value is already sitting in the index cursor's own key. emit_matched_row now recognizes a plain bare-column select list and reads that one column straight off the index cursor instead — any other select-list shape (*, computed expressions) falls back to the original table-cursor read, unchanged.

Changes

Fixed

  • emit_matched_row (range_scan.rs) substitutes an index-cursor Column read (position 0, with the same RealAffinity fixup emit_column_read applies) for the select-list column matching the fast path's indexed column.
  • Applies to all four fast paths sharing emit_matched_row: try_compile_between_seek, try_compile_like_prefix_seek, try_compile_in_list_seek, try_compile_forward_comparison_seek.

Testing

  • cargo test --lib — 979 passed
  • cargo test --test '*' — full integration/oracle-parity suite, no failures
  • cargo clippy --all-targets — clean
  • EXPLAIN opcode sequence for read_indexed_range (SELECT id, n, x, f, s FROM bench_data WHERE x > 50000) now matches oracle's shape exactly — verified by diffing against sqlite3's own EXPLAIN
  • Spot-checked byte-for-byte against oracle: BETWEEN, IN, SELECT *, a computed expression (x*2), and >= — all fall back or apply correctly
  • cargo bench --bench crud -- read_indexed_rangeno measurable change at the 1mb fixture (within noise; no 50mb variant exists for this scenario). The eliminated Column read is a small fraction of per-row cost next to the btree seek/page touch and the other four column reads still read from the table.

Honest framing: this lands as a correctness/opcode-parity fix (satisfies acceptance criteria #1, #2, #4 from #664) rather than a benchmarked performance win (criterion #3 not met at this scale) — flagging this explicitly rather than overclaiming.

spend: ~1x estimate (small).

…664)

The BETWEEN/LIKE-prefix/IN/forward-comparison index-seek fast paths
(src/codegen/select/range_scan.rs) all re-read the indexed column from
the table row after IdxRowid+SeekRowid, even though that same column's
value is already sitting in the index cursor's own key the seek just
matched against — one redundant Column decode per row, on top of the
table lookup still needed for the other selected columns.

emit_matched_row now recognizes a plain bare-column select list
(bare_column_names) and substitutes an index-cursor Column read
(emit_indexed_column_read, position 0, with the same REAL-affinity
fixup emit_column_read applies) for the one column that matches the
seek's indexed column. Any other select-list shape (*, computed
expressions) falls back to the unchanged emit_row_via_sink path.

Verified: EXPLAIN opcode sequence for read_indexed_range
(SELECT id, n, x, f, s FROM bench_data WHERE x > 50000) now matches
oracle's shape exactly (Column reads x off the index cursor, not the
table). Full test suite and oracle-parity corpus pass; spot-checked
BETWEEN/IN/*/computed-expression queries byte-for-byte against oracle.

Benchmark note: cargo bench --bench crud -- read_indexed_range shows no
measurable change at the 1mb fixture (within noise, no 50mb variant
exists for this scenario) — the eliminated Column read is a small
fraction of per-row cost next to the btree seek/page touch and the
other four column reads. Landing as a correctness/opcode-parity fix,
not a benchmarked performance win.

spend: ~1x estimate (small).

Refs #664

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@iheitlager
iheitlager force-pushed the fix/664-indexed-range-column-reuse branch from 5453816 to aaaa1f9 Compare August 30, 2026 11:48
@iheitlager
iheitlager merged commit 43aa312 into main Aug 30, 2026
6 checks passed
@iheitlager
iheitlager deleted the fix/664-indexed-range-column-reuse branch August 30, 2026 11:52
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.

perf: read_indexed_range codegen re-reads table column already present in index key

1 participant