Skip to main content

toasty_core/stmt/
expr.rs

1use crate::stmt::{ExprExists, Input};
2
3use super::{
4    Entry, EntryMut, EntryPath, ExprAllOp, ExprAnd, ExprAny, ExprAnyOp, ExprArg, ExprBetween,
5    ExprBinaryOp, ExprCast, ExprError, ExprFunc, ExprInList, ExprInSubquery, ExprIntersects,
6    ExprIsNull, ExprIsSuperset, ExprIsVariant, ExprLength, ExprLet, ExprLike, ExprList, ExprMap,
7    ExprMatch, ExprNot, ExprOr, ExprProject, ExprRecord, ExprStartsWith, ExprStmt, Node,
8    Projection, Resolve, Substitute, Type, Value, Visit, VisitMut, expr_reference::ExprReference,
9};
10use std::fmt;
11
12/// An expression node in Toasty's query AST.
13///
14/// `Expr` is the central type in the statement intermediate representation. Every
15/// filter, projection, value, and computed result in a Toasty query is
16/// represented as an `Expr` tree. The query engine compiles these trees through
17/// several phases (simplify, lower, plan, execute) before they reach a database
18/// driver.
19///
20/// # Examples
21///
22/// ```ignore
23/// use toasty_core::stmt::{Expr, Value};
24///
25/// // Constant value expressions
26/// let t = Expr::TRUE;
27/// assert!(t.is_true());
28///
29/// let n = Expr::null();
30/// assert!(n.is_value_null());
31///
32/// // From conversions
33/// let i: Expr = 42i64.into();
34/// assert!(i.is_value());
35/// ```
36#[derive(Clone, PartialEq)]
37pub enum Expr {
38    /// `lhs <op> ALL(rhs)` predicate against an array-valued operand. See [`ExprAllOp`].
39    AllOp(ExprAllOp),
40
41    /// Logical AND of multiple expressions. See [`ExprAnd`].
42    And(ExprAnd),
43
44    /// Returns `true` if any item in a collection is truthy. See [`ExprAny`].
45    Any(ExprAny),
46
47    /// `lhs <op> ANY(rhs)` predicate against an array-valued operand. See [`ExprAnyOp`].
48    AnyOp(ExprAnyOp),
49
50    /// `expr BETWEEN low AND high` inclusive range test. See [`ExprBetween`].
51    Between(ExprBetween),
52
53    /// Positional argument placeholder. See [`ExprArg`].
54    Arg(ExprArg),
55
56    /// Binary comparison or arithmetic operation. See [`ExprBinaryOp`].
57    BinaryOp(ExprBinaryOp),
58
59    /// Type cast. See [`ExprCast`].
60    Cast(ExprCast),
61
62    /// Instructs the database to use its default value for a column. Useful for
63    /// auto-increment fields and other columns with server-side defaults.
64    Default,
65
66    /// An error expression that fails evaluation with a message. See [`ExprError`].
67    Error(ExprError),
68
69    /// `[NOT] EXISTS(SELECT ...)` check. See [`ExprExists`].
70    Exists(ExprExists),
71
72    /// Aggregate or scalar function call. See [`ExprFunc`].
73    Func(ExprFunc),
74
75    /// An **unresolved** reference to a name (e.g. a column name in a DDL
76    /// context).
77    ///
78    /// Unlike [`Expr::Reference`] / [`ExprReference`], which hold **resolved**
79    /// index-based references into the schema, `Ident` carries only the raw
80    /// name string. It is used in contexts where schema resolution is not
81    /// applicable, such as CHECK constraints in CREATE TABLE statements.
82    Ident(String),
83
84    /// `expr IN (list)` membership test. See [`ExprInList`].
85    InList(ExprInList),
86
87    /// `expr IN (SELECT ...)` membership test. See [`ExprInSubquery`].
88    InSubquery(ExprInSubquery),
89
90    /// Boolean: two array operands share at least one element
91    /// (PostgreSQL `&&`). See [`ExprIntersects`].
92    Intersects(ExprIntersects),
93
94    /// `IS [NOT] NULL` check. Separate from binary operators because of
95    /// three-valued logic semantics in SQL. See [`ExprIsNull`].
96    IsNull(ExprIsNull),
97
98    /// Boolean: an array operand contains every element of another
99    /// (PostgreSQL `@>`). See [`ExprIsSuperset`].
100    IsSuperset(ExprIsSuperset),
101
102    /// Tests whether a value is a specific enum variant. See [`ExprIsVariant`].
103    IsVariant(ExprIsVariant),
104
105    /// Integer: the cardinality of an array (PostgreSQL `cardinality(expr)`).
106    /// See [`ExprLength`].
107    Length(ExprLength),
108
109    /// Scoped binding expression (transient -- inlined before planning).
110    /// See [`ExprLet`].
111    Let(ExprLet),
112
113    /// SQL `LIKE` pattern match: `expr LIKE pattern`. See [`ExprLike`].
114    Like(ExprLike),
115
116    /// Applies a transformation to each item in a collection. See [`ExprMap`].
117    Map(ExprMap),
118
119    /// Pattern-match dispatching on a subject. See [`ExprMatch`].
120    Match(ExprMatch),
121
122    /// Boolean negation. See [`ExprNot`].
123    Not(ExprNot),
124
125    /// Logical OR of multiple expressions. See [`ExprOr`].
126    Or(ExprOr),
127
128    /// Field projection from a composite value. See [`ExprProject`].
129    Project(ExprProject),
130
131    /// Fixed-size heterogeneous tuple of expressions. See [`ExprRecord`].
132    Record(ExprRecord),
133
134    // TODO: delete this
135    /// Reference to a field, column, or model in the current or an outer query
136    /// scope. See [`ExprReference`].
137    Reference(ExprReference),
138
139    /// Ordered, homogeneous collection of expressions. See [`ExprList`].
140    List(ExprList),
141
142    /// String prefix match: `starts_with(expr, prefix)`. See [`ExprStartsWith`].
143    StartsWith(ExprStartsWith),
144
145    /// Embedded sub-statement (e.g., a subquery). See [`ExprStmt`].
146    Stmt(ExprStmt),
147
148    /// Constant value. See [`Value`].
149    Value(Value),
150}
151
152impl Expr {
153    /// The boolean `true` constant expression.
154    pub const TRUE: Expr = Expr::Value(Value::Bool(true));
155
156    /// The boolean `false` constant expression.
157    pub const FALSE: Expr = Expr::Value(Value::Bool(false));
158
159    /// Alias for [`Expr::Default`] as a constant.
160    pub const DEFAULT: Expr = Expr::Default;
161
162    /// Creates a null value expression.
163    pub fn null() -> Self {
164        Self::Value(Value::Null)
165    }
166
167    /// Is a value that evaluates to null
168    pub fn is_value_null(&self) -> bool {
169        matches!(self, Self::Value(Value::Null))
170    }
171
172    /// Returns true if the expression is the `true` boolean expression
173    pub fn is_true(&self) -> bool {
174        matches!(self, Self::Value(Value::Bool(true)))
175    }
176
177    /// Returns `true` if the expression is the `false` boolean expression
178    pub fn is_false(&self) -> bool {
179        matches!(self, Self::Value(Value::Bool(false)))
180    }
181
182    /// Returns `true` if the expression can never evaluate to `true`.
183    ///
184    /// In SQL's three-valued logic, both `false` and `null` are unsatisfiable:
185    /// a filter producing either value will never match any rows.
186    pub fn is_unsatisfiable(&self) -> bool {
187        self.is_false() || self.is_value_null()
188    }
189
190    /// Returns `true` if the expression is the default expression
191    pub fn is_default(&self) -> bool {
192        matches!(self, Self::Default)
193    }
194
195    /// Returns true if the expression is a constant value.
196    pub fn is_value(&self) -> bool {
197        matches!(self, Self::Value(..))
198    }
199
200    /// Returns `true` if the expression is a sub-statement.
201    pub fn is_stmt(&self) -> bool {
202        matches!(self, Self::Stmt(..))
203    }
204
205    /// Returns `true` if this expression can evaluate to a value compatible
206    /// with `ty`.
207    ///
208    /// Mirrors [`Value::is_a`]: a value expression is checked directly, record
209    /// and list expressions are checked structurally, and expressions with a
210    /// statically known result type (boolean predicates, `COUNT`, ...) check
211    /// that result type against `ty`.
212    ///
213    /// # Panics
214    ///
215    /// Panics with `todo!` on expression variants whose result type cannot be
216    /// determined without evaluation context (arguments, references, casts,
217    /// ...). Support is added as callers need it.
218    pub fn is_a(&self, resolve: &impl Resolve, ty: &Type) -> bool {
219        if let Type::Union(types) = ty {
220            return types.iter().any(|t| self.is_a(resolve, t));
221        }
222        match self {
223            Self::Value(value) => value.is_a(resolve, ty),
224            Self::Record(expr_record) => match ty {
225                Type::Record(field_tys) if expr_record.fields.len() == field_tys.len() => {
226                    expr_record
227                        .fields
228                        .iter()
229                        .zip(field_tys)
230                        .all(|(expr, ty)| expr.is_a(resolve, ty))
231                }
232                // A record expression can evaluate to a document value (a
233                // `#[document]` embed): check each field against the embedded
234                // model's layout, as `Value::is_a` does. Unresolvable in a
235                // schema-free context, in which case there is no layout to
236                // check against.
237                Type::Model(id) => match resolve.model(*id) {
238                    Some(model) => {
239                        let fields = model.fields();
240                        expr_record.fields.len() == fields.len()
241                            && expr_record
242                                .fields
243                                .iter()
244                                .zip(fields)
245                                .all(|(expr, field)| expr.is_a(resolve, field.expr_ty()))
246                    }
247                    None => true,
248                },
249                _ => false,
250            },
251            Self::List(expr_list) => match ty {
252                Type::List(item_ty) => expr_list
253                    .items
254                    .iter()
255                    .all(|item| item.is_a(resolve, item_ty)),
256                _ => false,
257            },
258            // Expressions that always evaluate to a boolean.
259            Self::And(_)
260            | Self::Or(_)
261            | Self::Not(_)
262            | Self::Any(_)
263            | Self::AnyOp(_)
264            | Self::AllOp(_)
265            | Self::Between(_)
266            | Self::Exists(_)
267            | Self::InList(_)
268            | Self::InSubquery(_)
269            | Self::Intersects(_)
270            | Self::IsNull(_)
271            | Self::IsSuperset(_)
272            | Self::IsVariant(_)
273            | Self::Like(_)
274            | Self::StartsWith(_) => ty.is_bool(),
275            Self::BinaryOp(e) if !e.op.is_arithmetic() => ty.is_bool(),
276            Self::Func(ExprFunc::Count(_)) => ty.is_u64(),
277            Self::Func(ExprFunc::LastInsertId(_)) => ty.is_i64(),
278            // A match can produce the value of any of its arms.
279            Self::Match(expr_match) => {
280                expr_match.arms.iter().any(|arm| arm.expr.is_a(resolve, ty))
281                    || expr_match.else_expr.is_a(resolve, ty)
282            }
283            _ => todo!("Expr::is_a: expr={self:#?}; ty={ty:#?}"),
284        }
285    }
286
287    /// Returns true if the expression is a binary operation
288    pub fn is_binary_op(&self) -> bool {
289        matches!(self, Self::BinaryOp(..))
290    }
291
292    /// Returns `true` if the expression is an argument placeholder.
293    pub fn is_arg(&self) -> bool {
294        matches!(self, Self::Arg(_))
295    }
296
297    /// Returns true if the expression is always non-nullable.
298    ///
299    /// This method is conservative and only returns true for expressions we can
300    /// prove are non-nullable.
301    pub fn is_always_non_nullable(&self) -> bool {
302        match self {
303            // A constant value is non-nullable if it's not null.
304            Self::Value(value) => !value.is_null(),
305            // Boolean logic expressions always evaluate to true or false.
306            Self::And(_) | Self::Or(_) | Self::Not(_) => true,
307            // ANY returns true if any item matches, always boolean.
308            Self::Any(_) => true,
309            // ANY/ALL array predicates always evaluate to true or false.
310            Self::AnyOp(_) | Self::AllOp(_) => true,
311            // BETWEEN always evaluates to true or false.
312            Self::Between(_) => true,
313            // Comparisons always evaluate to true or false.
314            Self::BinaryOp(_) => true,
315            // IS NULL checks always evaluate to true or false.
316            Self::IsNull(_) => true,
317            // Variant checks always evaluate to true or false.
318            Self::IsVariant(_) => true,
319            // EXISTS checks always evaluate to true or false.
320            Self::Exists(_) => true,
321            // IN expressions always evaluate to true or false.
322            Self::InList(_) | Self::InSubquery(_) => true,
323            // Array predicates always evaluate to true or false.
324            Self::IsSuperset(_) | Self::Intersects(_) => true,
325            // Array length is an integer — non-null when the array is non-null.
326            Self::Length(_) => true,
327            // For other expressions, we cannot prove non-nullability.
328            _ => false,
329        }
330    }
331
332    /// Consumes the expression and returns the inner [`Value`].
333    ///
334    /// # Panics
335    ///
336    /// Panics (via `todo!()`) if `self` is not an `Expr::Value`.
337    pub fn into_value(self) -> Value {
338        match self {
339            Self::Value(value) => value,
340            _ => todo!(),
341        }
342    }
343
344    /// Consumes the expression and returns the inner [`ExprStmt`].
345    ///
346    /// # Panics
347    ///
348    /// Panics (via `todo!()`) if `self` is not an `Expr::Stmt`.
349    pub fn into_stmt(self) -> ExprStmt {
350        match self {
351            Self::Stmt(stmt) => stmt,
352            _ => todo!(),
353        }
354    }
355
356    /// Returns `true` if the expression is stable
357    ///
358    /// An expression is stable if it yields the same value each time it is evaluated
359    pub fn is_stable(&self) -> bool {
360        match self {
361            // Always stable - constant values
362            Self::Value(_) => true,
363
364            // Unresolved identifiers refer to external state (e.g. a column)
365            Self::Ident(_) => false,
366
367            // Never stable - generates new values each evaluation
368            Self::Default => false,
369
370            // Error expressions are stable (they always produce the same error)
371            Self::Error(_) => true,
372
373            // Stable if all children are stable
374            Self::Record(expr_record) => expr_record.iter().all(|expr| expr.is_stable()),
375            Self::List(expr_list) => expr_list.items.iter().all(|expr| expr.is_stable()),
376            Self::Cast(expr_cast) => expr_cast.expr.is_stable(),
377            Self::StartsWith(e) => e.expr.is_stable() && e.prefix.is_stable(),
378            Self::Like(e) => e.expr.is_stable() && e.pattern.is_stable(),
379            Self::Between(expr_between) => {
380                expr_between.expr.is_stable()
381                    && expr_between.low.is_stable()
382                    && expr_between.high.is_stable()
383            }
384            Self::BinaryOp(expr_binary) => {
385                expr_binary.lhs.is_stable() && expr_binary.rhs.is_stable()
386            }
387            Self::And(expr_and) => expr_and.iter().all(|expr| expr.is_stable()),
388            Self::Any(expr_any) => expr_any.expr.is_stable(),
389            Self::AnyOp(e) => e.lhs.is_stable() && e.rhs.is_stable(),
390            Self::AllOp(e) => e.lhs.is_stable() && e.rhs.is_stable(),
391            Self::Or(expr_or) => expr_or.iter().all(|expr| expr.is_stable()),
392            Self::IsNull(expr_is_null) => expr_is_null.expr.is_stable(),
393            Self::IsVariant(expr_is_variant) => expr_is_variant.expr.is_stable(),
394            Self::Not(expr_not) => expr_not.expr.is_stable(),
395            Self::InList(expr_in_list) => {
396                expr_in_list.expr.is_stable() && expr_in_list.list.is_stable()
397            }
398            Self::Project(expr_project) => expr_project.base.is_stable(),
399            Self::Let(expr_let) => {
400                expr_let.bindings.iter().all(|b| b.is_stable()) && expr_let.body.is_stable()
401            }
402            Self::Map(expr_map) => expr_map.base.is_stable() && expr_map.map.is_stable(),
403            Self::Match(expr_match) => {
404                expr_match.subject.is_stable()
405                    && expr_match.arms.iter().all(|arm| arm.expr.is_stable())
406            }
407
408            // References and statements - stable (they reference existing data)
409            Self::Reference(_) | Self::Arg(_) => true,
410
411            // Array predicates and length — stable if all operands are stable.
412            Self::IsSuperset(e) => e.lhs.is_stable() && e.rhs.is_stable(),
413            Self::Intersects(e) => e.lhs.is_stable() && e.rhs.is_stable(),
414            Self::Length(e) => e.expr.is_stable(),
415
416            // Subqueries and functions - could be unstable
417            // For now, conservatively mark as unstable
418            Self::Stmt(_) | Self::Func(_) | Self::InSubquery(_) | Self::Exists(_) => false,
419        }
420    }
421
422    /// Returns `true` if `self` and `other` are syntactically identical **and**
423    /// both sides are stable.
424    ///
425    /// This is the soundness-preserving comparison used by simplification
426    /// rules that rewrite on the assumption that two equal sub-expressions
427    /// produce the same value (idempotent, absorption, complement,
428    /// range-to-equality, OR-to-IN, factoring, variant tautology).
429    ///
430    /// Syntactic identity alone is not enough: `LAST_INSERT_ID() =
431    /// LAST_INSERT_ID()` is two independent evaluations and may yield
432    /// different values, so rewriting `a AND a` to `a` would be unsound when
433    /// `a` is non-deterministic. Gating on [`Self::is_stable`] excludes any
434    /// sub-expression whose value may change across evaluations.
435    pub fn is_equivalent_to(&self, other: &Self) -> bool {
436        self == other && self.is_stable()
437    }
438
439    /// Returns `true` if the expression is a constant expression.
440    ///
441    /// A constant expression is one that does not reference any external data.
442    /// This means it contains no `Reference`, `Stmt`, or `Arg` expressions that
443    /// reference external inputs.
444    ///
445    /// `Arg` expressions inside `Map` bodies *with `nesting` less than the current
446    /// map depth* are local bindings (bound to the mapped element), not external
447    /// inputs, and are therefore considered const in that context.
448    pub fn is_const(&self) -> bool {
449        self.is_const_at_depth(0)
450    }
451
452    /// Inner implementation of [`is_const`] that tracks the number of enclosing
453    /// `Map` scopes. An `Arg` with `nesting < map_depth` is a local binding
454    /// introduced by one of those `Map`s and does not count as external input.
455    fn is_const_at_depth(&self, map_depth: usize) -> bool {
456        match self {
457            // Always constant
458            Self::Value(_) => true,
459
460            // Unresolved identifiers reference external data
461            Self::Ident(_) => false,
462
463            // Arg: local if nesting is within map_depth, otherwise external
464            Self::Arg(arg) => arg.nesting < map_depth,
465
466            // Error expressions are constant (no external data)
467            Self::Error(_) => true,
468
469            // Never constant - references external data
470            Self::Reference(_)
471            | Self::Stmt(_)
472            | Self::InSubquery(_)
473            | Self::Exists(_)
474            | Self::Default
475            | Self::Func(_) => false,
476
477            // Const if all children are const at the same depth
478            Self::Record(expr_record) => expr_record
479                .iter()
480                .all(|expr| expr.is_const_at_depth(map_depth)),
481            Self::List(expr_list) => expr_list
482                .items
483                .iter()
484                .all(|expr| expr.is_const_at_depth(map_depth)),
485            Self::Cast(expr_cast) => expr_cast.expr.is_const_at_depth(map_depth),
486            Self::StartsWith(e) => {
487                e.expr.is_const_at_depth(map_depth) && e.prefix.is_const_at_depth(map_depth)
488            }
489            Self::Like(e) => {
490                e.expr.is_const_at_depth(map_depth) && e.pattern.is_const_at_depth(map_depth)
491            }
492            Self::Between(expr_between) => {
493                expr_between.expr.is_const_at_depth(map_depth)
494                    && expr_between.low.is_const_at_depth(map_depth)
495                    && expr_between.high.is_const_at_depth(map_depth)
496            }
497            Self::BinaryOp(expr_binary) => {
498                expr_binary.lhs.is_const_at_depth(map_depth)
499                    && expr_binary.rhs.is_const_at_depth(map_depth)
500            }
501            Self::And(expr_and) => expr_and
502                .iter()
503                .all(|expr| expr.is_const_at_depth(map_depth)),
504            Self::Any(expr_any) => expr_any.expr.is_const_at_depth(map_depth),
505            Self::AnyOp(e) => {
506                e.lhs.is_const_at_depth(map_depth) && e.rhs.is_const_at_depth(map_depth)
507            }
508            Self::AllOp(e) => {
509                e.lhs.is_const_at_depth(map_depth) && e.rhs.is_const_at_depth(map_depth)
510            }
511            Self::Not(expr_not) => expr_not.expr.is_const_at_depth(map_depth),
512            Self::Or(expr_or) => expr_or.iter().all(|expr| expr.is_const_at_depth(map_depth)),
513            Self::IsNull(expr_is_null) => expr_is_null.expr.is_const_at_depth(map_depth),
514            Self::IsVariant(expr_is_variant) => expr_is_variant.expr.is_const_at_depth(map_depth),
515            Self::InList(expr_in_list) => {
516                expr_in_list.expr.is_const_at_depth(map_depth)
517                    && expr_in_list.list.is_const_at_depth(map_depth)
518            }
519            Self::Project(expr_project) => expr_project.base.is_const_at_depth(map_depth),
520
521            // Let: binding is checked at the current depth; the body is checked
522            // at depth+1 so that arg(nesting=0) in the body is treated as local.
523            Self::Let(expr_let) => {
524                expr_let
525                    .bindings
526                    .iter()
527                    .all(|b| b.is_const_at_depth(map_depth))
528                    && expr_let.body.is_const_at_depth(map_depth + 1)
529            }
530            // Map: base is checked at the current depth; the map body is checked
531            // at depth+1 so that arg(nesting=0) in the body is treated as local.
532            Self::Map(expr_map) => {
533                expr_map.base.is_const_at_depth(map_depth)
534                    && expr_map.map.is_const_at_depth(map_depth + 1)
535            }
536            Self::Match(expr_match) => {
537                expr_match.subject.is_const_at_depth(map_depth)
538                    && expr_match
539                        .arms
540                        .iter()
541                        .all(|arm| arm.expr.is_const_at_depth(map_depth))
542            }
543
544            // Array predicates and length: const iff all operands are const.
545            Self::IsSuperset(e) => {
546                e.lhs.is_const_at_depth(map_depth) && e.rhs.is_const_at_depth(map_depth)
547            }
548            Self::Intersects(e) => {
549                e.lhs.is_const_at_depth(map_depth) && e.rhs.is_const_at_depth(map_depth)
550            }
551            Self::Length(e) => e.expr.is_const_at_depth(map_depth),
552        }
553    }
554
555    /// Returns `true` if the expression can be evaluated.
556    ///
557    /// An expression can be evaluated if it doesn't contain references to external
558    /// data sources like subqueries or references. Args are allowed since they
559    /// represent function parameters that can be bound at evaluation time.
560    pub fn is_eval(&self) -> bool {
561        match self {
562            // Always evaluable
563            Self::Value(_) => true,
564
565            // Unresolved identifiers cannot be evaluated
566            Self::Ident(_) => false,
567
568            // Args are OK for evaluation
569            Self::Arg(_) => true,
570
571            // Error expressions are evaluable (they produce an error)
572            Self::Error(_) => true,
573
574            // Never evaluable - references external data or requires a database driver
575            Self::Default
576            | Self::Reference(_)
577            | Self::Stmt(_)
578            | Self::InSubquery(_)
579            | Self::Exists(_)
580            | Self::StartsWith(_)
581            | Self::Like(_) => false,
582
583            // Evaluable if all children are evaluable
584            Self::Record(expr_record) => expr_record.iter().all(|expr| expr.is_eval()),
585            Self::List(expr_list) => expr_list.items.iter().all(|expr| expr.is_eval()),
586            Self::Cast(expr_cast) => expr_cast.expr.is_eval(),
587            Self::Between(expr_between) => {
588                expr_between.expr.is_eval()
589                    && expr_between.low.is_eval()
590                    && expr_between.high.is_eval()
591            }
592            Self::BinaryOp(expr_binary) => expr_binary.lhs.is_eval() && expr_binary.rhs.is_eval(),
593            Self::And(expr_and) => expr_and.iter().all(|expr| expr.is_eval()),
594            Self::Any(expr_any) => expr_any.expr.is_eval(),
595            Self::AnyOp(e) => e.lhs.is_eval() && e.rhs.is_eval(),
596            Self::AllOp(e) => e.lhs.is_eval() && e.rhs.is_eval(),
597            Self::Or(expr_or) => expr_or.iter().all(|expr| expr.is_eval()),
598            Self::Not(expr_not) => expr_not.expr.is_eval(),
599            Self::IsNull(expr_is_null) => expr_is_null.expr.is_eval(),
600            Self::IsVariant(expr_is_variant) => expr_is_variant.expr.is_eval(),
601            Self::InList(expr_in_list) => {
602                expr_in_list.expr.is_eval() && expr_in_list.list.is_eval()
603            }
604            Self::Project(expr_project) => expr_project.base.is_eval(),
605            Self::Let(expr_let) => {
606                expr_let.bindings.iter().all(|b| b.is_eval()) && expr_let.body.is_eval()
607            }
608            Self::Map(expr_map) => expr_map.base.is_eval() && expr_map.map.is_eval(),
609            Self::Match(expr_match) => {
610                expr_match.subject.is_eval() && expr_match.arms.iter().all(|arm| arm.expr.is_eval())
611            }
612            Self::Func(_) => false,
613            // Array predicates and length: evaluable iff all operands are.
614            Self::IsSuperset(e) => e.lhs.is_eval() && e.rhs.is_eval(),
615            Self::Intersects(e) => e.lhs.is_eval() && e.rhs.is_eval(),
616            Self::Length(e) => e.expr.is_eval(),
617        }
618    }
619
620    /// Returns a clone of this expression with all [`Projection`] nodes
621    /// transformed by `f`.
622    pub fn map_projections(&self, f: impl FnMut(&Projection) -> Projection) -> Self {
623        struct MapProjections<T>(T);
624
625        impl<T: FnMut(&Projection) -> Projection> VisitMut for MapProjections<T> {
626            fn visit_projection_mut(&mut self, i: &mut Projection) {
627                *i = self.0(i);
628            }
629        }
630
631        let mut mapped = self.clone();
632        MapProjections(f).visit_expr_mut(&mut mapped);
633        mapped
634    }
635
636    /// Navigates into a nested record or list expression by `path` and returns
637    /// a read-only [`Entry`] reference.
638    ///
639    /// Returns `None` if the path cannot be followed: the expression is not a
640    /// record or list at the expected depth, or a step indexes past the end of
641    /// a record or list.
642    #[track_caller]
643    pub fn entry(&self, path: impl EntryPath) -> Option<Entry<'_>> {
644        let mut ret = Entry::Expr(self);
645
646        for step in path.step_iter() {
647            ret = match ret {
648                Entry::Expr(Self::Record(expr)) => Entry::Expr(expr.get(step)?),
649                Entry::Expr(Self::List(expr)) => Entry::Expr(expr.items.get(step)?),
650                Entry::Value(Value::Record(record))
651                | Entry::Expr(Self::Value(Value::Record(record))) => {
652                    Entry::Value(record.get(step)?)
653                }
654                Entry::Value(Value::List(items)) | Entry::Expr(Self::Value(Value::List(items))) => {
655                    Entry::Value(items.get(step)?)
656                }
657                _ => return None,
658            }
659        }
660
661        Some(ret)
662    }
663
664    /// Navigates into a nested record or list expression by `path` and returns
665    /// a mutable [`EntryMut`] reference.
666    ///
667    /// # Panics
668    ///
669    /// Panics if the path cannot be followed on the current expression shape.
670    #[track_caller]
671    pub fn entry_mut(&mut self, path: impl EntryPath) -> EntryMut<'_> {
672        let mut ret = EntryMut::Expr(self);
673
674        for step in path.step_iter() {
675            ret = match ret {
676                EntryMut::Expr(Self::Record(expr)) => EntryMut::Expr(&mut expr[step]),
677                EntryMut::Value(Value::Record(record))
678                | EntryMut::Expr(Self::Value(Value::Record(record))) => {
679                    EntryMut::Value(&mut record[step])
680                }
681                _ => todo!("ret={ret:#?}; step={step:#?}"),
682            }
683        }
684
685        ret
686    }
687
688    /// Takes the expression out, leaving `Expr::Value(Value::Null)` in its
689    /// place. Equivalent to `std::mem::replace(self, Expr::null())`.
690    pub fn take(&mut self) -> Self {
691        std::mem::replace(self, Self::Value(Value::Null))
692    }
693
694    /// Replaces every [`ExprArg`] in this expression tree with the
695    /// corresponding value from `input`.
696    pub fn substitute(&mut self, input: impl Input) {
697        Substitute::new(input).visit_expr_mut(self);
698    }
699}
700
701impl Node for Expr {
702    fn visit<V: Visit>(&self, mut visit: V) {
703        visit.visit_expr(self);
704    }
705
706    fn visit_mut<V: VisitMut>(&mut self, mut visit: V) {
707        visit.visit_expr_mut(self);
708    }
709}
710
711// === Conversions ===
712
713impl From<bool> for Expr {
714    fn from(value: bool) -> Self {
715        Self::Value(Value::from(value))
716    }
717}
718
719impl From<i64> for Expr {
720    fn from(value: i64) -> Self {
721        Self::Value(value.into())
722    }
723}
724
725impl From<&i64> for Expr {
726    fn from(value: &i64) -> Self {
727        Self::Value(value.into())
728    }
729}
730
731impl From<String> for Expr {
732    fn from(value: String) -> Self {
733        Self::Value(value.into())
734    }
735}
736
737impl From<&String> for Expr {
738    fn from(value: &String) -> Self {
739        Self::Value(value.into())
740    }
741}
742
743impl From<&str> for Expr {
744    fn from(value: &str) -> Self {
745        Self::Value(value.into())
746    }
747}
748
749impl From<Value> for Expr {
750    fn from(value: Value) -> Self {
751        Self::Value(value)
752    }
753}
754
755impl<E1, E2> From<(E1, E2)> for Expr
756where
757    E1: Into<Self>,
758    E2: Into<Self>,
759{
760    fn from(value: (E1, E2)) -> Self {
761        Self::Record(value.into())
762    }
763}
764
765impl fmt::Debug for Expr {
766    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
767        match self {
768            Self::AllOp(e) => e.fmt(f),
769            Self::And(e) => e.fmt(f),
770            Self::Any(e) => e.fmt(f),
771            Self::AnyOp(e) => e.fmt(f),
772            Self::Arg(e) => e.fmt(f),
773            Self::Between(e) => e.fmt(f),
774            Self::BinaryOp(e) => e.fmt(f),
775            Self::Cast(e) => e.fmt(f),
776            Self::Default => write!(f, "Default"),
777            Self::Error(e) => e.fmt(f),
778            Self::Exists(e) => e.fmt(f),
779            Self::Func(e) => e.fmt(f),
780            Self::Ident(e) => write!(f, "Ident({e:?})"),
781            Self::InList(e) => e.fmt(f),
782            Self::InSubquery(e) => e.fmt(f),
783            Self::Intersects(e) => e.fmt(f),
784            Self::IsNull(e) => e.fmt(f),
785            Self::IsSuperset(e) => e.fmt(f),
786            Self::IsVariant(e) => e.fmt(f),
787            Self::Length(e) => e.fmt(f),
788            Self::Let(e) => e.fmt(f),
789            Self::Like(e) => e.fmt(f),
790            Self::Map(e) => e.fmt(f),
791            Self::Match(e) => e.fmt(f),
792            Self::Not(e) => e.fmt(f),
793            Self::Or(e) => e.fmt(f),
794            Self::Project(e) => e.fmt(f),
795            Self::Record(e) => e.fmt(f),
796            Self::Reference(e) => e.fmt(f),
797            Self::List(e) => e.fmt(f),
798            Self::StartsWith(e) => e.fmt(f),
799            Self::Stmt(e) => e.fmt(f),
800            Self::Value(e) => e.fmt(f),
801        }
802    }
803}