use std::sync::Arc;
use vortex_error::VortexResult;
use vortex_error::vortex_bail;
use vortex_error::vortex_err;
use crate::LayoutRef;
use crate::layouts::chunked::Chunked;
use crate::layouts::chunked::ChunkedLayout;
use crate::layouts::dict::Dict;
use crate::layouts::dict::DictLayout;
use crate::layouts::flat::Flat;
use crate::layouts::flat::FlatLayout;
use crate::layouts::list::ELEMENTS_CHILD_INDEX;
use crate::layouts::list::List;
use crate::layouts::list::ListLayout;
use crate::layouts::list::OFFSETS_CHILD_INDEX;
use crate::layouts::list::VALIDITY_CHILD_INDEX;
use crate::layouts::struct_::Struct;
use crate::layouts::struct_::StructLayout;
use crate::layouts::zoned::LegacyStats;
use crate::layouts::zoned::Zoned;
use crate::plan::physical::ConcatPlan;
use crate::plan::physical::FilterPlan;
use crate::plan::physical::LegacyStatsPlan;
use crate::plan::physical::ListPackPlan;
use crate::plan::physical::PackPlan;
use crate::plan::physical::PlanChildren;
use crate::plan::physical::PlanRef;
use crate::plan::physical::SegmentScanPlan;
use crate::plan::physical::TakePlan;
use crate::plan::physical::ZonedPlan;
pub fn lower(layout: &LayoutRef) -> VortexResult<PlanRef> {
if let Some(layout) = layout.as_opt::<Flat>() {
return Ok(FilterPlan::new(lower_flat(layout).into_plan()).into_plan());
}
if let Some(layout) = layout.as_opt::<Chunked>() {
return Ok(lower_chunked(layout)?.into_plan());
}
if let Some(layout) = layout.as_opt::<Struct>() {
return Ok(lower_struct(layout)?.into_plan());
}
if let Some(layout) = layout.as_opt::<Dict>() {
return Ok(lower_dict(layout)?.into_plan());
}
if let Some(layout) = layout.as_opt::<List>() {
return Ok(lower_list(layout)?.into_plan());
}
if layout.is::<Zoned>() || layout.is::<LegacyStats>() {
return lower_zoned(layout);
}
vortex_bail!(
"No physical plan implementation for layout '{}'",
layout.encoding_id()
)
}
fn lower_flat(layout: &FlatLayout) -> SegmentScanPlan {
SegmentScanPlan::new(
layout.dtype().clone(),
layout.row_count(),
layout.segment_id(),
layout.array_ctx().clone(),
layout.array_tree().cloned(),
)
}
fn lower_chunked(layout: &ChunkedLayout) -> VortexResult<ConcatPlan> {
let mut row_offsets = Vec::with_capacity(layout.nchildren());
let mut row_count = 0u64;
for index in 0..layout.nchildren() {
row_offsets.push(row_count);
row_count = row_count
.checked_add(layout.child_row_count(index))
.ok_or_else(|| vortex_err!("Chunked row count overflow"))?;
}
Ok(unsafe {
ConcatPlan::from_children_unchecked(
layout.dtype().clone(),
layout.row_count(),
row_offsets.into(),
lazy_children(layout.to_layout(), (0..layout.nchildren()).collect()),
)
})
}
fn lower_struct(layout: &StructLayout) -> VortexResult<PackPlan> {
let fields = layout.struct_fields().clone();
let mut slots = (1..=fields.nfields()).collect::<Vec<_>>();
if layout.dtype().is_nullable() {
slots.push(0);
}
Ok(unsafe {
PackPlan::from_children_unchecked(
fields,
layout.dtype().nullability(),
layout.row_count(),
lazy_children(layout.to_layout(), slots),
)
})
}
fn lower_dict(layout: &DictLayout) -> VortexResult<TakePlan> {
Ok(unsafe {
TakePlan::from_children_unchecked(
layout.dtype().clone(),
layout.row_count(),
lazy_children(layout.to_layout(), vec![1, 0]),
)
})
}
fn lower_list(layout: &ListLayout) -> VortexResult<ListPackPlan> {
let mut slots = vec![ELEMENTS_CHILD_INDEX, OFFSETS_CHILD_INDEX];
if layout.dtype().is_nullable() {
slots.push(VALIDITY_CHILD_INDEX);
}
Ok(unsafe {
ListPackPlan::from_children_unchecked(
layout.dtype().clone(),
layout.row_count(),
lazy_children(layout.to_layout(), slots),
)
})
}
fn lazy_children(layout: LayoutRef, slots: Vec<usize>) -> PlanChildren {
PlanChildren::lazy(slots.len(), move |index| {
let slot = slots
.get(index)
.copied()
.ok_or_else(|| vortex_err!("Missing plan child slot {index}"))?;
let child = layout
.slot(slot)?
.ok_or_else(|| vortex_err!("Layout child slot {slot} is absent"))?;
lower(&child)
})
}
fn lower_zoned(layout: &LayoutRef) -> VortexResult<PlanRef> {
let metadata = if let Some(layout) = layout.as_opt::<Zoned>() {
layout.data()
} else if let Some(layout) = layout.as_opt::<LegacyStats>() {
layout.data()
} else {
vortex_bail!("Zoned plan requires a zoned layout")
};
let no_stats = metadata
.stats_table_dtype()
.as_struct_fields_opt()
.is_none_or(|fields| fields.nfields() == 0);
if metadata.zone_len() == 0 || no_stats {
let data = layout
.slot(0)?
.ok_or_else(|| vortex_err!("Zoned layout has no data child"))?;
return lower(&data);
}
let children = lazy_children(Arc::clone(layout), vec![0, 1]);
let zone_len = u64::try_from(metadata.zone_len())?;
Ok(match metadata.legacy_stats() {
Some(stats) => LegacyStatsPlan::from_children(
layout.dtype().clone(),
layout.row_count(),
children,
zone_len,
Arc::clone(stats),
)
.into_plan(),
None => ZonedPlan::from_children(
layout.dtype().clone(),
layout.row_count(),
children,
zone_len,
metadata.aggregate_fns(),
)
.into_plan(),
})
}