Skip to main content

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}