1use std::fmt;
5use std::fmt::Display;
6use std::fmt::Formatter;
7use std::hash::Hash;
8use std::hash::Hasher;
9use std::sync::Arc;
10
11use itertools::Itertools;
12use vortex_error::VortexExpect;
13use vortex_error::VortexResult;
14use vortex_error::vortex_ensure;
15use vortex_session::VortexSession;
16
17use crate::dtype::DType;
18use crate::expr::Expression;
19use crate::expr::display::DisplayTreeExpr;
20use crate::expr::scope::Scope;
21use crate::expr::traversal::TraversalOrder;
22use crate::expr::traversal::pre_order_visit_down;
23use crate::scalar_fn::ScalarFnRef;
24use crate::scalar_fn::ScalarFnVTable;
25use crate::stats::rewrite::StatsRewriteCtx;
26
27#[derive(Clone, Debug, PartialEq, Eq, Hash)]
35pub enum BoundExpression {
36 Scalar {
38 dtype: DType,
40 scalar_fn: ScalarFnRef,
42 children: Arc<Vec<BoundExpression>>,
47 },
48 Root {
50 dtype: DType,
52 },
53}
54
55#[derive(Clone, Debug)]
57pub struct ExactBoundExpr(pub BoundExpression);
58
59impl PartialEq for ExactBoundExpr {
60 fn eq(&self, other: &Self) -> bool {
61 match (&self.0, &other.0) {
62 (
63 BoundExpression::Root { dtype: lhs_dtype },
64 BoundExpression::Root { dtype: rhs_dtype },
65 ) => lhs_dtype == rhs_dtype,
66 (
67 BoundExpression::Scalar {
68 dtype: lhs_dtype,
69 scalar_fn: lhs_fn,
70 children: lhs_children,
71 },
72 BoundExpression::Scalar {
73 dtype: rhs_dtype,
74 scalar_fn: rhs_fn,
75 children: rhs_children,
76 },
77 ) => {
78 lhs_fn == rhs_fn
79 && Arc::ptr_eq(lhs_children, rhs_children)
80 && lhs_dtype == rhs_dtype
81 }
82 _ => false,
83 }
84 }
85}
86
87impl Eq for ExactBoundExpr {}
88
89impl Hash for ExactBoundExpr {
90 fn hash<H: Hasher>(&self, state: &mut H) {
91 match &self.0 {
94 BoundExpression::Root { .. } => state.write_u8(0),
95 BoundExpression::Scalar {
96 scalar_fn,
97 children,
98 ..
99 } => {
100 state.write_u8(1);
101 scalar_fn.hash(state);
102 Arc::as_ptr(children).hash(state);
103 }
104 }
105 }
106}
107
108impl BoundExpression {
109 pub fn new_root(dtype: DType) -> Self {
111 Self::Root { dtype }
112 }
113
114 pub fn try_new(
116 scalar_fn: ScalarFnRef,
117 children: impl IntoIterator<Item = BoundExpression>,
118 ) -> VortexResult<Self> {
119 Self::try_new_vec(scalar_fn, children.into_iter().collect())
120 }
121
122 fn try_new_vec(scalar_fn: ScalarFnRef, children: Vec<BoundExpression>) -> VortexResult<Self> {
123 vortex_ensure!(
124 scalar_fn.signature().arity().matches(children.len()),
125 "Expression arity mismatch: expected {} children but got {}",
126 scalar_fn.signature().arity(),
127 children.len()
128 );
129
130 let arg_dtypes = children
131 .iter()
132 .map(|child| child.dtype().clone())
133 .collect_vec();
134 let dtype = scalar_fn.return_dtype(&arg_dtypes)?;
135
136 Ok(Self::Scalar {
137 dtype,
138 scalar_fn,
139 children: children.into(),
140 })
141 }
142
143 pub fn with_children(
145 self,
146 children: impl IntoIterator<Item = BoundExpression>,
147 ) -> VortexResult<Self> {
148 let children = Vec::from_iter(children);
149 let BoundExpression::Scalar { scalar_fn, .. } = &self else {
150 vortex_ensure!(
151 children.is_empty(),
152 "Root expression cannot have {} children",
153 children.len()
154 );
155 return Ok(self);
156 };
157
158 Self::try_new_vec(scalar_fn.clone(), children)
159 }
160
161 pub fn dtype(&self) -> &DType {
163 match self {
164 Self::Scalar { dtype, .. } | Self::Root { dtype } => dtype,
165 }
166 }
167
168 pub fn children(&self) -> &[BoundExpression] {
170 match self {
171 Self::Scalar { children, .. } => children.as_slice(),
172 Self::Root { .. } => &[],
173 }
174 }
175
176 pub fn child(&self, index: usize) -> &BoundExpression {
178 &self.children()[index]
179 }
180
181 pub fn as_scalar(&self) -> Option<&ScalarFnRef> {
183 match self {
184 Self::Scalar { scalar_fn, .. } => Some(scalar_fn),
185 Self::Root { .. } => None,
186 }
187 }
188
189 pub fn is<V: ScalarFnVTable>(&self) -> bool {
191 self.as_scalar().is_some_and(ScalarFnRef::is::<V>)
192 }
193
194 pub fn contains<V: ScalarFnVTable>(&self) -> VortexResult<bool> {
196 let mut contains = false;
197 pre_order_visit_down(self, |node| {
198 if node.is::<V>() {
199 contains = true;
200 return Ok(TraversalOrder::Stop);
201 }
202 Ok(TraversalOrder::Continue)
203 })?;
204 Ok(contains)
205 }
206
207 pub fn as_opt<V: ScalarFnVTable>(&self) -> Option<&V::Options> {
209 self.as_scalar().and_then(ScalarFnRef::as_opt::<V>)
210 }
211
212 pub fn as_<V: ScalarFnVTable>(&self) -> &V::Options {
218 self.as_opt::<V>()
219 .vortex_expect("Bound expression options type mismatch")
220 }
221
222 pub fn is_root(&self) -> bool {
224 matches!(self, Self::Root { .. })
225 }
226
227 pub fn is_root_bound_to(&self, dtype: &DType) -> bool {
231 let mut is_bound_to = true;
232 pre_order_visit_down(self, |node| {
233 if node.is_root() && node.dtype() != dtype {
234 is_bound_to = false;
235 return Ok(TraversalOrder::Stop);
236 }
237 Ok(TraversalOrder::Continue)
238 })
239 .vortex_expect("bound expression traversal cannot not fail");
240 is_bound_to
241 }
242
243 pub fn falsify(&self, session: &VortexSession) -> VortexResult<Option<BoundExpression>> {
245 StatsRewriteCtx::new(session).falsify(self)
246 }
247
248 pub fn satisfy(&self, session: &VortexSession) -> VortexResult<Option<BoundExpression>> {
250 StatsRewriteCtx::new(session).satisfy(self)
251 }
252
253 pub fn display_tree(&self) -> impl Display {
255 DisplayTreeExpr(self)
256 }
257}
258
259impl Display for BoundExpression {
260 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
261 match self {
262 Self::Scalar { scalar_fn, .. } => scalar_fn.fmt_sql(self, f),
263 Self::Root { .. } => f.write_str("$"),
264 }
265 }
266}
267
268impl Expression {
269 pub fn bind(&self, dtype: &DType) -> VortexResult<BoundExpression> {
275 self.bind_scope(&Scope::new(dtype.clone()))
276 }
277
278 pub fn bind_scope(&self, scope: &Scope) -> VortexResult<BoundExpression> {
280 if self.is_root() {
281 return Ok(BoundExpression::new_root(scope.root().clone()));
282 }
283
284 let children: Vec<_> = self
285 .children()
286 .iter()
287 .map(|child| child.bind_scope(scope))
288 .try_collect()?;
289 let scalar_fn = self
290 .as_scalar()
291 .vortex_expect("root was handled above, so this is a scalar node");
292 BoundExpression::try_new(scalar_fn.clone(), children)
293 }
294}
295
296impl Drop for BoundExpression {
298 fn drop(&mut self) {
299 let Self::Scalar { children, .. } = self else {
300 return;
301 };
302 let Some(children) = Arc::get_mut(children) else {
303 return;
304 };
305
306 let mut to_drop = std::mem::take(children);
307 while let Some(mut child) = to_drop.pop() {
308 if let BoundExpression::Scalar { children, .. } = &mut child
309 && let Some(grandchildren) = Arc::get_mut(children)
310 {
311 to_drop.append(grandchildren);
312 }
313 }
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use vortex_error::VortexResult;
320
321 use super::*;
322 use crate::dtype::Nullability;
323 use crate::dtype::PType;
324 use crate::expr::col;
325 use crate::expr::eq;
326 use crate::expr::lit;
327 use crate::expr::root;
328 use crate::expr::test_harness::struct_dtype;
329 use crate::scalar_fn::fns::literal::Literal;
330
331 fn scope() -> Scope {
332 Scope::new(struct_dtype())
333 }
334
335 #[test]
336 fn root_binds_to_the_scope() -> VortexResult<()> {
337 let bound = root().bind_scope(&scope())?;
338 assert!(bound.is_root());
339 assert_eq!(bound.dtype(), &struct_dtype());
340 assert_eq!(bound, BoundExpression::new_root(struct_dtype()));
341 Ok(())
342 }
343
344 #[test]
345 fn every_node_carries_its_dtype() -> VortexResult<()> {
346 let expr = eq(col("a"), lit(1_i32));
347 let bound = expr.bind_scope(&scope())?;
348
349 assert_eq!(bound.dtype(), &DType::Bool(Nullability::NonNullable));
350
351 let lhs = &bound.children()[0];
352 assert_eq!(
353 lhs.dtype(),
354 &DType::Primitive(PType::I32, Nullability::NonNullable)
355 );
356 assert_eq!(lhs.children()[0].dtype(), &struct_dtype());
357 Ok(())
358 }
359
360 #[test]
361 fn bind_agrees_with_return_dtype() -> VortexResult<()> {
362 for expr in [root(), col("a"), eq(col("a"), lit(1_i32)), lit(true)] {
363 assert_eq!(
364 expr.bind(&struct_dtype())?.dtype(),
365 &expr.return_dtype(&struct_dtype())?,
366 "disagreement for {expr}"
367 );
368 }
369 Ok(())
370 }
371
372 #[test]
373 fn contains_scalar_function() -> VortexResult<()> {
374 let bound = eq(col("a"), lit(1_i32)).bind_scope(&scope())?;
375 assert!(bound.contains::<Literal>()?);
376 assert!(!root().bind_scope(&scope())?.contains::<Literal>()?);
377 Ok(())
378 }
379
380 #[test]
381 fn bound_to_checks_every_root() -> VortexResult<()> {
382 let dtype = struct_dtype();
383 let bound = eq(col("a"), col("a")).bind(&dtype)?;
384 assert!(bound.is_root_bound_to(&dtype));
385 assert!(!bound.is_root_bound_to(&DType::Bool(Nullability::NonNullable)));
386 assert!(
387 lit(true)
388 .bind(&dtype)?
389 .is_root_bound_to(&DType::Bool(Nullability::NonNullable))
390 );
391 Ok(())
392 }
393
394 #[test]
395 fn bound_display_matches_unbound() -> VortexResult<()> {
396 for expr in [root(), col("a"), eq(col("a"), lit(1_i32)), lit(true)] {
397 let bound = expr.bind_scope(&scope())?;
398 assert_eq!(bound.to_string(), expr.to_string());
399 assert_eq!(
400 bound.display_tree().to_string(),
401 expr.display_tree().to_string()
402 );
403 }
404 Ok(())
405 }
406
407 #[test]
408 fn clone_shares_children() -> VortexResult<()> {
409 let bound = eq(col("a"), lit(1_i32)).bind_scope(&scope())?;
410 let cloned = bound.clone();
411
412 let (
413 BoundExpression::Scalar { children: a, .. },
414 BoundExpression::Scalar { children: b, .. },
415 ) = (&bound, &cloned)
416 else {
417 unreachable!("eq is a scalar node")
418 };
419 assert!(Arc::ptr_eq(a, b));
420 Ok(())
421 }
422
423 #[test]
424 fn repeated_subtree_is_bound_per_occurrence() -> VortexResult<()> {
425 let shared = col("a");
426 let bound = eq(shared.clone(), shared).bind_scope(&scope())?;
427 let children = bound.children();
428 assert_eq!(children[0].dtype(), children[1].dtype());
429 Ok(())
430 }
431
432 #[test]
433 fn structural_and_exact_equality_are_distinct() -> VortexResult<()> {
434 let expr = eq(col("a"), lit(1_i32));
435 let bound = expr.bind_scope(&scope())?;
436 let independently_bound = expr.bind_scope(&scope())?;
437
438 assert_eq!(bound, independently_bound);
439 assert_eq!(ExactBoundExpr(bound.clone()), ExactBoundExpr(bound.clone()));
440 assert_ne!(ExactBoundExpr(bound), ExactBoundExpr(independently_bound));
441 Ok(())
442 }
443
444 #[test]
445 fn binding_reports_a_type_error() {
446 let expr = eq(col("a"), lit("nope"));
447 assert!(expr.bind_scope(&scope()).is_err());
448 }
449}