use std::borrow::Cow;
use std::ops::Range;
use std::sync::Arc;
use vortex_array::EmptyMetadata;
use vortex_array::dtype::DType;
use vortex_array::expr::stats::Stat;
use vortex_error::VortexResult;
use vortex_error::vortex_bail;
use vortex_error::vortex_err;
use vortex_mask::Mask;
use vortex_session::registry::CachedId;
use crate::layouts::zoned::legacy_stats_to_aggregates;
use crate::plan::physical::EvalPlan;
use crate::plan::physical::Plan;
use crate::plan::physical::PlanChildren;
use crate::plan::physical::PlanId;
use crate::plan::physical::PlanParts;
use crate::plan::physical::PlanRef;
use crate::plan::physical::PlanVTable;
use crate::plan::physical::Zoned;
use crate::plan::physical::ZonedPlan;
use crate::plan::physical::check_child_count;
use crate::plan::physical::optimizer::PlanParentReduceRule;
use crate::plan::physical::optimizer::PlanReduceRule;
use crate::plan::physical::pipeline::Chain;
use crate::plan::physical::pipeline::Compiler;
use crate::plan::physical::pipeline::Reach;
use crate::segments::SegmentId;
const DATA: usize = 0;
const ZONES: usize = 1;
#[derive(Clone, Debug)]
pub struct LegacyStats;
#[derive(Clone, Debug)]
pub struct LegacyStatsData {
zone_len: u64,
stats: Arc<[Stat]>,
}
pub type LegacyStatsPlan = Plan<LegacyStats>;
impl LegacyStatsPlan {
pub(crate) fn from_children(
dtype: DType,
row_count: u64,
children: PlanChildren,
zone_len: u64,
stats: Arc<[Stat]>,
) -> Self {
PlanParts {
vtable: LegacyStats,
dtype,
row_count,
children,
data: LegacyStatsData { zone_len, stats },
}
.into_typed()
}
pub fn new(data: PlanRef, zones: PlanRef, zone_len: u64, stats: Arc<[Stat]>) -> Self {
let dtype = data.dtype().clone();
let row_count = data.row_count();
Self::from_children(dtype, row_count, vec![data, zones].into(), zone_len, stats)
}
pub fn zone_len(&self) -> u64 {
self.data().zone_len
}
pub fn stats(&self) -> &Arc<[Stat]> {
&self.data().stats
}
pub fn data_plan(&self) -> VortexResult<PlanRef> {
self.child_required(DATA)
}
pub fn zones_plan(&self) -> VortexResult<PlanRef> {
self.child_required(ZONES)
}
}
impl PlanVTable for LegacyStats {
type PlanData = LegacyStatsData;
type Metadata = EmptyMetadata;
fn id(&self) -> PlanId {
static ID: CachedId = CachedId::new("vortex.plan.legacy_stats");
*ID
}
fn metadata(_plan: &Plan<Self>) -> Option<Self::Metadata> {
None
}
fn with_children(
plan: &Plan<Self>,
children: &PlanChildren,
_data: &mut Self::PlanData,
) -> VortexResult<()> {
check_child_count("LegacyStats", children, 2)?;
let data = children
.get(DATA)?
.ok_or_else(|| vortex_err!("LegacyStats data child is absent"))?;
if data.dtype() != plan.dtype() || data.row_count() != plan.row_count() {
vortex_bail!("LegacyStats data child shape does not match the plan output");
}
Ok(())
}
fn child_name(_plan: &Plan<Self>, index: usize) -> Cow<'_, str> {
match index {
DATA => Cow::Borrowed("data"),
ZONES => Cow::Borrowed("zones"),
_ => Cow::Owned(format!("child[{index}]")),
}
}
fn compile(
plan: &Plan<Self>,
rows: Range<u64>,
mask: &Mask,
compiler: &mut Compiler<'_>,
) -> VortexResult<Option<Chain>> {
compiler.compile(&plan.data_plan()?, rows, mask)
}
fn reach(
plan: &Plan<Self>,
rows: Range<u64>,
at: &Reach,
visit: &mut dyn FnMut(SegmentId, Range<u64>),
) -> VortexResult<()> {
plan.data_plan()?.reach(rows, at, visit)
}
}
#[derive(Debug)]
pub(crate) struct LegacyStatsRule;
impl PlanReduceRule<LegacyStats> for LegacyStatsRule {
fn reduce(&self, plan: &Plan<LegacyStats>) -> VortexResult<Option<PlanRef>> {
let data = plan.data_plan()?;
if plan.zone_len() == 0 || data.is::<Zoned>() {
return Ok(Some(data));
}
let zones = plan.zones_plan()?;
let (aggregate_fns, conversion) =
legacy_stats_to_aggregates(plan.dtype(), zones.dtype(), plan.stats());
if aggregate_fns.is_empty() {
return Ok(Some(data));
}
let zones = EvalPlan::try_new(conversion.bind(zones.dtype())?, zones)?.into_plan();
let zoned = ZonedPlan::new(data, zones, plan.zone_len(), aggregate_fns);
Ok(Some(zoned.into_plan()))
}
}
#[derive(Debug)]
pub(crate) struct ZonedLegacyStatsRule;
impl PlanParentReduceRule<LegacyStats> for ZonedLegacyStatsRule {
type Parent = Zoned;
fn reduce_parent(
&self,
child: &LegacyStatsPlan,
parent: &ZonedPlan,
child_idx: usize,
) -> VortexResult<Option<PlanRef>> {
if child_idx != DATA || parent.is_pruning() {
return Ok(None);
}
let children = vec![child.data_plan()?, parent.zones_plan()?];
Ok(Some(parent.clone().into_plan().with_children(children)?))
}
}