vortex_array/expr/expression.rs
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::fmt;
5use std::fmt::Debug;
6use std::fmt::Display;
7use std::fmt::Formatter;
8use std::hash::Hash;
9use std::sync::Arc;
10
11use itertools::Itertools;
12use vortex_error::VortexExpect;
13use vortex_error::VortexResult;
14use vortex_error::vortex_ensure;
15
16use crate::dtype::DType;
17use crate::expr::display::DisplayTreeExpr;
18use crate::expr::traversal::TraversalOrder;
19use crate::expr::traversal::pre_order_visit_down;
20use crate::scalar_fn::ScalarFnRef;
21use crate::scalar_fn::ScalarFnVTable;
22
23/// An empty child slice, returned by [`Expression::children`] for childless variants.
24const NO_CHILDREN: &[Expression] = &[];
25
26/// A node in a Vortex expression tree.
27///
28/// Most nodes are a scalar function applied to child expressions. [`Expression::Root`] is the scope
29/// itself: a language primitive rather than a registered function, because its dtype comes from the
30/// scope rather than from children and it is not executable. A [`ScalarFnVTable`] can answer neither
31/// of those, so `Root` is a variant instead.
32#[derive(Clone, Debug, PartialEq, Eq, Hash)]
33pub enum Expression {
34 /// A scalar function applied to child expressions.
35 Scalar {
36 /// The scalar fn for this node.
37 scalar_fn: ScalarFnRef,
38 /// Any children of this expression.
39 children: Arc<Vec<Expression>>,
40 },
41 /// The full scope of the expression evaluation.
42 Root,
43}
44
45impl Expression {
46 /// Create a new expression node from a scalar_fn expression and its children.
47 pub fn try_new(
48 scalar_fn: ScalarFnRef,
49 children: impl IntoIterator<Item = Expression>,
50 ) -> VortexResult<Self> {
51 let children = Vec::from_iter(children);
52
53 vortex_ensure!(
54 scalar_fn.signature().arity().matches(children.len()),
55 "Expression arity mismatch: expected {} children but got {}",
56 scalar_fn.signature().arity(),
57 children.len()
58 );
59
60 Ok(Self::Scalar {
61 scalar_fn,
62 children: children.into(),
63 })
64 }
65
66 /// Whether this expression is the scope root.
67 pub fn is_root(&self) -> bool {
68 matches!(self, Self::Root)
69 }
70
71 /// Returns the scalar fn for this expression, or `None` if it is not a scalar node.
72 pub fn as_scalar(&self) -> Option<&ScalarFnRef> {
73 match self {
74 Self::Scalar { scalar_fn, .. } => Some(scalar_fn),
75 Self::Root => None,
76 }
77 }
78
79 /// Whether this expression's scalar fn is of the given vtable type.
80 pub fn is<V: ScalarFnVTable>(&self) -> bool {
81 self.as_scalar().is_some_and(|sf| sf.is::<V>())
82 }
83
84 /// The typed options for this expression if its scalar fn matches the given vtable type.
85 pub fn as_opt<V: ScalarFnVTable>(&self) -> Option<&V::Options> {
86 self.as_scalar().and_then(|sf| sf.as_opt::<V>())
87 }
88
89 /// The typed options for this expression.
90 ///
91 /// # Panics
92 ///
93 /// Panics if the vtable type does not match.
94 pub fn as_<V: ScalarFnVTable>(&self) -> &V::Options {
95 self.as_opt::<V>()
96 .vortex_expect("Expression options type mismatch")
97 }
98
99 /// Returns the children of this expression.
100 pub fn children(&self) -> &[Expression] {
101 match self {
102 Self::Scalar { children, .. } => children.as_slice(),
103 Self::Root => NO_CHILDREN,
104 }
105 }
106
107 /// Returns the n'th child of this expression.
108 pub fn child(&self, n: usize) -> &Expression {
109 &self.children()[n]
110 }
111
112 /// Replace the children of this expression with the provided new children.
113 pub fn with_children(
114 self,
115 children: impl IntoIterator<Item = Expression>,
116 ) -> VortexResult<Self> {
117 let children = Vec::from_iter(children);
118 match &self {
119 Self::Root => {
120 vortex_ensure!(
121 children.is_empty(),
122 "Expression arity mismatch: root expects 0 children but got {}",
123 children.len()
124 );
125 Ok(Self::Root)
126 }
127 Self::Scalar { scalar_fn, .. } => {
128 vortex_ensure!(
129 scalar_fn.signature().arity().matches(children.len()),
130 "Expression arity mismatch: expected {} children but got {}",
131 scalar_fn.signature().arity(),
132 children.len()
133 );
134 Ok(Self::Scalar {
135 scalar_fn: scalar_fn.clone(),
136 children: children.into(),
137 })
138 }
139 }
140 }
141
142 /// Computes the return dtype of this expression given the input dtype.
143 pub fn return_dtype(&self, scope: &DType) -> VortexResult<DType> {
144 match self {
145 Self::Root => Ok(scope.clone()),
146 Self::Scalar {
147 scalar_fn,
148 children,
149 } => {
150 let dtypes: Vec<_> = children
151 .iter()
152 .map(|c| c.return_dtype(scope))
153 .try_collect()?;
154 scalar_fn.return_dtype(&dtypes)
155 }
156 }
157 }
158
159 /// Returns a new expression representing the validity mask output of this expression.
160 ///
161 /// The returned expression evaluates to a non-nullable boolean array.
162 pub fn validity(&self) -> VortexResult<Expression> {
163 match self {
164 // The scope is exactly as valid as itself.
165 Self::Root => Ok(Self::Root),
166 Self::Scalar { scalar_fn, .. } => scalar_fn.validity(self),
167 }
168 }
169
170 /// Format the expression as a compact string.
171 ///
172 /// Since this is a recursive formatter, it is exposed on the public Expression type.
173 /// See fmt_data that is only implemented on the vtable trait.
174 pub fn fmt_sql(&self, f: &mut Formatter<'_>) -> fmt::Result {
175 match self {
176 Self::Root => write!(f, "$"),
177 Self::Scalar { scalar_fn, .. } => scalar_fn.fmt_sql(self, f),
178 }
179 }
180
181 /// Display the expression as a formatted tree structure.
182 ///
183 /// This provides a hierarchical view of the expression that shows the relationships
184 /// between parent and child expressions, making complex nested expressions easier
185 /// to understand and debug.
186 ///
187 /// # Example
188 ///
189 /// ```rust
190 /// # use vortex_array::dtype::{DType, Nullability, PType};
191 /// # use vortex_array::scalar_fn::fns::like::{Like, LikeOptions};
192 /// # use vortex_array::scalar_fn::ScalarFnVTableExt;
193 /// # use vortex_array::expr::{and, cast, eq, get_item, gt, lit, not, root, select};
194 /// // Build a complex nested expression
195 /// let complex_expr = select(
196 /// ["result"],
197 /// and(
198 /// not(eq(get_item("status", root()), lit("inactive"))),
199 /// and(
200 /// Like.new_expr(LikeOptions::default(), [get_item("name", root()), lit("%admin%")]),
201 /// gt(
202 /// cast(get_item("score", root()), DType::Primitive(PType::F64, Nullability::NonNullable)),
203 /// lit(75.0)
204 /// )
205 /// )
206 /// )
207 /// );
208 ///
209 /// println!("{}", complex_expr.display_tree());
210 /// ```
211 ///
212 /// This produces output like:
213 ///
214 /// ```text
215 /// Select(include): {result}
216 /// └── Binary(and)
217 /// ├── lhs: Not
218 /// │ └── Binary(=)
219 /// │ ├── lhs: GetItem(status)
220 /// │ │ └── Root
221 /// │ └── rhs: Literal(value: "inactive", dtype: utf8)
222 /// └── rhs: Binary(and)
223 /// ├── lhs: Like
224 /// │ ├── child: GetItem(name)
225 /// │ │ └── Root
226 /// │ └── pattern: Literal(value: "%admin%", dtype: utf8)
227 /// └── rhs: Binary(>)
228 /// ├── lhs: Cast(target: f64)
229 /// │ └── GetItem(score)
230 /// │ └── Root
231 /// └── rhs: Literal(value: 75f64, dtype: f64)
232 /// ```
233 pub fn display_tree(&self) -> impl Display {
234 DisplayTreeExpr(self)
235 }
236
237 /// Returns true if this expression contains expression E inside.
238 ///
239 /// # Example
240 ///
241 /// ```rust
242 /// # use vortex_array::scalar_fn::fns::literal::Literal;
243 /// # use vortex_array::expr::{eq, lit, root};
244 /// let expression = &eq(root(), lit(3u64));
245 /// assert!(expression.contains::<Literal>().unwrap());
246 /// let expression = root();
247 /// assert!(!expression.contains::<Literal>().unwrap());
248 /// ```
249 pub fn contains<E: ScalarFnVTable>(&self) -> VortexResult<bool> {
250 let mut contains = false;
251 pre_order_visit_down(self, |node| {
252 if node.is::<E>() {
253 contains = true;
254 return Ok(TraversalOrder::Stop);
255 }
256 Ok(TraversalOrder::Continue)
257 })?;
258 Ok(contains)
259 }
260}
261
262/// The default display implementation for expressions uses the 'SQL'-style format.
263impl Display for Expression {
264 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
265 self.fmt_sql(f)
266 }
267}
268
269/// Iterative drop for expression to avoid stack overflows.
270impl Drop for Expression {
271 fn drop(&mut self) {
272 let Self::Scalar { children, .. } = self else {
273 return;
274 };
275 let Some(children) = Arc::get_mut(children) else {
276 return;
277 };
278
279 let mut children_to_drop = std::mem::take(children);
280 while let Some(mut child) = children_to_drop.pop() {
281 if let Self::Scalar { children, .. } = &mut child
282 && let Some(expr_children) = Arc::get_mut(children)
283 {
284 children_to_drop.append(expr_children);
285 }
286 }
287 }
288}