use crate::physical_plan::sorts::cursor::{FieldArray, FieldCursor, RowCursor};
use crate::physical_plan::SendableRecordBatchStream;
use crate::physical_plan::{PhysicalExpr, PhysicalSortExpr};
use arrow::array::Array;
use arrow::datatypes::Schema;
use arrow::record_batch::RecordBatch;
use arrow::row::{RowConverter, SortField};
use datafusion_common::Result;
use datafusion_execution::memory_pool::MemoryReservation;
use futures::stream::{Fuse, StreamExt};
use std::marker::PhantomData;
use std::sync::Arc;
use std::task::{ready, Context, Poll};
pub trait PartitionedStream: std::fmt::Debug + Send {
type Output;
fn partitions(&self) -> usize;
fn poll_next(
&mut self,
cx: &mut Context<'_>,
stream_idx: usize,
) -> Poll<Option<Self::Output>>;
}
struct FusedStreams(Vec<Fuse<SendableRecordBatchStream>>);
impl std::fmt::Debug for FusedStreams {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FusedStreams")
.field("num_streams", &self.0.len())
.finish()
}
}
impl FusedStreams {
fn poll_next(
&mut self,
cx: &mut Context<'_>,
stream_idx: usize,
) -> Poll<Option<Result<RecordBatch>>> {
loop {
match ready!(self.0[stream_idx].poll_next_unpin(cx)) {
Some(Ok(b)) if b.num_rows() == 0 => continue,
r => return Poll::Ready(r),
}
}
}
}
#[derive(Debug)]
pub struct RowCursorStream {
converter: RowConverter,
column_expressions: Vec<Arc<dyn PhysicalExpr>>,
streams: FusedStreams,
reservation: MemoryReservation,
}
impl RowCursorStream {
pub fn try_new(
schema: &Schema,
expressions: &[PhysicalSortExpr],
streams: Vec<SendableRecordBatchStream>,
reservation: MemoryReservation,
) -> Result<Self> {
let sort_fields = expressions
.iter()
.map(|expr| {
let data_type = expr.expr.data_type(schema)?;
Ok(SortField::new_with_options(data_type, expr.options))
})
.collect::<Result<Vec<_>>>()?;
let streams = streams.into_iter().map(|s| s.fuse()).collect();
let converter = RowConverter::new(sort_fields)?;
Ok(Self {
converter,
reservation,
column_expressions: expressions.iter().map(|x| x.expr.clone()).collect(),
streams: FusedStreams(streams),
})
}
fn convert_batch(&mut self, batch: &RecordBatch) -> Result<RowCursor> {
let cols = self
.column_expressions
.iter()
.map(|expr| Ok(expr.evaluate(batch)?.into_array(batch.num_rows())))
.collect::<Result<Vec<_>>>()?;
let rows = self.converter.convert_columns(&cols)?;
self.reservation.try_resize(self.converter.size())?;
let mut rows_reservation = self.reservation.new_empty();
rows_reservation.try_grow(rows.size())?;
Ok(RowCursor::new(rows, rows_reservation))
}
}
impl PartitionedStream for RowCursorStream {
type Output = Result<(RowCursor, RecordBatch)>;
fn partitions(&self) -> usize {
self.streams.0.len()
}
fn poll_next(
&mut self,
cx: &mut Context<'_>,
stream_idx: usize,
) -> Poll<Option<Self::Output>> {
Poll::Ready(ready!(self.streams.poll_next(cx, stream_idx)).map(|r| {
r.and_then(|batch| {
let cursor = self.convert_batch(&batch)?;
Ok((cursor, batch))
})
}))
}
}
pub struct FieldCursorStream<T: FieldArray> {
sort: PhysicalSortExpr,
streams: FusedStreams,
phantom: PhantomData<fn(T) -> T>,
}
impl<T: FieldArray> std::fmt::Debug for FieldCursorStream<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PrimitiveCursorStream")
.field("num_streams", &self.streams)
.finish()
}
}
impl<T: FieldArray> FieldCursorStream<T> {
pub fn new(sort: PhysicalSortExpr, streams: Vec<SendableRecordBatchStream>) -> Self {
let streams = streams.into_iter().map(|s| s.fuse()).collect();
Self {
sort,
streams: FusedStreams(streams),
phantom: Default::default(),
}
}
fn convert_batch(&mut self, batch: &RecordBatch) -> Result<FieldCursor<T::Values>> {
let value = self.sort.expr.evaluate(batch)?;
let array = value.into_array(batch.num_rows());
let array = array.as_any().downcast_ref::<T>().expect("field values");
Ok(FieldCursor::new(self.sort.options, array))
}
}
impl<T: FieldArray> PartitionedStream for FieldCursorStream<T> {
type Output = Result<(FieldCursor<T::Values>, RecordBatch)>;
fn partitions(&self) -> usize {
self.streams.0.len()
}
fn poll_next(
&mut self,
cx: &mut Context<'_>,
stream_idx: usize,
) -> Poll<Option<Self::Output>> {
Poll::Ready(ready!(self.streams.poll_next(cx, stream_idx)).map(|r| {
r.and_then(|batch| {
let cursor = self.convert_batch(&batch)?;
Ok((cursor, batch))
})
}))
}
}