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::EmptyOptions;
24use crate::scalar_fn::ExpressionReduceNode;
25use crate::scalar_fn::ReduceNode;
26use crate::scalar_fn::ReduceNodeValidity;
27use crate::scalar_fn::ScalarFnRef;
28use crate::scalar_fn::ScalarFnVTable;
29use crate::scalar_fn::ScalarFnVTableExt;
30use crate::scalar_fn::fns::is_not_null::IsNotNull;
31use crate::stats::rewrite::falsify;
32use crate::stats::rewrite::satisfy;
33
34#[derive(Clone, Debug, PartialEq, Eq, Hash)]
42pub enum BoundExpression {
43 Scalar {
45 dtype: DType,
47 scalar_fn: ScalarFnRef,
49 children: Arc<Vec<BoundExpression>>,
54 },
55 Root {
57 dtype: DType,
59 },
60}
61
62#[derive(Clone, Debug)]
64pub struct ExactBoundExpr(pub BoundExpression);
65
66impl PartialEq for ExactBoundExpr {
67 fn eq(&self, other: &Self) -> bool {
68 match (&self.0, &other.0) {
69 (
70 BoundExpression::Root { dtype: lhs_dtype },
71 BoundExpression::Root { dtype: rhs_dtype },
72 ) => lhs_dtype == rhs_dtype,
73 (
74 BoundExpression::Scalar {
75 dtype: lhs_dtype,
76 scalar_fn: lhs_fn,
77 children: lhs_children,
78 },
79 BoundExpression::Scalar {
80 dtype: rhs_dtype,
81 scalar_fn: rhs_fn,
82 children: rhs_children,
83 },
84 ) => {
85 lhs_fn == rhs_fn
86 && Arc::ptr_eq(lhs_children, rhs_children)
87 && lhs_dtype == rhs_dtype
88 }
89 _ => false,
90 }
91 }
92}
93
94impl Eq for ExactBoundExpr {}
95
96impl Hash for ExactBoundExpr {
97 fn hash<H: Hasher>(&self, state: &mut H) {
98 match &self.0 {
101 BoundExpression::Root { .. } => state.write_u8(0),
102 BoundExpression::Scalar {
103 scalar_fn,
104 children,
105 ..
106 } => {
107 state.write_u8(1);
108 scalar_fn.hash(state);
109 Arc::as_ptr(children).hash(state);
110 }
111 }
112 }
113}
114
115impl BoundExpression {
116 pub fn new_root(dtype: DType) -> Self {
118 Self::Root { dtype }
119 }
120
121 pub fn try_new(
123 scalar_fn: ScalarFnRef,
124 children: impl IntoIterator<Item = BoundExpression>,
125 ) -> VortexResult<Self> {
126 Self::try_new_vec(scalar_fn, children.into_iter().collect())
127 }
128
129 fn try_new_vec(scalar_fn: ScalarFnRef, children: Vec<BoundExpression>) -> VortexResult<Self> {
130 vortex_ensure!(
131 scalar_fn.signature().arity().matches(children.len()),
132 "Expression arity mismatch: expected {} children but got {}",
133 scalar_fn.signature().arity(),
134 children.len()
135 );
136
137 let arg_dtypes = children
138 .iter()
139 .map(|child| child.dtype().clone())
140 .collect_vec();
141 let dtype = scalar_fn.return_dtype(&arg_dtypes)?;
142
143 Ok(Self::Scalar {
144 dtype,
145 scalar_fn,
146 children: children.into(),
147 })
148 }
149
150 pub fn with_children(
152 self,
153 children: impl IntoIterator<Item = BoundExpression>,
154 ) -> VortexResult<Self> {
155 let children = Vec::from_iter(children);
156 let BoundExpression::Scalar { scalar_fn, .. } = &self else {
157 vortex_ensure!(
158 children.is_empty(),
159 "Root expression cannot have {} children",
160 children.len()
161 );
162 return Ok(self);
163 };
164
165 Self::try_new_vec(scalar_fn.clone(), children)
166 }
167
168 pub fn dtype(&self) -> &DType {
170 match self {
171 Self::Scalar { dtype, .. } | Self::Root { dtype } => dtype,
172 }
173 }
174
175 pub fn children(&self) -> &[BoundExpression] {
177 match self {
178 Self::Scalar { children, .. } => children.as_slice(),
179 Self::Root { .. } => &[],
180 }
181 }
182
183 pub fn child(&self, index: usize) -> &BoundExpression {
185 &self.children()[index]
186 }
187
188 pub fn as_scalar(&self) -> Option<&ScalarFnRef> {
190 match self {
191 Self::Scalar { scalar_fn, .. } => Some(scalar_fn),
192 Self::Root { .. } => None,
193 }
194 }
195
196 pub fn is<V: ScalarFnVTable>(&self) -> bool {
198 self.as_scalar().is_some_and(ScalarFnRef::is::<V>)
199 }
200
201 pub fn contains<V: ScalarFnVTable>(&self) -> VortexResult<bool> {
203 let mut contains = false;
204 pre_order_visit_down(self, |node| {
205 if node.is::<V>() {
206 contains = true;
207 return Ok(TraversalOrder::Stop);
208 }
209 Ok(TraversalOrder::Continue)
210 })?;
211 Ok(contains)
212 }
213
214 pub fn as_opt<V: ScalarFnVTable>(&self) -> Option<&V::Options> {
216 self.as_scalar().and_then(ScalarFnRef::as_opt::<V>)
217 }
218
219 pub fn as_<V: ScalarFnVTable>(&self) -> &V::Options {
225 self.as_opt::<V>()
226 .vortex_expect("Bound expression options type mismatch")
227 }
228
229 pub fn is_root(&self) -> bool {
231 matches!(self, Self::Root { .. })
232 }
233
234 pub fn is_root_bound_to(&self, dtype: &DType) -> bool {
238 let mut is_bound_to = true;
239 pre_order_visit_down(self, |node| {
240 if node.is_root() && node.dtype() != dtype {
241 is_bound_to = false;
242 return Ok(TraversalOrder::Stop);
243 }
244 Ok(TraversalOrder::Continue)
245 })
246 .vortex_expect("bound expression traversal cannot not fail");
247 is_bound_to
248 }
249
250 pub fn validity(&self) -> VortexResult<BoundExpression> {
256 match self {
257 Self::Root { .. } => Ok(self.clone()),
259 Self::Scalar { .. } => {
260 let node = ExpressionReduceNode::new(self);
261 Ok(match node.validity()? {
262 ReduceNodeValidity::Reduced(reduced) => reduced.into_expression(),
263 ReduceNodeValidity::Irreducible => {
266 IsNotNull.try_new_bound_expr(EmptyOptions, [self.clone()])?
267 }
268 })
269 }
270 }
271 }
272
273 pub fn falsify(&self, session: &VortexSession) -> VortexResult<Option<BoundExpression>> {
275 falsify(self, session)
276 }
277
278 pub fn satisfy(&self, session: &VortexSession) -> VortexResult<Option<BoundExpression>> {
280 satisfy(self, session)
281 }
282
283 pub fn display_tree(&self) -> impl Display {
285 DisplayTreeExpr(self)
286 }
287}
288
289impl Display for BoundExpression {
290 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
291 match self {
292 Self::Scalar { scalar_fn, .. } => scalar_fn.fmt_sql(self, f),
293 Self::Root { .. } => f.write_str("$"),
294 }
295 }
296}
297
298impl Expression {
299 pub fn bind(&self, dtype: &DType) -> VortexResult<BoundExpression> {
305 self.bind_scope(&Scope::new(dtype.clone()))
306 }
307
308 pub fn bind_scope(&self, scope: &Scope) -> VortexResult<BoundExpression> {
310 if self.is_root() {
311 return Ok(BoundExpression::new_root(scope.root().clone()));
312 }
313
314 let children: Vec<_> = self
315 .children()
316 .iter()
317 .map(|child| child.bind_scope(scope))
318 .try_collect()?;
319 let scalar_fn = self
320 .as_scalar()
321 .vortex_expect("root was handled above, so this is a scalar node");
322 BoundExpression::try_new(scalar_fn.clone(), children)
323 }
324}
325
326impl Drop for BoundExpression {
328 fn drop(&mut self) {
329 let Self::Scalar { children, .. } = self else {
330 return;
331 };
332 let Some(children) = Arc::get_mut(children) else {
333 return;
334 };
335
336 let mut to_drop = std::mem::take(children);
337 while let Some(mut child) = to_drop.pop() {
338 if let BoundExpression::Scalar { children, .. } = &mut child
339 && let Some(grandchildren) = Arc::get_mut(children)
340 {
341 to_drop.append(grandchildren);
342 }
343 }
344 }
345}
346
347#[cfg(test)]
348mod tests {
349 use vortex_error::VortexResult;
350
351 use super::*;
352 use crate::dtype::Nullability;
353 use crate::dtype::PType;
354 use crate::expr::col;
355 use crate::expr::eq;
356 use crate::expr::lit;
357 use crate::expr::root;
358 use crate::expr::test_harness::struct_dtype;
359 use crate::scalar_fn::fns::literal::Literal;
360
361 fn scope() -> Scope {
362 Scope::new(struct_dtype())
363 }
364
365 #[test]
366 fn root_binds_to_the_scope() -> VortexResult<()> {
367 let bound = root().bind_scope(&scope())?;
368 assert!(bound.is_root());
369 assert_eq!(bound.dtype(), &struct_dtype());
370 assert_eq!(bound, BoundExpression::new_root(struct_dtype()));
371 Ok(())
372 }
373
374 #[test]
375 fn every_node_carries_its_dtype() -> VortexResult<()> {
376 let expr = eq(col("a"), lit(1_i32));
377 let bound = expr.bind_scope(&scope())?;
378
379 assert_eq!(bound.dtype(), &DType::Bool(Nullability::NonNullable));
380
381 let lhs = &bound.children()[0];
382 assert_eq!(
383 lhs.dtype(),
384 &DType::Primitive(PType::I32, Nullability::NonNullable)
385 );
386 assert_eq!(lhs.children()[0].dtype(), &struct_dtype());
387 Ok(())
388 }
389
390 #[test]
391 fn bind_agrees_with_return_dtype() -> VortexResult<()> {
392 for expr in [root(), col("a"), eq(col("a"), lit(1_i32)), lit(true)] {
393 assert_eq!(
394 expr.bind(&struct_dtype())?.dtype(),
395 &expr.return_dtype(&struct_dtype())?,
396 "disagreement for {expr}"
397 );
398 }
399 Ok(())
400 }
401
402 #[test]
403 fn contains_scalar_function() -> VortexResult<()> {
404 let bound = eq(col("a"), lit(1_i32)).bind_scope(&scope())?;
405 assert!(bound.contains::<Literal>()?);
406 assert!(!root().bind_scope(&scope())?.contains::<Literal>()?);
407 Ok(())
408 }
409
410 #[test]
411 fn bound_to_checks_every_root() -> VortexResult<()> {
412 let dtype = struct_dtype();
413 let bound = eq(col("a"), col("a")).bind(&dtype)?;
414 assert!(bound.is_root_bound_to(&dtype));
415 assert!(!bound.is_root_bound_to(&DType::Bool(Nullability::NonNullable)));
416 assert!(
417 lit(true)
418 .bind(&dtype)?
419 .is_root_bound_to(&DType::Bool(Nullability::NonNullable))
420 );
421 Ok(())
422 }
423
424 #[test]
425 fn bound_display_matches_unbound() -> VortexResult<()> {
426 for expr in [root(), col("a"), eq(col("a"), lit(1_i32)), lit(true)] {
427 let bound = expr.bind_scope(&scope())?;
428 assert_eq!(bound.to_string(), expr.to_string());
429 assert_eq!(
430 bound.display_tree().to_string(),
431 expr.display_tree().to_string()
432 );
433 }
434 Ok(())
435 }
436
437 #[test]
438 fn clone_shares_children() -> VortexResult<()> {
439 let bound = eq(col("a"), lit(1_i32)).bind_scope(&scope())?;
440 let cloned = bound.clone();
441
442 let (
443 BoundExpression::Scalar { children: a, .. },
444 BoundExpression::Scalar { children: b, .. },
445 ) = (&bound, &cloned)
446 else {
447 unreachable!("eq is a scalar node")
448 };
449 assert!(Arc::ptr_eq(a, b));
450 Ok(())
451 }
452
453 #[test]
454 fn repeated_subtree_is_bound_per_occurrence() -> VortexResult<()> {
455 let shared = col("a");
456 let bound = eq(shared.clone(), shared).bind_scope(&scope())?;
457 let children = bound.children();
458 assert_eq!(children[0].dtype(), children[1].dtype());
459 Ok(())
460 }
461
462 #[test]
463 fn structural_and_exact_equality_are_distinct() -> VortexResult<()> {
464 let expr = eq(col("a"), lit(1_i32));
465 let bound = expr.bind_scope(&scope())?;
466 let independently_bound = expr.bind_scope(&scope())?;
467
468 assert_eq!(bound, independently_bound);
469 assert_eq!(ExactBoundExpr(bound.clone()), ExactBoundExpr(bound.clone()));
470 assert_ne!(ExactBoundExpr(bound), ExactBoundExpr(independently_bound));
471 Ok(())
472 }
473
474 #[test]
475 fn binding_reports_a_type_error() {
476 let expr = eq(col("a"), lit("nope"));
477 assert!(expr.bind_scope(&scope()).is_err());
478 }
479}