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