toasty_core/stmt/
expr.rs

1use crate::stmt::{ExprExists, Input};
2
3use super::{
4    Entry, EntryMut, EntryPath, ExprAnd, ExprAny, ExprArg, ExprBinaryOp, ExprCast, ExprError,
5    ExprFunc, ExprInList, ExprInSubquery, ExprIsNull, ExprIsVariant, ExprLet, ExprList, ExprMap,
6    ExprMatch, ExprNot, ExprOr, ExprProject, ExprRecord, ExprStmt, Node, Projection, Substitute,
7    Value, Visit, VisitMut, expr_reference::ExprReference,
8};
9use std::fmt;
10
11/// An expression node in Toasty's query AST.
12///
13/// `Expr` is the central type in the statement intermediate representation. Every
14/// filter, projection, value, and computed result in a Toasty query is
15/// represented as an `Expr` tree. The query engine compiles these trees through
16/// several phases (simplify, lower, plan, execute) before they reach a database
17/// driver.
18///
19/// # Examples
20///
21/// ```ignore
22/// use toasty_core::stmt::{Expr, Value};
23///
24/// // Constant value expressions
25/// let t = Expr::TRUE;
26/// assert!(t.is_true());
27///
28/// let n = Expr::null();
29/// assert!(n.is_value_null());
30///
31/// // From conversions
32/// let i: Expr = 42i64.into();
33/// assert!(i.is_value());
34/// ```
35#[derive(Clone, PartialEq)]
36pub enum Expr {
37    /// Logical AND of multiple expressions. See [`ExprAnd`].
38    And(ExprAnd),
39
40    /// Returns `true` if any item in a collection is truthy. See [`ExprAny`].
41    Any(ExprAny),
42
43    /// Positional argument placeholder. See [`ExprArg`].
44    Arg(ExprArg),
45
46    /// Binary comparison or arithmetic operation. See [`ExprBinaryOp`].
47    BinaryOp(ExprBinaryOp),
48
49    /// Type cast. See [`ExprCast`].
50    Cast(ExprCast),
51
52    /// Instructs the database to use its default value for a column. Useful for
53    /// auto-increment fields and other columns with server-side defaults.
54    Default,
55
56    /// An error expression that fails evaluation with a message. See [`ExprError`].
57    Error(ExprError),
58
59    /// `[NOT] EXISTS(SELECT ...)` check. See [`ExprExists`].
60    Exists(ExprExists),
61
62    /// Aggregate or scalar function call. See [`ExprFunc`].
63    Func(ExprFunc),
64
65    /// `expr IN (list)` membership test. See [`ExprInList`].
66    InList(ExprInList),
67
68    /// `expr IN (SELECT ...)` membership test. See [`ExprInSubquery`].
69    InSubquery(ExprInSubquery),
70
71    /// `IS [NOT] NULL` check. Separate from binary operators because of
72    /// three-valued logic semantics in SQL. See [`ExprIsNull`].
73    IsNull(ExprIsNull),
74
75    /// Tests whether a value is a specific enum variant. See [`ExprIsVariant`].
76    IsVariant(ExprIsVariant),
77
78    /// Scoped binding expression (transient -- inlined before planning).
79    /// See [`ExprLet`].
80    Let(ExprLet),
81
82    /// Applies a transformation to each item in a collection. See [`ExprMap`].
83    Map(ExprMap),
84
85    /// Pattern-match dispatching on a subject. See [`ExprMatch`].
86    Match(ExprMatch),
87
88    /// Boolean negation. See [`ExprNot`].
89    Not(ExprNot),
90
91    /// Logical OR of multiple expressions. See [`ExprOr`].
92    Or(ExprOr),
93
94    /// Field projection from a composite value. See [`ExprProject`].
95    Project(ExprProject),
96
97    /// Fixed-size heterogeneous tuple of expressions. See [`ExprRecord`].
98    Record(ExprRecord),
99
100    // TODO: delete this
101    /// Reference to a field, column, or model in the current or an outer query
102    /// scope. See [`ExprReference`].
103    Reference(ExprReference),
104
105    /// Ordered, homogeneous collection of expressions. See [`ExprList`].
106    List(ExprList),
107
108    /// Embedded sub-statement (e.g., a subquery). See [`ExprStmt`].
109    Stmt(ExprStmt),
110
111    /// Constant value. See [`Value`].
112    Value(Value),
113}
114
115impl Expr {
116    /// The boolean `true` constant expression.
117    pub const TRUE: Expr = Expr::Value(Value::Bool(true));
118
119    /// The boolean `false` constant expression.
120    pub const FALSE: Expr = Expr::Value(Value::Bool(false));
121
122    /// Alias for [`Expr::Default`] as a constant.
123    pub const DEFAULT: Expr = Expr::Default;
124
125    /// Creates a null value expression.
126    pub fn null() -> Self {
127        Self::Value(Value::Null)
128    }
129
130    /// Is a value that evaluates to null
131    pub fn is_value_null(&self) -> bool {
132        matches!(self, Self::Value(Value::Null))
133    }
134
135    /// Returns true if the expression is the `true` boolean expression
136    pub fn is_true(&self) -> bool {
137        matches!(self, Self::Value(Value::Bool(true)))
138    }
139
140    /// Returns `true` if the expression is the `false` boolean expression
141    pub fn is_false(&self) -> bool {
142        matches!(self, Self::Value(Value::Bool(false)))
143    }
144
145    /// Returns `true` if the expression can never evaluate to `true`.
146    ///
147    /// In SQL's three-valued logic, both `false` and `null` are unsatisfiable:
148    /// a filter producing either value will never match any rows.
149    pub fn is_unsatisfiable(&self) -> bool {
150        self.is_false() || self.is_value_null()
151    }
152
153    /// Returns `true` if the expression is the default expression
154    pub fn is_default(&self) -> bool {
155        matches!(self, Self::Default)
156    }
157
158    /// Returns true if the expression is a constant value.
159    pub fn is_value(&self) -> bool {
160        matches!(self, Self::Value(..))
161    }
162
163    /// Returns `true` if the expression is a sub-statement.
164    pub fn is_stmt(&self) -> bool {
165        matches!(self, Self::Stmt(..))
166    }
167
168    /// Returns true if the expression is a binary operation
169    pub fn is_binary_op(&self) -> bool {
170        matches!(self, Self::BinaryOp(..))
171    }
172
173    /// Returns `true` if the expression is an argument placeholder.
174    pub fn is_arg(&self) -> bool {
175        matches!(self, Self::Arg(_))
176    }
177
178    /// Returns true if the expression is always non-nullable.
179    ///
180    /// This method is conservative and only returns true for expressions we can
181    /// prove are non-nullable.
182    pub fn is_always_non_nullable(&self) -> bool {
183        match self {
184            // A constant value is non-nullable if it's not null.
185            Self::Value(value) => !value.is_null(),
186            // Boolean logic expressions always evaluate to true or false.
187            Self::And(_) | Self::Or(_) | Self::Not(_) => true,
188            // ANY returns true if any item matches, always boolean.
189            Self::Any(_) => true,
190            // Comparisons always evaluate to true or false.
191            Self::BinaryOp(_) => true,
192            // IS NULL checks always evaluate to true or false.
193            Self::IsNull(_) => true,
194            // Variant checks always evaluate to true or false.
195            Self::IsVariant(_) => true,
196            // EXISTS checks always evaluate to true or false.
197            Self::Exists(_) => true,
198            // IN expressions always evaluate to true or false.
199            Self::InList(_) | Self::InSubquery(_) => true,
200            // For other expressions, we cannot prove non-nullability.
201            _ => false,
202        }
203    }
204
205    /// Consumes the expression and returns the inner [`Value`].
206    ///
207    /// # Panics
208    ///
209    /// Panics (via `todo!()`) if `self` is not an `Expr::Value`.
210    pub fn into_value(self) -> Value {
211        match self {
212            Self::Value(value) => value,
213            _ => todo!(),
214        }
215    }
216
217    /// Consumes the expression and returns the inner [`ExprStmt`].
218    ///
219    /// # Panics
220    ///
221    /// Panics (via `todo!()`) if `self` is not an `Expr::Stmt`.
222    pub fn into_stmt(self) -> ExprStmt {
223        match self {
224            Self::Stmt(stmt) => stmt,
225            _ => todo!(),
226        }
227    }
228
229    /// Returns `true` if the expression is stable
230    ///
231    /// An expression is stable if it yields the same value each time it is evaluated
232    pub fn is_stable(&self) -> bool {
233        match self {
234            // Always stable - constant values
235            Self::Value(_) => true,
236
237            // Never stable - generates new values each evaluation
238            Self::Default => false,
239
240            // Error expressions are stable (they always produce the same error)
241            Self::Error(_) => true,
242
243            // Stable if all children are stable
244            Self::Record(expr_record) => expr_record.iter().all(|expr| expr.is_stable()),
245            Self::List(expr_list) => expr_list.items.iter().all(|expr| expr.is_stable()),
246            Self::Cast(expr_cast) => expr_cast.expr.is_stable(),
247            Self::BinaryOp(expr_binary) => {
248                expr_binary.lhs.is_stable() && expr_binary.rhs.is_stable()
249            }
250            Self::And(expr_and) => expr_and.iter().all(|expr| expr.is_stable()),
251            Self::Any(expr_any) => expr_any.expr.is_stable(),
252            Self::Or(expr_or) => expr_or.iter().all(|expr| expr.is_stable()),
253            Self::IsNull(expr_is_null) => expr_is_null.expr.is_stable(),
254            Self::IsVariant(expr_is_variant) => expr_is_variant.expr.is_stable(),
255            Self::Not(expr_not) => expr_not.expr.is_stable(),
256            Self::InList(expr_in_list) => {
257                expr_in_list.expr.is_stable() && expr_in_list.list.is_stable()
258            }
259            Self::Project(expr_project) => expr_project.base.is_stable(),
260            Self::Let(expr_let) => {
261                expr_let.bindings.iter().all(|b| b.is_stable()) && expr_let.body.is_stable()
262            }
263            Self::Map(expr_map) => expr_map.base.is_stable() && expr_map.map.is_stable(),
264            Self::Match(expr_match) => {
265                expr_match.subject.is_stable()
266                    && expr_match.arms.iter().all(|arm| arm.expr.is_stable())
267            }
268
269            // References and statements - stable (they reference existing data)
270            Self::Reference(_) | Self::Arg(_) => true,
271
272            // Subqueries and functions - could be unstable
273            // For now, conservatively mark as unstable
274            Self::Stmt(_) | Self::Func(_) | Self::InSubquery(_) | Self::Exists(_) => false,
275        }
276    }
277
278    /// Returns `true` if the expression is a constant expression.
279    ///
280    /// A constant expression is one that does not reference any external data.
281    /// This means it contains no `Reference`, `Stmt`, or `Arg` expressions that
282    /// reference external inputs.
283    ///
284    /// `Arg` expressions inside `Map` bodies *with `nesting` less than the current
285    /// map depth* are local bindings (bound to the mapped element), not external
286    /// inputs, and are therefore considered const in that context.
287    pub fn is_const(&self) -> bool {
288        self.is_const_at_depth(0)
289    }
290
291    /// Inner implementation of [`is_const`] that tracks the number of enclosing
292    /// `Map` scopes. An `Arg` with `nesting < map_depth` is a local binding
293    /// introduced by one of those `Map`s and does not count as external input.
294    fn is_const_at_depth(&self, map_depth: usize) -> bool {
295        match self {
296            // Always constant
297            Self::Value(_) => true,
298
299            // Arg: local if nesting is within map_depth, otherwise external
300            Self::Arg(arg) => arg.nesting < map_depth,
301
302            // Error expressions are constant (no external data)
303            Self::Error(_) => true,
304
305            // Never constant - references external data
306            Self::Reference(_)
307            | Self::Stmt(_)
308            | Self::InSubquery(_)
309            | Self::Exists(_)
310            | Self::Default
311            | Self::Func(_) => false,
312
313            // Const if all children are const at the same depth
314            Self::Record(expr_record) => expr_record
315                .iter()
316                .all(|expr| expr.is_const_at_depth(map_depth)),
317            Self::List(expr_list) => expr_list
318                .items
319                .iter()
320                .all(|expr| expr.is_const_at_depth(map_depth)),
321            Self::Cast(expr_cast) => expr_cast.expr.is_const_at_depth(map_depth),
322            Self::BinaryOp(expr_binary) => {
323                expr_binary.lhs.is_const_at_depth(map_depth)
324                    && expr_binary.rhs.is_const_at_depth(map_depth)
325            }
326            Self::And(expr_and) => expr_and
327                .iter()
328                .all(|expr| expr.is_const_at_depth(map_depth)),
329            Self::Any(expr_any) => expr_any.expr.is_const_at_depth(map_depth),
330            Self::Not(expr_not) => expr_not.expr.is_const_at_depth(map_depth),
331            Self::Or(expr_or) => expr_or.iter().all(|expr| expr.is_const_at_depth(map_depth)),
332            Self::IsNull(expr_is_null) => expr_is_null.expr.is_const_at_depth(map_depth),
333            Self::IsVariant(expr_is_variant) => expr_is_variant.expr.is_const_at_depth(map_depth),
334            Self::InList(expr_in_list) => {
335                expr_in_list.expr.is_const_at_depth(map_depth)
336                    && expr_in_list.list.is_const_at_depth(map_depth)
337            }
338            Self::Project(expr_project) => expr_project.base.is_const_at_depth(map_depth),
339
340            // Let: binding is checked at the current depth; the body is checked
341            // at depth+1 so that arg(nesting=0) in the body is treated as local.
342            Self::Let(expr_let) => {
343                expr_let
344                    .bindings
345                    .iter()
346                    .all(|b| b.is_const_at_depth(map_depth))
347                    && expr_let.body.is_const_at_depth(map_depth + 1)
348            }
349            // Map: base is checked at the current depth; the map body is checked
350            // at depth+1 so that arg(nesting=0) in the body is treated as local.
351            Self::Map(expr_map) => {
352                expr_map.base.is_const_at_depth(map_depth)
353                    && expr_map.map.is_const_at_depth(map_depth + 1)
354            }
355            Self::Match(expr_match) => {
356                expr_match.subject.is_const_at_depth(map_depth)
357                    && expr_match
358                        .arms
359                        .iter()
360                        .all(|arm| arm.expr.is_const_at_depth(map_depth))
361            }
362        }
363    }
364
365    /// Returns `true` if the expression can be evaluated.
366    ///
367    /// An expression can be evaluated if it doesn't contain references to external
368    /// data sources like subqueries or references. Args are allowed since they
369    /// represent function parameters that can be bound at evaluation time.
370    pub fn is_eval(&self) -> bool {
371        match self {
372            // Always evaluable
373            Self::Value(_) => true,
374
375            // Args are OK for evaluation
376            Self::Arg(_) => true,
377
378            // Error expressions are evaluable (they produce an error)
379            Self::Error(_) => true,
380
381            // Never evaluable - references external data
382            Self::Default
383            | Self::Reference(_)
384            | Self::Stmt(_)
385            | Self::InSubquery(_)
386            | Self::Exists(_) => false,
387
388            // Evaluable if all children are evaluable
389            Self::Record(expr_record) => expr_record.iter().all(|expr| expr.is_eval()),
390            Self::List(expr_list) => expr_list.items.iter().all(|expr| expr.is_eval()),
391            Self::Cast(expr_cast) => expr_cast.expr.is_eval(),
392            Self::BinaryOp(expr_binary) => expr_binary.lhs.is_eval() && expr_binary.rhs.is_eval(),
393            Self::And(expr_and) => expr_and.iter().all(|expr| expr.is_eval()),
394            Self::Any(expr_any) => expr_any.expr.is_eval(),
395            Self::Or(expr_or) => expr_or.iter().all(|expr| expr.is_eval()),
396            Self::Not(expr_not) => expr_not.expr.is_eval(),
397            Self::IsNull(expr_is_null) => expr_is_null.expr.is_eval(),
398            Self::IsVariant(expr_is_variant) => expr_is_variant.expr.is_eval(),
399            Self::InList(expr_in_list) => {
400                expr_in_list.expr.is_eval() && expr_in_list.list.is_eval()
401            }
402            Self::Project(expr_project) => expr_project.base.is_eval(),
403            Self::Let(expr_let) => {
404                expr_let.bindings.iter().all(|b| b.is_eval()) && expr_let.body.is_eval()
405            }
406            Self::Map(expr_map) => expr_map.base.is_eval() && expr_map.map.is_eval(),
407            Self::Match(expr_match) => {
408                expr_match.subject.is_eval() && expr_match.arms.iter().all(|arm| arm.expr.is_eval())
409            }
410            Self::Func(_) => false,
411        }
412    }
413
414    /// Returns a clone of this expression with all [`Projection`] nodes
415    /// transformed by `f`.
416    pub fn map_projections(&self, f: impl FnMut(&Projection) -> Projection) -> Self {
417        struct MapProjections<T>(T);
418
419        impl<T: FnMut(&Projection) -> Projection> VisitMut for MapProjections<T> {
420            fn visit_projection_mut(&mut self, i: &mut Projection) {
421                *i = self.0(i);
422            }
423        }
424
425        let mut mapped = self.clone();
426        MapProjections(f).visit_expr_mut(&mut mapped);
427        mapped
428    }
429
430    /// Navigates into a nested record or list expression by `path` and returns
431    /// a read-only [`Entry`] reference.
432    ///
433    /// Returns `None` if the path cannot be followed (e.g., the expression is
434    /// not a record or list at the expected depth).
435    #[track_caller]
436    pub fn entry(&self, path: impl EntryPath) -> Option<Entry<'_>> {
437        let mut ret = Entry::Expr(self);
438
439        for step in path.step_iter() {
440            ret = match ret {
441                Entry::Expr(Self::Record(expr)) => Entry::Expr(&expr[step]),
442                Entry::Expr(Self::List(expr)) => Entry::Expr(&expr.items[step]),
443                Entry::Value(Value::Record(record))
444                | Entry::Expr(Self::Value(Value::Record(record))) => Entry::Value(&record[step]),
445                Entry::Value(Value::List(items)) | Entry::Expr(Self::Value(Value::List(items))) => {
446                    Entry::Value(&items[step])
447                }
448                _ => return None,
449            }
450        }
451
452        Some(ret)
453    }
454
455    /// Navigates into a nested record or list expression by `path` and returns
456    /// a mutable [`EntryMut`] reference.
457    ///
458    /// # Panics
459    ///
460    /// Panics if the path cannot be followed on the current expression shape.
461    #[track_caller]
462    pub fn entry_mut(&mut self, path: impl EntryPath) -> EntryMut<'_> {
463        let mut ret = EntryMut::Expr(self);
464
465        for step in path.step_iter() {
466            ret = match ret {
467                EntryMut::Expr(Self::Record(expr)) => EntryMut::Expr(&mut expr[step]),
468                EntryMut::Value(Value::Record(record))
469                | EntryMut::Expr(Self::Value(Value::Record(record))) => {
470                    EntryMut::Value(&mut record[step])
471                }
472                _ => todo!("ret={ret:#?}; step={step:#?}"),
473            }
474        }
475
476        ret
477    }
478
479    /// Takes the expression out, leaving `Expr::Value(Value::Null)` in its
480    /// place. Equivalent to `std::mem::replace(self, Expr::null())`.
481    pub fn take(&mut self) -> Self {
482        std::mem::replace(self, Self::Value(Value::Null))
483    }
484
485    /// Replaces every [`ExprArg`] in this expression tree with the
486    /// corresponding value from `input`.
487    pub fn substitute(&mut self, input: impl Input) {
488        Substitute::new(input).visit_expr_mut(self);
489    }
490}
491
492impl Node for Expr {
493    fn visit<V: Visit>(&self, mut visit: V) {
494        visit.visit_expr(self);
495    }
496
497    fn visit_mut<V: VisitMut>(&mut self, mut visit: V) {
498        visit.visit_expr_mut(self);
499    }
500}
501
502// === Conversions ===
503
504impl From<bool> for Expr {
505    fn from(value: bool) -> Self {
506        Self::Value(Value::from(value))
507    }
508}
509
510impl From<i64> for Expr {
511    fn from(value: i64) -> Self {
512        Self::Value(value.into())
513    }
514}
515
516impl From<&i64> for Expr {
517    fn from(value: &i64) -> Self {
518        Self::Value(value.into())
519    }
520}
521
522impl From<String> for Expr {
523    fn from(value: String) -> Self {
524        Self::Value(value.into())
525    }
526}
527
528impl From<&String> for Expr {
529    fn from(value: &String) -> Self {
530        Self::Value(value.into())
531    }
532}
533
534impl From<&str> for Expr {
535    fn from(value: &str) -> Self {
536        Self::Value(value.into())
537    }
538}
539
540impl From<Value> for Expr {
541    fn from(value: Value) -> Self {
542        Self::Value(value)
543    }
544}
545
546impl<E1, E2> From<(E1, E2)> for Expr
547where
548    E1: Into<Self>,
549    E2: Into<Self>,
550{
551    fn from(value: (E1, E2)) -> Self {
552        Self::Record(value.into())
553    }
554}
555
556impl fmt::Debug for Expr {
557    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
558        match self {
559            Self::And(e) => e.fmt(f),
560            Self::Any(e) => e.fmt(f),
561            Self::Arg(e) => e.fmt(f),
562            Self::BinaryOp(e) => e.fmt(f),
563            Self::Cast(e) => e.fmt(f),
564            Self::Default => write!(f, "Default"),
565            Self::Error(e) => e.fmt(f),
566            Self::Exists(e) => e.fmt(f),
567            Self::Func(e) => e.fmt(f),
568            Self::InList(e) => e.fmt(f),
569            Self::InSubquery(e) => e.fmt(f),
570            Self::IsNull(e) => e.fmt(f),
571            Self::IsVariant(e) => e.fmt(f),
572            Self::Let(e) => e.fmt(f),
573            Self::Map(e) => e.fmt(f),
574            Self::Match(e) => e.fmt(f),
575            Self::Not(e) => e.fmt(f),
576            Self::Or(e) => e.fmt(f),
577            Self::Project(e) => e.fmt(f),
578            Self::Record(e) => e.fmt(f),
579            Self::Reference(e) => e.fmt(f),
580            Self::List(e) => e.fmt(f),
581            Self::Stmt(e) => e.fmt(f),
582            Self::Value(e) => e.fmt(f),
583        }
584    }
585}