toasty_core/stmt/expr.rs
1use crate::stmt::{ExprExists, ExprSet, 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, ExprVariant, 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 /// Selects a variant of an embedded enum value. See [`ExprVariant`].
167 Variant(ExprVariant),
168}
169
170impl Expr {
171 /// The boolean `true` constant expression.
172 pub const TRUE: Expr = Expr::Value(Value::Bool(true));
173
174 /// The boolean `false` constant expression.
175 pub const FALSE: Expr = Expr::Value(Value::Bool(false));
176
177 /// Alias for [`Expr::Default`] as a constant.
178 pub const DEFAULT: Expr = Expr::Default;
179
180 /// Creates a null value expression.
181 pub fn null() -> Self {
182 Self::Value(Value::Null)
183 }
184
185 /// Is a value that evaluates to null
186 pub fn is_value_null(&self) -> bool {
187 matches!(self, Self::Value(Value::Null))
188 }
189
190 /// Returns true if the expression is the `true` boolean expression
191 pub fn is_true(&self) -> bool {
192 matches!(self, Self::Value(Value::Bool(true)))
193 }
194
195 /// Returns `true` if the expression is the `false` boolean expression
196 pub fn is_false(&self) -> bool {
197 matches!(self, Self::Value(Value::Bool(false)))
198 }
199
200 /// Returns `true` if the expression can never evaluate to `true`.
201 ///
202 /// In SQL's three-valued logic, both `false` and `null` are unsatisfiable:
203 /// a filter producing either value will never match any rows.
204 pub fn is_unsatisfiable(&self) -> bool {
205 self.is_false() || self.is_value_null()
206 }
207
208 /// Returns `true` if the expression is the default expression
209 pub fn is_default(&self) -> bool {
210 matches!(self, Self::Default)
211 }
212
213 /// Returns true if the expression is a constant value.
214 pub fn is_value(&self) -> bool {
215 matches!(self, Self::Value(..))
216 }
217
218 /// Returns `true` if the expression is a sub-statement.
219 pub fn is_stmt(&self) -> bool {
220 matches!(self, Self::Stmt(..))
221 }
222
223 /// Returns `true` if this expression can evaluate to a value compatible
224 /// with `ty`.
225 ///
226 /// Mirrors [`Value::is_a`]: a value expression is checked directly, record
227 /// and list expressions are checked structurally, and expressions with a
228 /// statically known result type (boolean predicates, `COUNT`, ...) check
229 /// that result type against `ty`.
230 ///
231 /// # Panics
232 ///
233 /// Panics with `todo!` on expression variants whose result type cannot be
234 /// determined without evaluation context (arguments, references, casts,
235 /// ...). Support is added as callers need it.
236 pub fn is_a(&self, resolve: &impl Resolve, ty: &Type) -> bool {
237 if let Type::Union(types) = ty {
238 return types.iter().any(|t| self.is_a(resolve, t));
239 }
240 match self {
241 Self::Value(value) => value.is_a(resolve, ty),
242 Self::Record(expr_record) => match ty {
243 Type::Record(field_tys) if expr_record.fields.len() == field_tys.len() => {
244 expr_record
245 .fields
246 .iter()
247 .zip(field_tys)
248 .all(|(expr, ty)| expr.is_a(resolve, ty))
249 }
250 // A record expression can evaluate to a document value (a
251 // `#[document]` embed): check each field against the embedded
252 // model's layout, as `Value::is_a` does. Unresolvable in a
253 // schema-free context, in which case there is no layout to
254 // check against.
255 Type::Model(id) => match resolve.model(*id) {
256 Some(model) => {
257 let fields = model.fields();
258 expr_record.fields.len() == fields.len()
259 && expr_record
260 .fields
261 .iter()
262 .zip(fields)
263 .all(|(expr, field)| expr.is_a(resolve, field.expr_ty()))
264 }
265 None => true,
266 },
267 _ => false,
268 },
269 Self::List(expr_list) => match ty {
270 Type::List(item_ty) => expr_list
271 .items
272 .iter()
273 .all(|item| item.is_a(resolve, item_ty)),
274 _ => false,
275 },
276 // Expressions that always evaluate to a boolean.
277 Self::And(_)
278 | Self::Or(_)
279 | Self::Not(_)
280 | Self::Any(_)
281 | Self::AnyOp(_)
282 | Self::AllOp(_)
283 | Self::Between(_)
284 | Self::Exists(_)
285 | Self::InList(_)
286 | Self::InSubquery(_)
287 | Self::Intersects(_)
288 | Self::IsNull(_)
289 | Self::IsSuperset(_)
290 | Self::IsVariant(_)
291 | Self::Like(_)
292 | Self::StartsWith(_) => ty.is_bool(),
293 Self::BinaryOp(e) if !e.op.is_arithmetic() => ty.is_bool(),
294 Self::Func(ExprFunc::Count(_)) => ty.is_u64(),
295 Self::Func(ExprFunc::LastInsertId(_)) => ty.is_i64(),
296 // A match can produce the value of any of its arms.
297 Self::Match(expr_match) => {
298 expr_match.arms.iter().any(|arm| arm.expr.is_a(resolve, ty))
299 || expr_match.else_expr.is_a(resolve, ty)
300 }
301 _ => todo!("Expr::is_a: expr={self:#?}; ty={ty:#?}"),
302 }
303 }
304
305 /// Returns true if the expression is a binary operation
306 pub fn is_binary_op(&self) -> bool {
307 matches!(self, Self::BinaryOp(..))
308 }
309
310 /// Returns `true` if the expression is an argument placeholder.
311 pub fn is_arg(&self) -> bool {
312 matches!(self, Self::Arg(_))
313 }
314
315 /// Returns true if the expression is always non-nullable.
316 ///
317 /// This method is conservative and only returns true for expressions we can
318 /// prove are non-nullable.
319 pub fn is_always_non_nullable(&self) -> bool {
320 match self {
321 // A constant value is non-nullable if it's not null.
322 Self::Value(value) => !value.is_null(),
323 // Boolean logic expressions always evaluate to true or false.
324 Self::And(_) | Self::Or(_) | Self::Not(_) => true,
325 // ANY returns true if any item matches, always boolean.
326 Self::Any(_) => true,
327 // ANY/ALL array predicates always evaluate to true or false.
328 Self::AnyOp(_) | Self::AllOp(_) => true,
329 // BETWEEN always evaluates to true or false.
330 Self::Between(_) => true,
331 // Comparisons always evaluate to true or false.
332 Self::BinaryOp(_) => true,
333 // IS NULL checks always evaluate to true or false.
334 Self::IsNull(_) => true,
335 // Variant checks always evaluate to true or false.
336 Self::IsVariant(_) => true,
337 // EXISTS checks always evaluate to true or false.
338 Self::Exists(_) => true,
339 // IN expressions always evaluate to true or false.
340 Self::InList(_) | Self::InSubquery(_) => true,
341 // Array predicates always evaluate to true or false.
342 Self::IsSuperset(_) | Self::Intersects(_) => true,
343 // Array length is an integer — non-null when the array is non-null.
344 Self::Length(_) => true,
345 // For other expressions, we cannot prove non-nullability.
346 _ => false,
347 }
348 }
349
350 /// Consumes the expression and returns the inner [`Value`].
351 ///
352 /// # Panics
353 ///
354 /// Panics (via `todo!()`) if `self` is not an `Expr::Value`.
355 pub fn into_value(self) -> Value {
356 match self {
357 Self::Value(value) => value,
358 _ => todo!(),
359 }
360 }
361
362 /// Consumes the expression and returns the inner [`ExprStmt`].
363 ///
364 /// # Panics
365 ///
366 /// Panics (via `todo!()`) if `self` is not an `Expr::Stmt`.
367 pub fn into_stmt(self) -> ExprStmt {
368 match self {
369 Self::Stmt(stmt) => stmt,
370 _ => todo!(),
371 }
372 }
373
374 /// Returns `true` if the expression is stable
375 ///
376 /// An expression is stable if it yields the same value each time it is evaluated
377 pub fn is_stable(&self) -> bool {
378 match self {
379 // Always stable - constant values
380 Self::Value(_) | Self::Static(_) => true,
381
382 // Unresolved identifiers refer to external state (e.g. a column)
383 Self::Ident(_) => false,
384
385 // Never stable - generates new values each evaluation
386 Self::Default => false,
387
388 // Error expressions are stable (they always produce the same error)
389 Self::Error(_) => true,
390
391 // Stable if all children are stable
392 Self::Record(expr_record) => expr_record.iter().all(|expr| expr.is_stable()),
393 Self::List(expr_list) => expr_list.items.iter().all(|expr| expr.is_stable()),
394 Self::Cast(expr_cast) => expr_cast.expr.is_stable(),
395 Self::StartsWith(e) => e.expr.is_stable() && e.prefix.is_stable(),
396 Self::Like(e) => e.expr.is_stable() && e.pattern.is_stable(),
397 Self::Between(expr_between) => {
398 expr_between.expr.is_stable()
399 && expr_between.low.is_stable()
400 && expr_between.high.is_stable()
401 }
402 Self::BinaryOp(expr_binary) => {
403 expr_binary.lhs.is_stable() && expr_binary.rhs.is_stable()
404 }
405 Self::And(expr_and) => expr_and.iter().all(|expr| expr.is_stable()),
406 Self::Any(expr_any) => expr_any.expr.is_stable(),
407 Self::AnyOp(e) => e.lhs.is_stable() && e.rhs.is_stable(),
408 Self::AllOp(e) => e.lhs.is_stable() && e.rhs.is_stable(),
409 Self::Or(expr_or) => expr_or.iter().all(|expr| expr.is_stable()),
410 Self::IsNull(expr_is_null) => expr_is_null.expr.is_stable(),
411 Self::IsVariant(expr_is_variant) => expr_is_variant.expr.is_stable(),
412 Self::Variant(expr_variant) => expr_variant.base.is_stable(),
413 Self::Not(expr_not) => expr_not.expr.is_stable(),
414 Self::InList(expr_in_list) => {
415 expr_in_list.expr.is_stable() && expr_in_list.list.is_stable()
416 }
417 Self::Project(expr_project) => expr_project.base.is_stable(),
418 Self::Let(expr_let) => {
419 expr_let.bindings.iter().all(|b| b.is_stable()) && expr_let.body.is_stable()
420 }
421 Self::Map(expr_map) => expr_map.base.is_stable() && expr_map.map.is_stable(),
422 Self::Match(expr_match) => {
423 expr_match.subject.is_stable()
424 && expr_match.arms.iter().all(|arm| arm.expr.is_stable())
425 }
426
427 // References and statements - stable (they reference existing data)
428 Self::Reference(_) | Self::Incoming(_) | Self::Arg(_) => true,
429
430 // Array predicates and length — stable if all operands are stable.
431 Self::IsSuperset(e) => e.lhs.is_stable() && e.rhs.is_stable(),
432 Self::Intersects(e) => e.lhs.is_stable() && e.rhs.is_stable(),
433 Self::Length(e) => e.expr.is_stable(),
434
435 // Subqueries and functions - could be unstable
436 // For now, conservatively mark as unstable
437 Self::Stmt(_) | Self::Func(_) | Self::InSubquery(_) | Self::Exists(_) => false,
438 }
439 }
440
441 /// Returns `true` if `self` and `other` are syntactically identical **and**
442 /// both sides are stable.
443 ///
444 /// This is the soundness-preserving comparison used by simplification
445 /// rules that rewrite on the assumption that two equal sub-expressions
446 /// produce the same value (idempotent, absorption, complement,
447 /// range-to-equality, OR-to-IN, factoring, variant tautology).
448 ///
449 /// Syntactic identity alone is not enough: `LAST_INSERT_ID() =
450 /// LAST_INSERT_ID()` is two independent evaluations and may yield
451 /// different values, so rewriting `a AND a` to `a` would be unsound when
452 /// `a` is non-deterministic. Gating on [`Self::is_stable`] excludes any
453 /// sub-expression whose value may change across evaluations.
454 pub fn is_equivalent_to(&self, other: &Self) -> bool {
455 self == other && self.is_stable()
456 }
457
458 /// Returns `true` if the expression is a constant expression.
459 ///
460 /// A constant expression is one that does not reference any external data.
461 /// This means it contains no `Reference`, `Stmt`, or `Arg` expressions that
462 /// reference external inputs.
463 ///
464 /// `Arg` expressions inside `Map` bodies *with `nesting` less than the current
465 /// map depth* are local bindings (bound to the mapped element), not external
466 /// inputs, and are therefore considered const in that context.
467 pub fn is_const(&self) -> bool {
468 self.is_const_at_depth(0)
469 }
470
471 /// Inner implementation of [`is_const`] that tracks the number of enclosing
472 /// `Map` scopes. An `Arg` with `nesting < map_depth` is a local binding
473 /// introduced by one of those `Map`s and does not count as external input.
474 fn is_const_at_depth(&self, map_depth: usize) -> bool {
475 match self {
476 // Always constant
477 Self::Value(_) | Self::Static(_) => true,
478
479 // Unresolved identifiers reference external data
480 Self::Ident(_) => false,
481
482 // Arg: local if nesting is within map_depth, otherwise external
483 Self::Arg(arg) => arg.nesting < map_depth,
484
485 // Error expressions are constant (no external data)
486 Self::Error(_) => true,
487
488 // Never constant - references external data
489 Self::Reference(_)
490 | Self::Incoming(_)
491 | Self::Stmt(_)
492 | Self::InSubquery(_)
493 | Self::Exists(_)
494 | Self::Default
495 | Self::Func(_) => false,
496
497 // Const if all children are const at the same depth
498 Self::Record(expr_record) => expr_record
499 .iter()
500 .all(|expr| expr.is_const_at_depth(map_depth)),
501 Self::List(expr_list) => expr_list
502 .items
503 .iter()
504 .all(|expr| expr.is_const_at_depth(map_depth)),
505 Self::Cast(expr_cast) => expr_cast.expr.is_const_at_depth(map_depth),
506 Self::StartsWith(e) => {
507 e.expr.is_const_at_depth(map_depth) && e.prefix.is_const_at_depth(map_depth)
508 }
509 Self::Like(e) => {
510 e.expr.is_const_at_depth(map_depth) && e.pattern.is_const_at_depth(map_depth)
511 }
512 Self::Between(expr_between) => {
513 expr_between.expr.is_const_at_depth(map_depth)
514 && expr_between.low.is_const_at_depth(map_depth)
515 && expr_between.high.is_const_at_depth(map_depth)
516 }
517 Self::BinaryOp(expr_binary) => {
518 expr_binary.lhs.is_const_at_depth(map_depth)
519 && expr_binary.rhs.is_const_at_depth(map_depth)
520 }
521 Self::And(expr_and) => expr_and
522 .iter()
523 .all(|expr| expr.is_const_at_depth(map_depth)),
524 Self::Any(expr_any) => expr_any.expr.is_const_at_depth(map_depth),
525 Self::AnyOp(e) => {
526 e.lhs.is_const_at_depth(map_depth) && e.rhs.is_const_at_depth(map_depth)
527 }
528 Self::AllOp(e) => {
529 e.lhs.is_const_at_depth(map_depth) && e.rhs.is_const_at_depth(map_depth)
530 }
531 Self::Not(expr_not) => expr_not.expr.is_const_at_depth(map_depth),
532 Self::Or(expr_or) => expr_or.iter().all(|expr| expr.is_const_at_depth(map_depth)),
533 Self::IsNull(expr_is_null) => expr_is_null.expr.is_const_at_depth(map_depth),
534 Self::IsVariant(expr_is_variant) => expr_is_variant.expr.is_const_at_depth(map_depth),
535 Self::Variant(expr_variant) => expr_variant.base.is_const_at_depth(map_depth),
536 Self::InList(expr_in_list) => {
537 expr_in_list.expr.is_const_at_depth(map_depth)
538 && expr_in_list.list.is_const_at_depth(map_depth)
539 }
540 Self::Project(expr_project) => expr_project.base.is_const_at_depth(map_depth),
541
542 // Let: binding is checked at the current depth; the body is checked
543 // at depth+1 so that arg(nesting=0) in the body is treated as local.
544 Self::Let(expr_let) => {
545 expr_let
546 .bindings
547 .iter()
548 .all(|b| b.is_const_at_depth(map_depth))
549 && expr_let.body.is_const_at_depth(map_depth + 1)
550 }
551 // Map: base is checked at the current depth; the map body is checked
552 // at depth+1 so that arg(nesting=0) in the body is treated as local.
553 Self::Map(expr_map) => {
554 expr_map.base.is_const_at_depth(map_depth)
555 && expr_map.map.is_const_at_depth(map_depth + 1)
556 }
557 Self::Match(expr_match) => {
558 expr_match.subject.is_const_at_depth(map_depth)
559 && expr_match
560 .arms
561 .iter()
562 .all(|arm| arm.expr.is_const_at_depth(map_depth))
563 }
564
565 // Array predicates and length: const iff all operands are const.
566 Self::IsSuperset(e) => {
567 e.lhs.is_const_at_depth(map_depth) && e.rhs.is_const_at_depth(map_depth)
568 }
569 Self::Intersects(e) => {
570 e.lhs.is_const_at_depth(map_depth) && e.rhs.is_const_at_depth(map_depth)
571 }
572 Self::Length(e) => e.expr.is_const_at_depth(map_depth),
573 }
574 }
575
576 /// Returns `true` if the in-memory evaluator supports this expression.
577 ///
578 /// Args are allowed because they can be bound at evaluation time. References
579 /// to external data and operations that require lowering or a database are
580 /// rejected. `Exists` supports only `Values` bodies with evaluable rows.
581 /// Evaluation can still fail for invalid values or missing arguments.
582 pub fn is_eval(&self) -> bool {
583 match self {
584 // Always evaluable
585 Self::Value(_) | Self::Static(_) => true,
586
587 // Unresolved identifiers cannot be evaluated
588 Self::Ident(_) => false,
589
590 // Args are OK for evaluation
591 Self::Arg(_) => true,
592
593 // Error expressions are evaluable (they produce an error)
594 Self::Error(_) => true,
595
596 // Requires external data, lowering, or a database driver.
597 Self::Default
598 | Self::Reference(_)
599 | Self::Incoming(_)
600 | Self::Stmt(_)
601 | Self::InSubquery(_)
602 | Self::StartsWith(_)
603 | Self::Like(_)
604 | Self::Between(_)
605 | Self::IsVariant(_)
606 | Self::Variant(_)
607 | Self::IsSuperset(_)
608 | Self::Intersects(_)
609 | Self::Length(_) => false,
610
611 // Evaluable if all children are evaluable
612 Self::Record(expr_record) => expr_record.iter().all(|expr| expr.is_eval()),
613 Self::List(expr_list) => expr_list.items.iter().all(|expr| expr.is_eval()),
614 Self::Cast(expr_cast) => expr_cast.expr.is_eval(),
615 Self::BinaryOp(expr_binary) => expr_binary.lhs.is_eval() && expr_binary.rhs.is_eval(),
616 Self::And(expr_and) => expr_and.iter().all(|expr| expr.is_eval()),
617 Self::Any(expr_any) => expr_any.expr.is_eval(),
618 Self::AnyOp(e) => e.lhs.is_eval() && e.rhs.is_eval(),
619 Self::AllOp(e) => e.lhs.is_eval() && e.rhs.is_eval(),
620 Self::Or(expr_or) => expr_or.iter().all(|expr| expr.is_eval()),
621 Self::Not(expr_not) => expr_not.expr.is_eval(),
622 Self::IsNull(expr_is_null) => expr_is_null.expr.is_eval(),
623 Self::InList(expr_in_list) => {
624 expr_in_list.expr.is_eval() && expr_in_list.list.is_eval()
625 }
626 Self::Project(expr_project) => expr_project.base.is_eval(),
627 Self::Let(expr_let) => {
628 expr_let.bindings.iter().all(|b| b.is_eval()) && expr_let.body.is_eval()
629 }
630 Self::Map(expr_map) => expr_map.base.is_eval() && expr_map.map.is_eval(),
631 Self::Match(expr_match) => {
632 expr_match.subject.is_eval()
633 && expr_match.arms.iter().all(|arm| arm.expr.is_eval())
634 && expr_match.else_expr.is_eval()
635 }
636 Self::Exists(expr_exists) => match &expr_exists.subquery.body {
637 ExprSet::Values(values) => values.rows.iter().all(Self::is_eval),
638 _ => false,
639 },
640 Self::Func(ExprFunc::JsonExtract(func)) => func.base.is_eval(),
641 Self::Func(_) => false,
642 }
643 }
644
645 /// Returns a clone of this expression with all [`Projection`] nodes
646 /// transformed by `f`.
647 pub fn map_projections(&self, f: impl FnMut(&Projection) -> Projection) -> Self {
648 struct MapProjections<T>(T);
649
650 impl<T: FnMut(&Projection) -> Projection> VisitMut for MapProjections<T> {
651 fn visit_projection_mut(&mut self, i: &mut Projection) {
652 *i = self.0(i);
653 }
654 }
655
656 let mut mapped = self.clone();
657 MapProjections(f).visit_expr_mut(&mut mapped);
658 mapped
659 }
660
661 /// Navigates into a nested record or list expression by `path` and returns
662 /// a read-only [`Entry`] reference.
663 ///
664 /// Returns `None` if the path cannot be followed: the expression is not a
665 /// record or list at the expected depth, or a step indexes past the end of
666 /// a record or list.
667 #[track_caller]
668 pub fn entry(&self, path: impl EntryPath) -> Option<Entry<'_>> {
669 let mut ret = Entry::Expr(self);
670
671 for step in path.step_iter() {
672 ret = match ret {
673 Entry::Expr(Self::Record(expr)) => Entry::Expr(expr.get(step)?),
674 Entry::Expr(Self::List(expr)) => Entry::Expr(expr.items.get(step)?),
675 Entry::Value(Value::Record(record))
676 | Entry::Expr(Self::Value(Value::Record(record))) => {
677 Entry::Value(record.get(step)?)
678 }
679 Entry::Value(Value::List(items)) | Entry::Expr(Self::Value(Value::List(items))) => {
680 Entry::Value(items.get(step)?)
681 }
682 _ => return None,
683 }
684 }
685
686 Some(ret)
687 }
688
689 /// Navigates into a nested record or list expression by `path` and returns
690 /// a mutable [`EntryMut`] reference.
691 ///
692 /// # Panics
693 ///
694 /// Panics if the path cannot be followed on the current expression shape.
695 #[track_caller]
696 pub fn entry_mut(&mut self, path: impl EntryPath) -> EntryMut<'_> {
697 let mut ret = EntryMut::Expr(self);
698
699 for step in path.step_iter() {
700 ret = match ret {
701 EntryMut::Expr(Self::Record(expr)) => EntryMut::Expr(&mut expr[step]),
702 EntryMut::Value(Value::Record(record))
703 | EntryMut::Expr(Self::Value(Value::Record(record))) => {
704 EntryMut::Value(&mut record[step])
705 }
706 _ => todo!("ret={ret:#?}; step={step:#?}"),
707 }
708 }
709
710 ret
711 }
712
713 /// Takes the expression out, leaving `Expr::Value(Value::Null)` in its
714 /// place. Equivalent to `std::mem::replace(self, Expr::null())`.
715 pub fn take(&mut self) -> Self {
716 std::mem::replace(self, Self::Value(Value::Null))
717 }
718
719 /// Replaces every [`ExprArg`] in this expression tree with the
720 /// corresponding value from `input`.
721 pub fn substitute(&mut self, input: impl Input) {
722 Substitute::new(input).visit_expr_mut(self);
723 }
724}
725
726impl Node for Expr {
727 fn visit<V: Visit>(&self, mut visit: V) {
728 visit.visit_expr(self);
729 }
730
731 fn visit_mut<V: VisitMut>(&mut self, mut visit: V) {
732 visit.visit_expr_mut(self);
733 }
734}
735
736// === Conversions ===
737
738impl From<bool> for Expr {
739 fn from(value: bool) -> Self {
740 Self::Value(Value::from(value))
741 }
742}
743
744impl From<i64> for Expr {
745 fn from(value: i64) -> Self {
746 Self::Value(value.into())
747 }
748}
749
750impl From<&i64> for Expr {
751 fn from(value: &i64) -> Self {
752 Self::Value(value.into())
753 }
754}
755
756impl From<String> for Expr {
757 fn from(value: String) -> Self {
758 Self::Value(value.into())
759 }
760}
761
762impl From<&String> for Expr {
763 fn from(value: &String) -> Self {
764 Self::Value(value.into())
765 }
766}
767
768impl From<&str> for Expr {
769 fn from(value: &str) -> Self {
770 Self::Value(value.into())
771 }
772}
773
774impl From<Value> for Expr {
775 fn from(value: Value) -> Self {
776 Self::Value(value)
777 }
778}
779
780impl<E1, E2> From<(E1, E2)> for Expr
781where
782 E1: Into<Self>,
783 E2: Into<Self>,
784{
785 fn from(value: (E1, E2)) -> Self {
786 Self::Record(value.into())
787 }
788}
789
790impl fmt::Debug for Expr {
791 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
792 match self {
793 Self::AllOp(e) => e.fmt(f),
794 Self::And(e) => e.fmt(f),
795 Self::Any(e) => e.fmt(f),
796 Self::AnyOp(e) => e.fmt(f),
797 Self::Arg(e) => e.fmt(f),
798 Self::Between(e) => e.fmt(f),
799 Self::BinaryOp(e) => e.fmt(f),
800 Self::Cast(e) => e.fmt(f),
801 Self::Default => write!(f, "Default"),
802 Self::Error(e) => e.fmt(f),
803 Self::Exists(e) => e.fmt(f),
804 Self::Func(e) => e.fmt(f),
805 Self::Ident(e) => write!(f, "Ident({e:?})"),
806 Self::InList(e) => e.fmt(f),
807 Self::InSubquery(e) => e.fmt(f),
808 Self::Incoming(e) => write!(f, "Incoming({e:?})"),
809 Self::Intersects(e) => e.fmt(f),
810 Self::IsNull(e) => e.fmt(f),
811 Self::IsSuperset(e) => e.fmt(f),
812 Self::IsVariant(e) => e.fmt(f),
813 Self::Variant(e) => e.fmt(f),
814 Self::Length(e) => e.fmt(f),
815 Self::Let(e) => e.fmt(f),
816 Self::Like(e) => e.fmt(f),
817 Self::Map(e) => e.fmt(f),
818 Self::Match(e) => e.fmt(f),
819 Self::Not(e) => e.fmt(f),
820 Self::Or(e) => e.fmt(f),
821 Self::Project(e) => e.fmt(f),
822 Self::Record(e) => e.fmt(f),
823 Self::Reference(e) => e.fmt(f),
824 Self::List(e) => e.fmt(f),
825 Self::StartsWith(e) => e.fmt(f),
826 Self::Stmt(e) => e.fmt(f),
827 Self::Value(e) => e.fmt(f),
828 Self::Static(e) => write!(f, "Static({e:?})"),
829 }
830 }
831}