-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Speed up Parquet filter pushdown v4 (Predicate evaluation cache for async_reader) #7850
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
1f93a93
2e01e56
0bd08c3
d6ecbd4
7cd5518
4520048
e6281bc
6b6d4fc
b696b66
f60581f
1851f0b
5e414a8
58add51
470cc01
2bf3d38
2cf1a8f
86e149c
4d24172
be134d6
eecaf99
5537bcb
b835163
5132de8
253dad3
5d9781e
2e20902
721d00c
f8aed80
884b591
4f6b918
6c53bfd
8ebe579
c240a52
30a0d1c
ed3ce13
42d5520
6e618b3
f70e46a
15d6826
bec6d9c
3e05cb2
4d64dc0
8da582b
315e463
1db701a
bea4433
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -620,7 +620,12 @@ where | |
| // (pre) Fetch only the columns that are selected by the predicate | ||
| let selection = plan_builder.selection(); | ||
| row_group | ||
| .fetch(&mut self.input, predicate.projection(), selection) | ||
| .fetch( | ||
| &mut self.input, | ||
| predicate.projection(), | ||
| selection, | ||
| batch_size, | ||
| ) | ||
| .await?; | ||
|
|
||
| let mut cache_projection = predicate.projection().clone(); | ||
|
|
@@ -676,7 +681,12 @@ where | |
| } | ||
| // fetch the pages needed for decoding | ||
| row_group | ||
| .fetch(&mut self.input, &projection, plan_builder.selection()) | ||
| .fetch( | ||
| &mut self.input, | ||
| &projection, | ||
| plan_builder.selection(), | ||
| batch_size, | ||
| ) | ||
| .await?; | ||
|
|
||
| let plan = plan_builder.build(); | ||
|
|
@@ -696,6 +706,7 @@ where | |
| Ok((self, Some(reader))) | ||
| } | ||
|
|
||
| /// Compute which columns are used in filters and the final (output) projection | ||
| fn compute_cache_projection(&self, projection: &ProjectionMask) -> Option<ProjectionMask> { | ||
| let filters = self.filter.as_ref()?; | ||
| let mut cache_projection = filters.predicates.first()?.projection().clone(); | ||
|
|
@@ -934,9 +945,11 @@ impl InMemoryRowGroup<'_> { | |
| input: &mut T, | ||
| projection: &ProjectionMask, | ||
| selection: Option<&RowSelection>, | ||
| batch_size: usize, | ||
| ) -> Result<()> { | ||
| let metadata = self.metadata.row_group(self.row_group_idx); | ||
| if let Some((selection, offset_index)) = selection.zip(self.offset_index) { | ||
| let selection = selection.expand_to_batch_boundaries(batch_size, self.row_count); | ||
| // If we have a `RowSelection` and an `OffsetIndex` then only fetch pages required for the | ||
| // `RowSelection` | ||
| let mut page_start_offsets: Vec<Vec<u64>> = vec![]; | ||
|
|
@@ -1869,6 +1882,7 @@ mod tests { | |
| assert_eq!(total_rows, 730); | ||
| } | ||
|
|
||
| #[ignore] | ||
|
||
| #[tokio::test] | ||
| async fn test_in_memory_row_group_sparse() { | ||
| let testdata = arrow::util::test_util::parquet_test_data(); | ||
|
|
@@ -2423,4 +2437,53 @@ mod tests { | |
| let result = reader.try_collect::<Vec<_>>().await.unwrap(); | ||
| assert_eq!(result.len(), 1); | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn test_cached_array_reader_sparse_offset_error() { | ||
| use futures::TryStreamExt; | ||
|
|
||
| use crate::arrow::arrow_reader::{ArrowPredicateFn, RowFilter, RowSelection, RowSelector}; | ||
| use arrow_array::{BooleanArray, RecordBatch}; | ||
|
|
||
| let testdata = arrow::util::test_util::parquet_test_data(); | ||
| let path = format!("{testdata}/alltypes_tiny_pages_plain.parquet"); | ||
| let data = Bytes::from(std::fs::read(path).unwrap()); | ||
|
|
||
| let async_reader = TestReader::new(data); | ||
|
|
||
| // Enable page index so the fetch logic loads only required pages | ||
| let options = ArrowReaderOptions::new().with_page_index(true); | ||
| let builder = ParquetRecordBatchStreamBuilder::new_with_options(async_reader, options) | ||
| .await | ||
| .unwrap(); | ||
|
|
||
| // Skip the first 22 rows (entire first Parquet page) and then select the | ||
| // next 3 rows (22, 23, 24). This means the fetch step will not include | ||
| // the first page starting at file offset 0. | ||
| let selection = RowSelection::from(vec![RowSelector::skip(22), RowSelector::select(3)]); | ||
|
|
||
| // Trivial predicate on column 0 that always returns `true`. Using the | ||
| // same column in both predicate and projection activates the caching | ||
| // layer (Producer/Consumer pattern). | ||
| let parquet_schema = builder.parquet_schema(); | ||
| let proj = ProjectionMask::leaves(parquet_schema, vec![0]); | ||
| let always_true = ArrowPredicateFn::new(proj.clone(), |batch: RecordBatch| { | ||
| Ok(BooleanArray::from(vec![true; batch.num_rows()])) | ||
| }); | ||
| let filter = RowFilter::new(vec![Box::new(always_true)]); | ||
|
|
||
| // Build the stream with batch size 8 so the cache reads whole batches | ||
| // that straddle the requested row range (rows 0-7, 8-15, 16-23, …). | ||
| let stream = builder | ||
| .with_batch_size(8) | ||
| .with_projection(proj) | ||
| .with_row_selection(selection) | ||
| .with_row_filter(filter) | ||
| .build() | ||
| .unwrap(); | ||
|
|
||
| // Collecting the stream should fail with the sparse column chunk offset | ||
| // error we want to reproduce. | ||
| let _result: Vec<_> = stream.try_collect().await.unwrap(); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.