use std::collections::VecDeque;
use std::sync::Arc;
use arrow::array::{RecordBatch, RecordBatchOptions};
use arrow::datatypes::Schema;
use futures::StreamExt;
use futures::stream::BoxStream;
use parquet::DecodeResult;
use parquet::arrow::arrow_reader::metrics::ArrowReaderMetrics;
use parquet::arrow::arrow_reader::{ArrowReaderMetadata, RowSelectionPolicy};
use parquet::arrow::async_reader::AsyncFileReader;
use parquet::arrow::push_decoder::{ParquetPushDecoder, ParquetPushDecoderBuilder};
use datafusion_common::{DataFusionError, Result};
use datafusion_physical_expr::projection::Projector;
use datafusion_physical_plan::metrics::{BaselineMetrics, Gauge};
use crate::access_plan::PreparedAccessPlan;
use crate::row_filter::ParquetReadPlan;
pub(crate) struct DecoderBuilderConfig<'a> {
pub(crate) read_plan: &'a ParquetReadPlan,
pub(crate) batch_size: usize,
pub(crate) arrow_reader_metrics: &'a ArrowReaderMetrics,
pub(crate) force_filter_selections: bool,
pub(crate) decoder_limit: Option<usize>,
}
impl DecoderBuilderConfig<'_> {
pub(crate) fn build(
&self,
prepared_access_plan: PreparedAccessPlan,
metadata: ArrowReaderMetadata,
) -> ParquetPushDecoderBuilder {
let mut builder = ParquetPushDecoderBuilder::new_with_metadata(metadata)
.with_projection(self.read_plan.projection_mask.clone())
.with_batch_size(self.batch_size)
.with_metrics(self.arrow_reader_metrics.clone());
if self.force_filter_selections {
builder = builder.with_row_selection_policy(RowSelectionPolicy::Selectors);
}
if let Some(row_selection) = prepared_access_plan.row_selection {
builder = builder.with_row_selection(row_selection);
}
builder = builder.with_row_groups(prepared_access_plan.row_group_indexes);
if let Some(limit) = self.decoder_limit {
builder = builder.with_limit(limit);
}
builder
}
}
pub(crate) struct PushDecoderStreamState {
pub(crate) decoder: ParquetPushDecoder,
pub(crate) pending_decoders: VecDeque<ParquetPushDecoder>,
pub(crate) remaining_limit: Option<usize>,
pub(crate) reader: Box<dyn AsyncFileReader>,
pub(crate) projector: Projector,
pub(crate) output_schema: Arc<Schema>,
pub(crate) replace_schema: bool,
pub(crate) arrow_reader_metrics: ArrowReaderMetrics,
pub(crate) predicate_cache_inner_records: Gauge,
pub(crate) predicate_cache_records: Gauge,
pub(crate) baseline_metrics: BaselineMetrics,
}
impl PushDecoderStreamState {
pub(crate) fn into_stream(self) -> BoxStream<'static, Result<RecordBatch>> {
futures::stream::unfold(self, |state| async move { state.transition().await })
.fuse()
.boxed()
}
async fn transition(mut self) -> Option<(Result<RecordBatch>, Self)> {
loop {
if self.remaining_limit == Some(0) {
return None;
}
match self.decoder.try_decode() {
Ok(DecodeResult::NeedsData(ranges)) => {
let data = self
.reader
.get_byte_ranges(ranges.clone())
.await
.map_err(DataFusionError::from);
match data {
Ok(data) => {
if let Err(e) = self.decoder.push_ranges(ranges, data) {
return Some((Err(DataFusionError::from(e)), self));
}
}
Err(e) => return Some((Err(e), self)),
}
}
Ok(DecodeResult::Data(batch)) => {
let batch = if let Some(remaining_limit) = self.remaining_limit {
if batch.num_rows() > remaining_limit {
self.remaining_limit = Some(0);
batch.slice(0, remaining_limit)
} else {
self.remaining_limit =
Some(remaining_limit - batch.num_rows());
batch
}
} else {
batch
};
let mut timer = self.baseline_metrics.elapsed_compute().timer();
self.copy_arrow_reader_metrics();
let result = self.project_batch(&batch);
timer.stop();
drop(timer);
return Some((result, self));
}
Ok(DecodeResult::Finished) => {
if let Some(next) = self.pending_decoders.pop_front() {
self.decoder = next;
continue;
}
return None;
}
Err(e) => {
return Some((Err(DataFusionError::from(e)), self));
}
}
}
}
fn copy_arrow_reader_metrics(&self) {
if let Some(v) = self.arrow_reader_metrics.records_read_from_inner() {
self.predicate_cache_inner_records.set(v);
}
if let Some(v) = self.arrow_reader_metrics.records_read_from_cache() {
self.predicate_cache_records.set(v);
}
}
fn project_batch(&self, batch: &RecordBatch) -> Result<RecordBatch> {
let mut batch = self.projector.project_batch(batch)?;
if self.replace_schema {
let (_stream_schema, arrays, num_rows) = batch.into_parts();
let options = RecordBatchOptions::new().with_row_count(Some(num_rows));
batch = RecordBatch::try_new_with_options(
Arc::clone(&self.output_schema),
arrays,
&options,
)?;
}
Ok(batch)
}
}