vortex_layout/plan/physical/plans/
eval.rs1use std::borrow::Cow;
5use std::fmt;
6use std::ops::Range;
7
8use vortex_array::EmptyMetadata;
9use vortex_array::expr::BoundExpression;
10use vortex_error::VortexResult;
11use vortex_error::vortex_bail;
12use vortex_mask::Mask;
13use vortex_session::registry::CachedId;
14
15use crate::plan::physical::Plan;
16use crate::plan::physical::PlanChildren;
17use crate::plan::physical::PlanId;
18use crate::plan::physical::PlanParts;
19use crate::plan::physical::PlanRef;
20use crate::plan::physical::PlanVTable;
21use crate::plan::physical::check_child_count;
22use crate::plan::physical::operators::EvalStage;
23use crate::plan::physical::optimizer::PlanReduceRule;
24use crate::plan::physical::pipeline::Chain;
25use crate::plan::physical::pipeline::Compiler;
26use crate::plan::physical::pipeline::Reach;
27use crate::segments::SegmentId;
28
29#[derive(Clone, Debug)]
31pub struct Eval;
32
33#[derive(Clone, Debug)]
35pub struct EvalData {
36 expression: BoundExpression,
37}
38
39pub type EvalPlan = Plan<Eval>;
41
42impl EvalPlan {
43 pub fn try_new(expression: BoundExpression, child: PlanRef) -> VortexResult<Self> {
45 validate_expression_child(&expression, &child)?;
46
47 Ok(unsafe { Self::new_unchecked(expression, child) })
49 }
50
51 pub unsafe fn new_unchecked(expression: BoundExpression, child: PlanRef) -> Self {
57 PlanParts {
58 vtable: Eval,
59 dtype: expression.dtype().clone(),
60 row_count: child.row_count(),
61 children: vec![child].into(),
62 data: EvalData { expression },
63 }
64 .into_typed()
65 }
66
67 pub fn expression(&self) -> &BoundExpression {
69 &self.data().expression
70 }
71
72 pub fn child_plan(&self) -> VortexResult<PlanRef> {
74 self.child_required(0)
75 }
76}
77
78impl PlanVTable for Eval {
79 type PlanData = EvalData;
80 type Metadata = EmptyMetadata;
81
82 fn id(&self) -> PlanId {
83 static ID: CachedId = CachedId::new("vortex.plan.eval");
84 *ID
85 }
86
87 fn fmt(plan: &Plan<Self>, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
88 write!(formatter, " expr={}", plan.expression())
89 }
90
91 fn metadata(_plan: &Plan<Self>) -> Option<Self::Metadata> {
92 None
94 }
95
96 fn with_children(
97 plan: &Plan<Self>,
98 children: &PlanChildren,
99 _data: &mut Self::PlanData,
100 ) -> VortexResult<()> {
101 check_child_count("Eval", children, 1)?;
102 let child = children
103 .get(0)?
104 .ok_or_else(|| vortex_error::vortex_err!("Eval child is absent"))?;
105 validate_expression_child(plan.expression(), &child)?;
106 if child.row_count() != plan.row_count() {
107 vortex_error::vortex_bail!(
108 "Eval child has {} rows but the plan has {}",
109 child.row_count(),
110 plan.row_count()
111 );
112 }
113 Ok(())
114 }
115
116 fn child_name(_plan: &Plan<Self>, index: usize) -> Cow<'_, str> {
117 if index == 0 {
118 Cow::Borrowed("child")
119 } else {
120 Cow::Owned(format!("child[{index}]"))
121 }
122 }
123
124 fn compile(
125 plan: &Plan<Self>,
126 rows: Range<u64>,
127 mask: &Mask,
128 compiler: &mut Compiler<'_>,
129 ) -> VortexResult<Option<Chain>> {
130 let chain = compiler.compile(&plan.child_plan()?, rows, mask)?;
131 Ok(chain.map(|chain| chain.with(EvalStage::new(plan.expression().clone()))))
132 }
133
134 fn reach(
135 plan: &Plan<Self>,
136 rows: Range<u64>,
137 at: &Reach,
138 visit: &mut dyn FnMut(SegmentId, Range<u64>),
139 ) -> VortexResult<()> {
140 plan.child_plan()?.reach(rows, at, visit)
141 }
142}
143
144fn validate_expression_child(expression: &BoundExpression, child: &PlanRef) -> VortexResult<()> {
145 if !expression.is_root_bound_to(child.dtype()) {
146 vortex_bail!(
147 "Eval expression is not bound to child dtype {}",
148 child.dtype()
149 );
150 }
151 Ok(())
152}
153
154#[derive(Debug)]
156pub(crate) struct EvalIdentityRule;
157
158impl PlanReduceRule<Eval> for EvalIdentityRule {
159 fn reduce(&self, plan: &Plan<Eval>) -> VortexResult<Option<PlanRef>> {
160 if plan.expression().is_root() {
161 Ok(Some(plan.child_plan()?))
162 } else {
163 Ok(None)
164 }
165 }
166}