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::scalar_fn::fns::root::Root;
26use crate::stats::rewrite::StatsRewriteCtx;
27
28#[derive(Clone, Debug, PartialEq, Eq, Hash)]
36pub struct BoundExpression {
37 kind: BoundKind,
38 dtype: DType,
39}
40
41#[derive(Clone, Debug, PartialEq, Eq, Hash)]
44pub enum BoundKind {
45 Scalar {
47 scalar_fn: ScalarFnRef,
49 children: Arc<Vec<BoundExpression>>,
54 },
55 Root,
57}
58
59#[derive(Clone, Debug)]
61pub struct ExactBoundExpr(pub BoundExpression);
62
63impl PartialEq for ExactBoundExpr {
64 fn eq(&self, other: &Self) -> bool {
65 match (&self.0.kind, &other.0.kind) {
66 (BoundKind::Root, BoundKind::Root) => self.0.dtype == other.0.dtype,
67 (
68 BoundKind::Scalar {
69 scalar_fn: lhs_fn,
70 children: lhs_children,
71 },
72 BoundKind::Scalar {
73 scalar_fn: rhs_fn,
74 children: rhs_children,
75 },
76 ) => {
77 lhs_fn == rhs_fn
78 && Arc::ptr_eq(lhs_children, rhs_children)
79 && self.0.dtype == other.0.dtype
80 }
81 _ => false,
82 }
83 }
84}
85
86impl Eq for ExactBoundExpr {}
87
88impl Hash for ExactBoundExpr {
89 fn hash<H: Hasher>(&self, state: &mut H) {
90 match &self.0.kind {
93 BoundKind::Root => state.write_u8(0),
94 BoundKind::Scalar {
95 scalar_fn,
96 children,
97 } => {
98 state.write_u8(1);
99 scalar_fn.hash(state);
100 Arc::as_ptr(children).hash(state);
101 }
102 }
103 }
104}
105
106impl BoundExpression {
107 pub fn new_root(dtype: DType) -> Self {
109 Self {
110 kind: BoundKind::Root,
111 dtype,
112 }
113 }
114
115 pub fn try_new(
117 scalar_fn: ScalarFnRef,
118 children: impl IntoIterator<Item = BoundExpression>,
119 ) -> VortexResult<Self> {
120 let children = Vec::from_iter(children);
121 vortex_ensure!(
122 scalar_fn.signature().arity().matches(children.len()),
123 "Expression arity mismatch: expected {} children but got {}",
124 scalar_fn.signature().arity(),
125 children.len()
126 );
127
128 let arg_dtypes = children
129 .iter()
130 .map(|child| child.dtype().clone())
131 .collect_vec();
132 let dtype = scalar_fn.return_dtype(&arg_dtypes)?;
133
134 Ok(Self {
135 kind: BoundKind::Scalar {
136 scalar_fn,
137 children: children.into(),
138 },
139 dtype,
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 BoundKind::Scalar { scalar_fn, .. } = &self.kind 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(scalar_fn.clone(), children)
159 }
160
161 pub fn dtype(&self) -> &DType {
163 &self.dtype
164 }
165
166 pub fn kind(&self) -> &BoundKind {
168 &self.kind
169 }
170
171 pub fn children(&self) -> &[BoundExpression] {
173 match &self.kind {
174 BoundKind::Scalar { children, .. } => children.as_slice(),
175 BoundKind::Root => &[],
176 }
177 }
178
179 pub fn child(&self, index: usize) -> &BoundExpression {
181 &self.children()[index]
182 }
183
184 pub fn as_scalar(&self) -> Option<&ScalarFnRef> {
186 match &self.kind {
187 BoundKind::Scalar { scalar_fn, .. } => Some(scalar_fn),
188 BoundKind::Root => None,
189 }
190 }
191
192 pub fn is<V: ScalarFnVTable>(&self) -> bool {
194 self.as_scalar().is_some_and(ScalarFnRef::is::<V>)
195 }
196
197 pub fn contains<V: ScalarFnVTable>(&self) -> VortexResult<bool> {
199 let mut contains = false;
200 pre_order_visit_down(self, |node| {
201 if node.is::<V>() {
202 contains = true;
203 return Ok(TraversalOrder::Stop);
204 }
205 Ok(TraversalOrder::Continue)
206 })?;
207 Ok(contains)
208 }
209
210 pub fn as_opt<V: ScalarFnVTable>(&self) -> Option<&V::Options> {
212 self.as_scalar().and_then(ScalarFnRef::as_opt::<V>)
213 }
214
215 pub fn as_<V: ScalarFnVTable>(&self) -> &V::Options {
221 self.as_opt::<V>()
222 .vortex_expect("Bound expression options type mismatch")
223 }
224
225 pub fn is_root(&self) -> bool {
227 matches!(self.kind, BoundKind::Root)
228 }
229
230 pub fn falsify(&self, session: &VortexSession) -> VortexResult<Option<BoundExpression>> {
232 StatsRewriteCtx::new(session).falsify(self)
233 }
234
235 pub fn satisfy(&self, session: &VortexSession) -> VortexResult<Option<BoundExpression>> {
237 StatsRewriteCtx::new(session).satisfy(self)
238 }
239
240 pub fn display_tree(&self) -> impl Display {
242 DisplayTreeExpr(self)
243 }
244}
245
246impl Display for BoundExpression {
247 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
248 match self.kind() {
249 BoundKind::Scalar { scalar_fn, .. } => scalar_fn.fmt_sql(self, f),
250 BoundKind::Root => f.write_str("$"),
251 }
252 }
253}
254
255impl Expression {
256 pub fn bind(&self, dtype: &DType) -> VortexResult<BoundExpression> {
262 self.bind_scope(&Scope::new(dtype.clone()))
263 }
264
265 pub fn bind_scope(&self, scope: &Scope) -> VortexResult<BoundExpression> {
267 if self.is::<Root>() {
268 return Ok(BoundExpression::new_root(scope.root().clone()));
269 }
270
271 let children: Vec<_> = self
272 .children()
273 .iter()
274 .map(|child| child.bind_scope(scope))
275 .try_collect()?;
276 BoundExpression::try_new(self.scalar_fn().clone(), children)
277 }
278}
279
280impl Drop for BoundExpression {
282 fn drop(&mut self) {
283 let BoundKind::Scalar { children, .. } = &mut self.kind else {
284 return;
285 };
286 let Some(children) = Arc::get_mut(children) else {
287 return;
288 };
289
290 let mut to_drop = std::mem::take(children);
291 while let Some(mut child) = to_drop.pop() {
292 if let BoundKind::Scalar { children, .. } = &mut child.kind
293 && let Some(grandchildren) = Arc::get_mut(children)
294 {
295 to_drop.append(grandchildren);
296 }
297 }
298 }
299}
300
301#[cfg(test)]
302mod tests {
303 use vortex_error::VortexResult;
304
305 use super::*;
306 use crate::dtype::Nullability;
307 use crate::dtype::PType;
308 use crate::expr::col;
309 use crate::expr::eq;
310 use crate::expr::lit;
311 use crate::expr::root;
312 use crate::expr::test_harness::struct_dtype;
313 use crate::scalar_fn::fns::literal::Literal;
314
315 fn scope() -> Scope {
316 Scope::new(struct_dtype())
317 }
318
319 #[test]
320 fn root_binds_to_the_scope() -> VortexResult<()> {
321 let bound = root().bind_scope(&scope())?;
322 assert!(bound.is_root());
323 assert_eq!(bound.dtype(), &struct_dtype());
324 assert_eq!(bound, BoundExpression::new_root(struct_dtype()));
325 Ok(())
326 }
327
328 #[test]
329 fn every_node_carries_its_dtype() -> VortexResult<()> {
330 let expr = eq(col("a"), lit(1_i32));
331 let bound = expr.bind_scope(&scope())?;
332
333 assert_eq!(bound.dtype(), &DType::Bool(Nullability::NonNullable));
334
335 let lhs = &bound.children()[0];
336 assert_eq!(
337 lhs.dtype(),
338 &DType::Primitive(PType::I32, Nullability::NonNullable)
339 );
340 assert_eq!(lhs.children()[0].dtype(), &struct_dtype());
341 Ok(())
342 }
343
344 #[test]
345 fn bind_agrees_with_return_dtype() -> VortexResult<()> {
346 for expr in [root(), col("a"), eq(col("a"), lit(1_i32)), lit(true)] {
347 assert_eq!(
348 expr.bind(&struct_dtype())?.dtype(),
349 &expr.return_dtype(&struct_dtype())?,
350 "disagreement for {expr}"
351 );
352 }
353 Ok(())
354 }
355
356 #[test]
357 fn contains_scalar_function() -> VortexResult<()> {
358 let bound = eq(col("a"), lit(1_i32)).bind_scope(&scope())?;
359 assert!(bound.contains::<Literal>()?);
360 assert!(!root().bind_scope(&scope())?.contains::<Literal>()?);
361 Ok(())
362 }
363
364 #[test]
365 fn bound_display_matches_unbound() -> VortexResult<()> {
366 for expr in [root(), col("a"), eq(col("a"), lit(1_i32)), lit(true)] {
367 let bound = expr.bind_scope(&scope())?;
368 assert_eq!(bound.to_string(), expr.to_string());
369 assert_eq!(
370 bound.display_tree().to_string(),
371 expr.display_tree().to_string()
372 );
373 }
374 Ok(())
375 }
376
377 #[test]
378 fn clone_shares_children() -> VortexResult<()> {
379 let bound = eq(col("a"), lit(1_i32)).bind_scope(&scope())?;
380 let cloned = bound.clone();
381
382 let (BoundKind::Scalar { children: a, .. }, BoundKind::Scalar { children: b, .. }) =
383 (bound.kind(), cloned.kind())
384 else {
385 unreachable!("eq is a scalar node")
386 };
387 assert!(Arc::ptr_eq(a, b));
388 Ok(())
389 }
390
391 #[test]
392 fn repeated_subtree_is_bound_per_occurrence() -> VortexResult<()> {
393 let shared = col("a");
394 let bound = eq(shared.clone(), shared).bind_scope(&scope())?;
395 let children = bound.children();
396 assert_eq!(children[0].dtype(), children[1].dtype());
397 Ok(())
398 }
399
400 #[test]
401 fn structural_and_exact_equality_are_distinct() -> VortexResult<()> {
402 let expr = eq(col("a"), lit(1_i32));
403 let bound = expr.bind_scope(&scope())?;
404 let independently_bound = expr.bind_scope(&scope())?;
405
406 assert_eq!(bound, independently_bound);
407 assert_eq!(ExactBoundExpr(bound.clone()), ExactBoundExpr(bound.clone()));
408 assert_ne!(ExactBoundExpr(bound), ExactBoundExpr(independently_bound));
409 Ok(())
410 }
411
412 #[test]
413 fn binding_reports_a_type_error() {
414 let expr = eq(col("a"), lit("nope"));
415 assert!(expr.bind_scope(&scope()).is_err());
416 }
417}