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}