use vortex_array::IntoArray;
use vortex_array::arrays::ChunkedArray;
use vortex_array::dtype::DType;
use vortex_error::VortexExpect;
use vortex_error::VortexResult;
use vortex_error::vortex_bail;
use vortex_io::request::IoConsumer;
use vortex_io::request::IoRequestId;
use vortex_io::request::IoResult;
use vortex_mask::Mask;
use vortex_scan::planning::morsel::Morsel;
use vortex_scan::planning::morsel::MorselOutput;
use vortex_scan::planning::planner::Planner;
use vortex_scan::planning::planner::PlannerOutput;
use vortex_scan::planning::planner::State;
use crate::plan::physical::pipeline::Scan;
use crate::plan::physical::pipeline::Split;
use crate::plan::physical::selection::selected;
use crate::scan::execution::ScanPlans;
use crate::scan::execution::SelectedRows;
use crate::scan::execution::adaptor::AdaptorStep;
use crate::scan::execution::adaptor::Piece;
use crate::scan::execution::adaptor::ScanAdaptor;
pub(super) struct ProjectionPlanner {
work: Option<(ScanPlans, SelectedRows)>,
}
impl ProjectionPlanner {
pub(super) fn new(plans: ScanPlans, selected: SelectedRows) -> Self {
Self {
work: Some((plans, selected)),
}
}
}
impl IoConsumer for ProjectionPlanner {
fn set_io_result(&mut self, _request: IoRequestId, _result: IoResult) {}
}
impl Planner for ProjectionPlanner {
fn state(&self) -> State {
if self.work.is_some() {
State::NeedsCompute
} else {
State::Done
}
}
fn compute(&mut self) -> VortexResult<PlannerOutput> {
let Some((plans, selected)) = self.work.take() else {
return Ok(PlannerOutput::Done);
};
if selected.mask.all_false() {
return Ok(PlannerOutput::Done);
}
Ok(PlannerOutput::Morsel(
selected.scope.clone(),
Box::new(ProjectionMorsel::new(plans, selected)),
))
}
}
struct ProjectionMorsel {
plans: Option<ScanPlans>,
dtype: DType,
selected: SelectedRows,
adaptor: Option<ScanAdaptor>,
pieces: Vec<Piece>,
done: bool,
}
impl ProjectionMorsel {
fn new(plans: ScanPlans, selected: SelectedRows) -> Self {
Self {
dtype: plans.projection.dtype().clone(),
plans: Some(plans),
selected,
adaptor: None,
pieces: Vec::new(),
done: false,
}
}
fn finish(&mut self) -> VortexResult<MorselOutput> {
self.done = true;
self.pieces.sort_by_key(|piece| piece.rows.start);
let mut arrays: Vec<_> = std::mem::take(&mut self.pieces)
.into_iter()
.map(|piece| piece.array)
.filter(|array| !array.is_empty())
.collect();
let array = match arrays.len() {
0 => return Ok(MorselOutput::Done),
1 => arrays.remove(0),
_ => ChunkedArray::try_new(arrays, self.dtype.clone())?.into_array(),
};
Ok(MorselOutput::Batch(array))
}
}
impl IoConsumer for ProjectionMorsel {
fn set_io_result(&mut self, request: IoRequestId, result: IoResult) {
if let Some(adaptor) = self.adaptor.as_mut() {
adaptor.set_io_result(request, result);
}
}
}
impl Morsel for ProjectionMorsel {
fn state(&self) -> State {
match &self.adaptor {
_ if self.done => State::Done,
None => State::NeedsCompute,
Some(adaptor) => match adaptor.state() {
State::Done => State::NeedsCompute,
state => state,
},
}
}
fn compute(&mut self) -> VortexResult<MorselOutput> {
if self.done {
vortex_bail!("ProjectionMorsel: compute called after Done");
}
let Some(adaptor) = self.adaptor.as_mut() else {
let plans = self
.plans
.take()
.vortex_expect("an uncompiled projection still has its plans");
let scan = Scan::try_new(
plans.session,
selected(
plans.projection,
self.selected.scope.rows.clone(),
self.selected.mask.clone(),
),
vec![Split {
rows: self.selected.scope.rows.clone(),
mask: Mask::new_true(self.selected.mask.len()),
}],
)?
.with_row_offset(plans.row_offset)
.with_value_cache(plans.values);
self.adaptor = Some(ScanAdaptor::new(scan, plans.locations));
return Ok(MorselOutput::Continue);
};
if adaptor.state() == State::Done {
return self.finish();
}
Ok(match adaptor.compute()? {
AdaptorStep::Yield => MorselOutput::Continue,
AdaptorStep::NeedsIO(batch) => MorselOutput::NeedsIO(batch),
AdaptorStep::Piece(piece) => {
self.pieces.push(piece);
MorselOutput::Continue
}
})
}
}