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}