Skip to content

perf: range scans check bounds via per-row comparison opcode instead of baking bound into seek #666

Description

@iheitlager

Description

For range-scan scenarios (read_indexed_range, update_filtered_range, delete_filtered_range), our codegen checks the upper/lower bound manually per row via an explicit IdxCompareGT/Gt opcode plus a Goto, whereas oracle bakes the bound directly into the seek opcode itself (e.g. SeekGT ... p4=1) followed by a plain Next/Rewind loop with no per-row comparison opcode at all.

This is a smaller cost per row than #661/#663/#664, but it's paid on every row scanned, so it compounds on large range scans.

Complexity

Estimate: small
Reasoning: Codegen change to fold the range predicate into the seek opcode's own bound-check behavior (as SQLite's VDBE seek opcodes support), removing the separate per-row comparison + branch. Scoped to src/codegen/select/range_scan.rs (and its UPDATE/DELETE equivalents if they share the codegen path).

Context

Found via the top-down opcode-level sweep (codegen vs oracle EXPLAIN) across tests/performance/crud.rs's scenarios. Lowest priority of the batch (#661, #663, #664, this) since per-row cost is smaller, but flagged since it's a straightforward, well-understood VDBE idiom mismatch.

Acceptance Criteria

  • Range-scan codegen (read/update/delete) uses a bound-carrying seek opcode instead of a separate per-row IdxCompareGT/Gt+Goto
  • Opcode comparison against oracle's EXPLAIN shows the per-row comparison opcode removed for these scenarios
  • cargo bench --bench crud -- read_indexed_range / update_filtered_range / delete_filtered_range shows measurable improvement
  • Existing range-scan correctness/parity tests still pass

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions