toasty_core/stmt/
expr.rs

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