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