fix(parquet): support mask filtering across skipped pages#10288
fix(parquet): support mask filtering across skipped pages#10288hhhizzz wants to merge 3 commits into
Conversation
|
@alamb 👋Hi, This PR is now ready for review and updated with the latest main. |
|
Ok, thank ou @hhhizzz -- I will try. It just takes me a qhile to review non trivial PRs like this (not just the code, but also the approach). |
Thanks, and no rush at all. Please take whatever time works for you. I completely understand that reviewing the overall approach of a non-trivial change takes significantly more time than just reviewing the code. I’ve been focusing on this area recently, and I added a few diagrams to the PR description in the hope that they make the design and execution flow easier to understand. |
fd2a99e to
4fef178
Compare
Accumulate loaded-range-safe mask chunks until batch_size so sparse page pruning does not fragment logical batches or predicate evaluation. Cover cache policies, initial selections, multiple predicates, and nested columns with different page boundaries.
|
@alamb 👋 A brief update since your last note: I simplified the implementation I also realized that, in earlier iterations, I let the scope grow by pursuing I have now narrowed this PR to one behavioral goal: making Mask execution The PR has 604 additions and 168 deletions overall, but when Whenever your schedule allows, I’d really appreciate your review of the |
Which issue does this PR close?
Rationale for this change
Mask-backed row selection decodes every row covered by a mask chunk before applying Arrow's filter kernel. Page pruning can leave sparse in-memory column chunks where complete pages were never fetched. If a mask chunk crosses one of those gaps, decoding requests data that is not present and fails with an invalid sparse-page offset.
Previously,
Autoavoided this failure by falling back to selectors when page pruning was detected, while an explicitRowSelectionPolicy::Maskcould still fail. This PR makes skipped pages safe for Mask execution, allowing explicit Mask to work andAutoto retain its density-based strategy choice.What changes are included in this PR?
The reader now computes
LoadedRowRanges: absolute row-group intervals whose backing pages are loaded for every Parquet leaf in the current projection.For each predicate projection and the final output projection, it:
skip_records()to cross gaps without decoding missing pages.These ranges are decoder safety boundaries, not output batch boundaries.
ParquetRecordBatchReaderaccumulates multiple safe chunks and their mask bits until it reachesbatch_size, then consumes and filters once. Sparse pages therefore do not fragment one logical batch into one batch per loaded range.When no projected pages were skipped,
LoadedRowRangesis omitted and the existing Mask fast path is unchanged. No masks are intersected or rewritten; the original selection mask is still applied by Arrow's filter kernel.Loaded row range example
Consider two projected columns with different page boundaries and the selection mask
100000000001:[0, 4)[4, 6)[6, 8)[8, 10)[10, 12)100000000001[4, 8)[8, 12)[0, 6)[6, 10)The intersection does not need to match any one column's page boundaries. It is a row-domain guarantee: every row in the result has backing page data in every projected leaf. For example,
[0, 4)is a valid subset of Column B's[0, 6)page.Mask execution and batch behavior
With
batch_size = 12, the reader processes the example as follows:[0, 4)with mask1000skip_records(7)to row 11, then read[11, 12)with mask1The unloaded gap is never decoded, while the loaded-range boundary remains invisible to callers as a batch boundary.
Are these changes tested?
Coverage includes:
Auto, with predicate caching enabled and disabled;List<Struct<...>>projections whose leaves have different page boundaries;Validated with:
The current GitHub CI run is green, including Parquet tests, Clippy, MSRV, macOS, Windows, wasm32, and PySpark integration.
Are there any user-facing changes?
There are no public API or source compatibility changes.
Explicit Mask execution no longer fails when page pruning creates sparse column data.
Autono longer falls back to selectors solely because pages were pruned, and sparse loaded ranges no longer fragment logical output batches. These changes can affect execution performance characteristics, but not output semantics.