use std::borrow::Cow;
use std::fmt;
use std::ops::Range;
use vortex_array::EmptyMetadata;
use vortex_array::expr::BoundExpression;
use vortex_error::VortexResult;
use vortex_error::vortex_bail;
use vortex_mask::Mask;
use vortex_session::registry::CachedId;
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::check_child_count;
use crate::plan::physical::operators::EvalStage;
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;
#[derive(Clone, Debug)]
pub struct Eval;
#[derive(Clone, Debug)]
pub struct EvalData {
expression: BoundExpression,
}
pub type EvalPlan = Plan<Eval>;
impl EvalPlan {
pub fn try_new(expression: BoundExpression, child: PlanRef) -> VortexResult<Self> {
validate_expression_child(&expression, &child)?;
Ok(unsafe { Self::new_unchecked(expression, child) })
}
pub unsafe fn new_unchecked(expression: BoundExpression, child: PlanRef) -> Self {
PlanParts {
vtable: Eval,
dtype: expression.dtype().clone(),
row_count: child.row_count(),
children: vec![child].into(),
data: EvalData { expression },
}
.into_typed()
}
pub fn expression(&self) -> &BoundExpression {
&self.data().expression
}
pub fn child_plan(&self) -> VortexResult<PlanRef> {
self.child_required(0)
}
}
impl PlanVTable for Eval {
type PlanData = EvalData;
type Metadata = EmptyMetadata;
fn id(&self) -> PlanId {
static ID: CachedId = CachedId::new("vortex.plan.eval");
*ID
}
fn fmt(plan: &Plan<Self>, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, " expr={}", plan.expression())
}
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("Eval", children, 1)?;
let child = children
.get(0)?
.ok_or_else(|| vortex_error::vortex_err!("Eval child is absent"))?;
validate_expression_child(plan.expression(), &child)?;
if child.row_count() != plan.row_count() {
vortex_error::vortex_bail!(
"Eval child has {} rows but the plan has {}",
child.row_count(),
plan.row_count()
);
}
Ok(())
}
fn child_name(_plan: &Plan<Self>, index: usize) -> Cow<'_, str> {
if index == 0 {
Cow::Borrowed("child")
} else {
Cow::Owned(format!("child[{index}]"))
}
}
fn compile(
plan: &Plan<Self>,
rows: Range<u64>,
mask: &Mask,
compiler: &mut Compiler<'_>,
) -> VortexResult<Option<Chain>> {
let chain = compiler.compile(&plan.child_plan()?, rows, mask)?;
Ok(chain.map(|chain| chain.with(EvalStage::new(plan.expression().clone()))))
}
fn reach(
plan: &Plan<Self>,
rows: Range<u64>,
at: &Reach,
visit: &mut dyn FnMut(SegmentId, Range<u64>),
) -> VortexResult<()> {
plan.child_plan()?.reach(rows, at, visit)
}
}
fn validate_expression_child(expression: &BoundExpression, child: &PlanRef) -> VortexResult<()> {
if !expression.is_root_bound_to(child.dtype()) {
vortex_bail!(
"Eval expression is not bound to child dtype {}",
child.dtype()
);
}
Ok(())
}
#[derive(Debug)]
pub(crate) struct EvalIdentityRule;
impl PlanReduceRule<Eval> for EvalIdentityRule {
fn reduce(&self, plan: &Plan<Eval>) -> VortexResult<Option<PlanRef>> {
if plan.expression().is_root() {
Ok(Some(plan.child_plan()?))
} else {
Ok(None)
}
}
}