Repository navigation
Description
Is your feature request related to a problem or challenge?
The Parquet reader currently accepts selected row groups and one global RowSelection:
builder
.with_row_groups(row_group_indexes)
.with_row_selection(row_selection)Current DataFusion flow
DataFusion stores the scan decision for each row group separately in ParquetAccessPlan. Row-level filters update each entry through scan_selection:
row group 0: bitmap selection
row group 1: skip
row group 2: selector selection
row group 3: scan all
Before constructing the Parquet reader, prepare calls into_overall_row_selection to convert this plan into two values:
row groups: [0, 2, 3]
selection: one global RowSelection covering row groups 0, 2, and 3
Full row groups become select(row_group_rows), while partial selections are appended to the same selection stream. Mixed bitmap and selector inputs must also be converted to one common representation.
DataFusion then passes both values to arrow-rs in DecoderBuilderConfig::build:
builder
.with_row_groups(row_group_indexes)
.with_row_selection(global_selection)When arrow-rs starts reading, RowGroupFrontier::next_readable_row_group uses the row-group metadata to partition this global selection back into row-group-local selections. The complete flow is therefore:
per-row-group selections
-> one global RowSelection
-> per-row-group selections during decoding
Describe the solution you'd like
Allow the push decoder to accept row-group-local selections directly:
pub struct RowGroupSelection {
row_group_index: usize,
selection: Option<RowSelection>,
}
builder.with_row_group_selections(vec![
RowGroupSelection::new(0, Some(bitmap_selection)),
RowGroupSelection::new(2, Some(selector_selection)),
RowGroupSelection::new(3, None),
]);Proposed behavior:
- Entries are decoded in the supplied order.
- Omitted row groups are skipped.
Nonereads the entire row group.- Each row group retains its bitmap or selector representation.
- Existing
with_row_groupsandwith_row_selectionAPIs remain supported. - Rebuilding a push decoder preserves the remaining local selections.
Describe alternatives you've considered
keep current behaviour
Additional context
find this when work on apache/datafusion#24186
Activity
haohuaijin commentedon Aug 11, 2026
cc @alamb , what do you think about this, this can remove the
each row group row selection->full row selection->each row group row selectionconvert, but for e2e performance it do not imporve in normal query i think.but it have two bennefit for the user that already have a
RowSelection.bitamporrowselecterrepresention in row_group level, current only support file levelRowSelectionto be backed by aBooleanBufferto reduce memory usage #10140 (comment)). but for fragmented case, we already have the bitmap...alamb commentedon Aug 12, 2026
i think this is a good idea (and a very clear ticket)
In my mind the trick will be how to mesh the existing apis (
with_row_groupsandwith_row_selection) and make sure we have a coherent documentation of what will happen if the user calls each onezhuqi-lucas commentedon Aug 14, 2026
Just a note that this missing capability is also the root cause of a wrong-results bug family in DataFusion, not only a performance/ergonomics improvement.
Because DataFusion collapses the per-row-group selections into one global
RowSelectionand then rebuilds the push decoder to drop row groups at runtime (into_builder().with_row_groups(...)), the global selection is not sliced to match the dropped row groups — selectors intended for a dropped RG get applied to the next surviving one, silently returning wrong rows:RowSelection.The behavior you list — "Rebuilding a push decoder preserves the remaining local selections" — is exactly what makes both bugs structurally impossible: if selections stay row-group-local, dropping a RG on rebuild keeps every survivor's selection aligned by construction, and DataFusion can also delete the parallel
rg_planbookkeeping it maintains today.DataFusion has shipped minimal stop-the-bleeding fixes for both (apache/datafusion#24354, apache/datafusion#24359), and is tracking the structural fix on its side in apache/datafusion#24358, which depends on this API landing here. Happy to help with the DataFusion-side migration once
with_row_group_selectionsis available. Thanks @haohuaijin for writing this up.[branch-55]: don't runtime-prune row groups while a page-index RowSel…
fix(parquet): don't runtime-prune row groups while a page-index RowSe…
fix(parquet): don't runtime-prune row groups while a page-index RowSe…
feat: Add `RowSelection::total_row_count` (#10704)
feat: Add `RowSelection::total_row_count` (apache#10704)
feat: Add row-group-local RowSelection support to the push decoder (a…
feat: Add `RowSelection::total_row_count` (apache#10704)
feat: Add `RowSelection::total_row_count` (apache#10704)
feat: Add row-group-local RowSelection support to the push decoder (a…
feat: Add `RowSelection::total_row_count` (apache#10704)
feat: Add row-group-local RowSelection support to the push decoder (a…
feat: Add `RowSelection::total_row_count` (apache#10704)
feat: Add row-group-local RowSelection support to the push decoder (a…
feat: Add `RowSelection::total_row_count` (apache#10704)
feat: Add row-group-local RowSelection support to the push decoder (a…
feat: Add `RowSelection::total_row_count` (apache#10704)
feat: Add row-group-local RowSelection support to the push decoder (a…