`AddRowOffsetExec::compute_row_offsets` (rust/lance/src/io/exec/rowids.rs:470-484) adjusts each row's offset by the number of deleted rows below it using a peekable iterator over the fragment's deletion vector. The iterator only advances forward, so it assumes addresses within a fragment run arrive in ascending order. For an out-of-order address the delete count is stuck at the previous (higher) offset's value, producing a wrong `_rowoffset`.
Example: fragment with deletions at offsets 10 and 60; addresses arrive as [offset 100, offset 50]. Offset 100 correctly subtracts 2, but offset 50 also subtracts 2 (iterator already consumed both entries) instead of 1.
This is fed unsorted user-ordered addresses by the take path (rust/lance/src/dataset/take.rs:376-379, `compute_row_offset_array` on the original request-order `row_addrs`). Stable row ids make it much more reachable: after updates/compaction, taking by sorted row ids yields unsorted addresses within a fragment.
Fix options: pre-sort per fragment and map results back, or use `DeletionVector`'s rank support (count of deleted offsets < x) per address instead of a forward-only iterator.
Only affects `_rowoffset` output, not row content.
`AddRowOffsetExec::compute_row_offsets` (rust/lance/src/io/exec/rowids.rs:470-484) adjusts each row's offset by the number of deleted rows below it using a peekable iterator over the fragment's deletion vector. The iterator only advances forward, so it assumes addresses within a fragment run arrive in ascending order. For an out-of-order address the delete count is stuck at the previous (higher) offset's value, producing a wrong `_rowoffset`.
Example: fragment with deletions at offsets 10 and 60; addresses arrive as [offset 100, offset 50]. Offset 100 correctly subtracts 2, but offset 50 also subtracts 2 (iterator already consumed both entries) instead of 1.
This is fed unsorted user-ordered addresses by the take path (rust/lance/src/dataset/take.rs:376-379, `compute_row_offset_array` on the original request-order `row_addrs`). Stable row ids make it much more reachable: after updates/compaction, taking by sorted row ids yields unsorted addresses within a fragment.
Fix options: pre-sort per fragment and map results back, or use `DeletionVector`'s rank support (count of deleted offsets < x) per address instead of a forward-only iterator.
Only affects `_rowoffset` output, not row content.