feat: Add row-group-local RowSelection support to the push decoder - #10702
feat: Add row-group-local RowSelection support to the push decoder#10702haohuaijin wants to merge 2 commits into
Conversation
This PR adds |
02cae11 to
a9157e1
Compare
There was a problem hiding this comment.
Thanks @haohuaijin for this work!
From the DataFusion side: root fix for apache/datafusion#24352 / apache/datafusion#24355 — into_builder preserving local selections is exactly it. Core looks correct and well-tested; small notes inline. Happy to take the DataFusion migration (apache/datafusion#24358) once this lands.
|
Thanks for you reviews @zhuqi-lucas, i update in c0369a6 |
alamb
left a comment
There was a problem hiding this comment.
Thank you @haohuaijin and @zhuqi-lucas -- I spent quite a while reading this one in detail; I think it is looking very good -- I had some comment and encapsulation nits which I would like to fix before merging but I don't think they are strictly required
|
|
||
| /// Row-selection configuration shared by the Arrow reader builders. | ||
| /// | ||
| /// `Global` is the legacy configuration formed by `with_row_groups` and |
There was a problem hiding this comment.
nit: it would be nice to put these docs directly on the enum variants, along with a description of what they mean, not just where they came from / what they are used for
Something like this, perhaps:
/// Row-selection configuration shared by the Arrow reader builders.
pub(crate) enum RowGroupPlan {
/// (IS THIS TRUE?) Selection: first select any row_groups, adn then apply the RowSelection to
/// any remaining rows.
/// formed by `with_row_groups` and ....)
Global {
...
}There was a problem hiding this comment.
(I am happy to make these changes myself, but I wanted to double check first)
| } | ||
|
|
||
| impl RowGroupSelection { | ||
| /// Creates a row-group-local selection. |
There was a problem hiding this comment.
it might be good to define what row-group-local means more precisely -- specifically something like "The selection is relative to the rows in that selection (e.g. row 100 means the 100th row in the row group)"
| // `row_groups` and `selection` straddle `projection`/`filter` so that the | ||
| // legacy configuration keeps its historical field order; only the | ||
| // row-group-local fields are new. | ||
| let (row_groups, selection, row_group_selections) = match &self.row_group_plan { |
There was a problem hiding this comment.
this feels overly complicated. Why not derive Debug for RowGroupPlan and display it as normal here
| pub(crate) batch_size: usize, | ||
|
|
||
| pub(crate) row_groups: Option<Vec<usize>>, | ||
| pub(crate) row_group_plan: RowGroupPlan, |
| row_groups: Some(row_groups), | ||
| ..self | ||
| } | ||
| /// |
There was a problem hiding this comment.
If we are going to keep this wording I suggest making async stream builders a link too -- something like
- /// On [`ParquetPushDecoderBuilder`] and async stream builders, which
- /// additionally offer `with_row_group_selections`, this cannot be combined
- /// with that method; attempting to do so returns an error from `build`.
+ /// On [`ParquetPushDecoderBuilder`] and [`ParquetRecordBatchStreamBuilder`],
+ /// which additionally offer `with_row_group_selections`, this cannot be
+ /// combined with that method; attempting to do so returns an error from
+ /// `build`.
///
/// [`ParquetPushDecoderBuilder`]: crate::arrow::push_decoder::ParquetPushDecoderBuilder
+ /// [`ParquetRecordBatchStreamBuilder`]: crate::arrow::async_reader::ParquetRecordBatchStreamBuilder| has_predicates: _, | ||
| } = frontier; | ||
| let row_group_plan = match queued { | ||
| QueuedRowGroups::Global { |
There was a problem hiding this comment.
Can we also make this a method on QueuedRowGroups (like queued.into_plan() or something like that?
| ) -> Self { | ||
| Self { | ||
| ) -> Result<Self, ParquetError> { | ||
| let queued = match row_group_plan { |
There was a problem hiding this comment.
This looks like it would more naturally be a constructor / from impl on RowGroupPlan -- to construct QueuedRowGroups from a RowGroupPlan
That would also encapsulate the compleixty more
| /// error from [`Self::build`]. Calling this method more than once replaces | ||
| /// the previous row-group-local configuration. | ||
| /// | ||
| /// ```no_run |
There was a problem hiding this comment.
It took me some time to figure out (with some help) that we duplicated with_row_group_selections twice because it isn't supported via the serialized reader
| /// than its row group skips the trailing rows, while a selection longer | ||
| /// than its row group returns an error from [`Self::build`]. Each | ||
| /// selection is passed through without being re-partitioned, so no | ||
| /// conversion between the bitmap and selector representations is forced |
There was a problem hiding this comment.
I think the mention of different representations of the selection is confusing here (it seems like an irrelevant implementation detail)
|
I also ran |
Which issue does this PR close?
Rationale for this change
DataFusion makes row-group-local selection decisions (
ParquetAccessPlan), but the reader APIs only accept selected row groups plus a single globalRowSelection. Callers must concatenate per-row-group selections into one global selection, which arrow-rs then re-partitions back into per-row-group selections during decoding. This round trip is wasted work and loses each selection's representation (bitmap vs. selector).What changes are included in this PR?
RowGroupSelection(a row group index plus an optional row-group-localRowSelection) andParquetPushDecoderBuilder::with_row_group_selections. Entries decode in the supplied order, omitted row groups are skipped,Nonereads the whole row group, and each selection keeps its bitmap or selector representation.with_row_groups/with_row_selection: the setters share an internal state machine (RowGroupPlan) that reports conflicting combinations as an error frombuild()regardless of call order. The legacy API combination is unchanged.build()validates per-row-group plans eagerly: out-of-bounds indices and selections longer than their row group are errors; shorter selections skip the trailing rows.ParquetPushDecoder::into_builderpreserves remaining local selections (still in local coordinates), so adaptive scans compose with the new API.with_row_groupson the push decoder now returns aParquetErrorduring decoding instead of panicking.The sync and async builders are unchanged; the async builder already delegates to the push decoder, so extending the API to it is a small follow-up if needed.
Are these changes tested?
Yes, new tests cover bitmap- and selector-backed local selections (including out-of-order row groups and short selections), skip/replace semantics, mutual exclusion in all four call orders, build-time validation,
into_builderround-trips, and the unchanged legacy combination. All existing tests pass.Are there any user-facing changes?
New public API:
RowGroupSelectionandParquetPushDecoderBuilder::with_row_group_selections, with doc examples. No breaking changes; one behavior change: out-of-boundswith_row_groupsindices on the push decoder now error during decoding instead of panicking.