Skip to main content

mz_expr/
scalar.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10use std::collections::BTreeSet;
11use std::ops::BitOrAssign;
12use std::sync::Arc;
13use std::{fmt, mem};
14
15use itertools::Itertools;
16use mz_ore::cast::CastFrom;
17use mz_ore::iter::IteratorExt;
18use mz_ore::soft_assert_or_log;
19use mz_ore::stack::RecursionLimitError;
20use mz_ore::str::StrExt;
21use mz_ore::treat_as_equal::TreatAsEqual;
22use mz_ore::vec::swap_remove_multiple;
23use mz_pgrepr::TypeFromOidError;
24use mz_proto::{IntoRustIfSome, ProtoType, RustType, TryFromProtoError};
25use mz_repr::adt::array::InvalidArrayError;
26use mz_repr::adt::date::DateError;
27use mz_repr::adt::range::InvalidRangeError;
28use mz_repr::adt::regex::RegexCompilationError;
29use mz_repr::adt::timestamp::TimestampError;
30use mz_repr::strconv::{ParseError, ParseHexError};
31use mz_repr::{Datum, ReprColumnType, ReprScalarType, Row, RowArena, SqlColumnType};
32
33#[cfg(any(test, feature = "proptest"))]
34use proptest::prelude::*;
35#[cfg(any(test, feature = "proptest"))]
36use proptest_derive::Arbitrary;
37use serde::{Deserialize, Serialize};
38
39use crate::explain::{HumanizedExplain, HumanizerMode};
40pub use crate::scalar::columns::Columns;
41pub use crate::scalar::eval::Eval;
42use crate::scalar::func::variadic::{And, Or};
43use crate::scalar::func::{BinaryFunc, UnaryFunc, UnmaterializableFunc, VariadicFunc};
44pub use crate::scalar::optimizable::OptimizableExpr;
45use crate::scalar::proto_eval_error::proto_incompatible_array_dimensions::ProtoDims;
46use crate::visit::{Visit, VisitChildren};
47
48pub mod columns;
49pub mod eval;
50pub mod func;
51pub mod like_pattern;
52pub mod optimizable;
53mod reduce;
54
55include!(concat!(env!("OUT_DIR"), "/mz_expr.scalar.rs"));
56
57#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
58pub enum MirScalarExpr {
59    /// A column of the input row
60    Column(usize, TreatAsEqual<Option<Arc<str>>>),
61    /// A literal value.
62    /// (Stored as a row, because we can't own a Datum)
63    Literal(Result<Row, EvalError>, ReprColumnType),
64    /// A call to an unmaterializable function.
65    ///
66    /// These functions cannot be evaluated by `MirScalarExpr::eval`. They must
67    /// be transformed away by a higher layer.
68    CallUnmaterializable(UnmaterializableFunc),
69    /// A function call that takes one expression as an argument.
70    CallUnary {
71        func: UnaryFunc,
72        expr: Box<MirScalarExpr>,
73    },
74    /// A function call that takes two expressions as arguments.
75    CallBinary {
76        func: BinaryFunc,
77        expr1: Box<MirScalarExpr>,
78        expr2: Box<MirScalarExpr>,
79    },
80    /// A function call that takes an arbitrary number of arguments.
81    CallVariadic {
82        func: VariadicFunc,
83        exprs: Vec<MirScalarExpr>,
84    },
85    /// Conditionally evaluated expressions.
86    ///
87    /// It is important that `then` and `els` only be evaluated if
88    /// `cond` is true or not, respectively. This is the only way
89    /// users can guard execution (other logical operator do not
90    /// short-circuit) and we need to preserve that.
91    If {
92        cond: Box<MirScalarExpr>,
93        then: Box<MirScalarExpr>,
94        els: Box<MirScalarExpr>,
95    },
96}
97
98// We need a custom Debug because we don't want to show `None` for name information.
99// Sadly, the `derivative` crate doesn't support this use case.
100impl std::fmt::Debug for MirScalarExpr {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        match self {
103            MirScalarExpr::Column(i, TreatAsEqual(Some(name))) => {
104                write!(f, "Column({i}, {name:?})")
105            }
106            MirScalarExpr::Column(i, TreatAsEqual(None)) => write!(f, "Column({i})"),
107            MirScalarExpr::Literal(lit, typ) => write!(f, "Literal({lit:?}, {typ:?})"),
108            MirScalarExpr::CallUnmaterializable(func) => {
109                write!(f, "CallUnmaterializable({func:?})")
110            }
111            MirScalarExpr::CallUnary { func, expr } => {
112                write!(f, "CallUnary({func:?}, {expr:?})")
113            }
114            MirScalarExpr::CallBinary { func, expr1, expr2 } => {
115                write!(f, "CallBinary({func:?}, {expr1:?}, {expr2:?})")
116            }
117            MirScalarExpr::CallVariadic { func, exprs } => {
118                write!(f, "CallVariadic({func:?}, {exprs:?})")
119            }
120            MirScalarExpr::If { cond, then, els } => {
121                write!(f, "If({cond:?}, {then:?}, {els:?})")
122            }
123        }
124    }
125}
126
127impl MirScalarExpr {
128    pub fn columns(is: &[usize]) -> Vec<MirScalarExpr> {
129        is.iter().map(|i| MirScalarExpr::column(*i)).collect()
130    }
131
132    pub fn column(column: usize) -> Self {
133        MirScalarExpr::Column(column, TreatAsEqual(None))
134    }
135
136    pub fn named_column(column: usize, name: Arc<str>) -> Self {
137        MirScalarExpr::Column(column, TreatAsEqual(Some(name)))
138    }
139
140    pub fn literal(res: Result<Datum, EvalError>, typ: ReprScalarType) -> Self {
141        let typ = ReprColumnType {
142            scalar_type: typ,
143            nullable: matches!(res, Ok(Datum::Null)),
144        };
145        let row = res.map(|datum| Row::pack_slice(&[datum]));
146        MirScalarExpr::Literal(row, typ)
147    }
148
149    pub fn literal_ok(datum: Datum, typ: ReprScalarType) -> Self {
150        MirScalarExpr::literal(Ok(datum), typ)
151    }
152
153    /// Constructs a `MirScalarExpr::Literal` from a pre-packed `Row`
154    /// containing a single datum and a `ReprScalarType`. Nullability is
155    /// derived by inspecting the first datum in the row.
156    pub fn literal_from_single_element_row(row: Row, typ: ReprScalarType) -> Self {
157        soft_assert_or_log!(
158            row.iter().count() == 1,
159            "literal_from_row called with a Row containing {} datums",
160            row.iter().count()
161        );
162        let nullable = row.unpack_first() == Datum::Null;
163        let typ = ReprColumnType {
164            scalar_type: typ,
165            nullable,
166        };
167        MirScalarExpr::Literal(Ok(row), typ)
168    }
169
170    pub fn literal_null(typ: ReprScalarType) -> Self {
171        MirScalarExpr::literal_ok(Datum::Null, typ)
172    }
173
174    pub fn literal_false() -> Self {
175        MirScalarExpr::literal_ok(Datum::False, ReprScalarType::Bool)
176    }
177
178    pub fn literal_true() -> Self {
179        MirScalarExpr::literal_ok(Datum::True, ReprScalarType::Bool)
180    }
181
182    pub fn call_unary<U: Into<UnaryFunc>>(self, func: U) -> Self {
183        MirScalarExpr::CallUnary {
184            func: func.into(),
185            expr: Box::new(self),
186        }
187    }
188
189    pub fn call_binary<B: Into<BinaryFunc>>(self, other: Self, func: B) -> Self {
190        MirScalarExpr::CallBinary {
191            func: func.into(),
192            expr1: Box::new(self),
193            expr2: Box::new(other),
194        }
195    }
196
197    /// Call function `func` on `exprs`.
198    pub fn call_variadic<V: Into<VariadicFunc>>(func: V, exprs: Vec<Self>) -> Self {
199        MirScalarExpr::CallVariadic {
200            func: func.into(),
201            exprs,
202        }
203    }
204
205    pub fn if_then_else(self, t: Self, f: Self) -> Self {
206        MirScalarExpr::If {
207            cond: Box::new(self),
208            then: Box::new(t),
209            els: Box::new(f),
210        }
211    }
212
213    pub fn or(self, other: Self) -> Self {
214        MirScalarExpr::call_variadic(Or, vec![self, other])
215    }
216
217    pub fn and(self, other: Self) -> Self {
218        MirScalarExpr::call_variadic(And, vec![self, other])
219    }
220
221    pub fn not(self) -> Self {
222        self.call_unary(UnaryFunc::Not(func::Not))
223    }
224
225    pub fn call_is_null(self) -> Self {
226        self.call_unary(UnaryFunc::IsNull(func::IsNull))
227    }
228
229    /// Match AND or OR on self and get the args. If no match, then interpret self as if it were
230    /// wrapped in a 1-arg AND/OR.
231    pub fn and_or_args(&self, func_to_match: VariadicFunc) -> Vec<MirScalarExpr> {
232        assert!(func_to_match == Or.into() || func_to_match == And.into());
233        match self {
234            MirScalarExpr::CallVariadic { func, exprs } if *func == func_to_match => exprs.clone(),
235            _ => vec![self.clone()],
236        }
237    }
238
239    /// Try to match a literal equality involving the given expression on one side.
240    /// Return the (non-null) literal and a bool that indicates whether an inversion was needed.
241    ///
242    /// More specifically:
243    /// If `self` is an equality with a `null` literal on any side, then the match fails!
244    /// Otherwise: for a given `expr`, if `self` is `<expr> = <literal>` or `<literal> = <expr>`
245    /// then return `Some((<literal>, false))`. In addition to just trying to match `<expr>` as it
246    /// is, we also try to remove an invertible function call (such as a cast). If the match
247    /// succeeds with the inversion, then return `Some((<inverted-literal>, true))`. For more
248    /// details on the inversion, see `invert_casts_on_expr_eq_literal_inner`.
249    pub fn expr_eq_literal(&self, expr: &MirScalarExpr) -> Option<(Row, bool)> {
250        if let MirScalarExpr::CallBinary {
251            func: BinaryFunc::Eq(_),
252            expr1,
253            expr2,
254        } = self
255        {
256            if expr1.is_literal_null() || expr2.is_literal_null() {
257                return None;
258            }
259            if let Some(Ok(lit)) = expr1.as_literal_owned() {
260                return Self::expr_eq_literal_inner(expr, lit, expr1, expr2);
261            }
262            if let Some(Ok(lit)) = expr2.as_literal_owned() {
263                return Self::expr_eq_literal_inner(expr, lit, expr2, expr1);
264            }
265        }
266        None
267    }
268
269    fn expr_eq_literal_inner(
270        expr_to_match: &MirScalarExpr,
271        literal: Row,
272        literal_expr: &MirScalarExpr,
273        other_side: &MirScalarExpr,
274    ) -> Option<(Row, bool)> {
275        if other_side == expr_to_match {
276            return Some((literal, false));
277        } else {
278            // expr didn't exactly match. See if we can match it by inverse-casting.
279            let (cast_removed, inv_cast_lit) =
280                Self::invert_casts_on_expr_eq_literal_inner(other_side, literal_expr);
281            if &cast_removed == expr_to_match {
282                if let Some(Ok(inv_cast_lit_row)) = inv_cast_lit.as_literal_owned() {
283                    return Some((inv_cast_lit_row, true));
284                }
285            }
286        }
287        None
288    }
289
290    /// If `self` is `<expr> = <literal>` or `<literal> = <expr>` then
291    /// return `<expr>`. It also tries to remove a cast (or other invertible function call) from
292    /// `<expr>` before returning it, see `invert_casts_on_expr_eq_literal_inner`.
293    pub fn any_expr_eq_literal(&self) -> Option<MirScalarExpr> {
294        if let MirScalarExpr::CallBinary {
295            func: BinaryFunc::Eq(_),
296            expr1,
297            expr2,
298        } = self
299        {
300            if expr1.is_literal() {
301                let (expr, _literal) = Self::invert_casts_on_expr_eq_literal_inner(expr2, expr1);
302                return Some(expr);
303            }
304            if expr2.is_literal() {
305                let (expr, _literal) = Self::invert_casts_on_expr_eq_literal_inner(expr1, expr2);
306                return Some(expr);
307            }
308        }
309        None
310    }
311
312    /// If the given `MirScalarExpr` is a literal equality where one side is an invertible function
313    /// call, then calls the inverse function on both sides of the equality and returns the modified
314    /// version of the given `MirScalarExpr`. Otherwise, it returns the original expression.
315    /// For more details, see `invert_casts_on_expr_eq_literal_inner`.
316    pub fn invert_casts_on_expr_eq_literal(&self) -> MirScalarExpr {
317        if let MirScalarExpr::CallBinary {
318            func: BinaryFunc::Eq(_),
319            expr1,
320            expr2,
321        } = self
322        {
323            if expr1.is_literal() {
324                let (expr, literal) = Self::invert_casts_on_expr_eq_literal_inner(expr2, expr1);
325                return literal.call_binary(expr, func::Eq);
326            }
327            if expr2.is_literal() {
328                let (expr, literal) = Self::invert_casts_on_expr_eq_literal_inner(expr1, expr2);
329                return literal.call_binary(expr, func::Eq);
330            }
331            // Note: The above return statements should be consistent in whether they put the
332            // literal in expr1 or expr2, for the deduplication in CanonicalizeMfp to work.
333        }
334        self.clone()
335    }
336
337    /// Given an `<expr>` and a `<literal>` that were taken out from `<expr> = <literal>` or
338    /// `<literal> = <expr>`, it tries to simplify the equality by applying the inverse function of
339    /// the outermost function call of `<expr>` (if exists):
340    ///
341    /// `<literal> = func(<inner_expr>)`, where `func` is invertible
342    ///  -->
343    /// `<func^-1(literal)> = <inner_expr>`
344    /// if `func^-1(literal)` doesn't error out, and both `func` and `func^-1` preserve uniqueness.
345    ///
346    /// The return value is the `<inner_expr>` and the literal value that we get by applying the
347    /// inverse function.
348    fn invert_casts_on_expr_eq_literal_inner(
349        expr: &MirScalarExpr,
350        literal: &MirScalarExpr,
351    ) -> (MirScalarExpr, MirScalarExpr) {
352        assert!(matches!(literal, MirScalarExpr::Literal(..)));
353
354        let temp_storage = &RowArena::new();
355        let eval = |e: &MirScalarExpr| {
356            MirScalarExpr::literal(e.eval(&[], temp_storage), e.typ(&[]).scalar_type)
357        };
358
359        if let MirScalarExpr::CallUnary {
360            func,
361            expr: inner_expr,
362        } = expr
363        {
364            if let Some(inverse_func) = func.inverse() {
365                // We don't want to remove a function call that doesn't preserve uniqueness, e.g.,
366                // if `f` is a float, we don't want to inverse-cast `f::INT = 0`, because the
367                // inserted int-to-float cast wouldn't be able to invert the rounding.
368                // Also, we don't want to insert a function call that doesn't preserve
369                // uniqueness. E.g., if `a` has an integer type, we don't want to do
370                // a surprise rounding for `WHERE a = 3.14`.
371                if func.preserves_uniqueness() && inverse_func.preserves_uniqueness() {
372                    let lit_inv = eval(&MirScalarExpr::CallUnary {
373                        func: inverse_func,
374                        expr: Box::new(literal.clone()),
375                    });
376                    // The evaluation can error out, e.g., when casting a too large int32 to int16.
377                    // This case is handled by `impossible_literal_equality_because_types`.
378                    if !lit_inv.is_literal_err() {
379                        return (*inner_expr.clone(), lit_inv);
380                    }
381                }
382            }
383        }
384        (expr.clone(), literal.clone())
385    }
386
387    /// Tries to remove a cast (or other invertible function) in the same way as
388    /// `invert_casts_on_expr_eq_literal`, but if calling the inverse function fails on the literal,
389    /// then it deems the equality to be impossible. For example if `a` is a smallint column, then
390    /// it catches `a::integer = 1000000` to be an always false predicate (where the `::integer`
391    /// could have been inserted implicitly).
392    pub fn impossible_literal_equality_because_types(&self) -> bool {
393        if let MirScalarExpr::CallBinary {
394            func: BinaryFunc::Eq(_),
395            expr1,
396            expr2,
397        } = self
398        {
399            if expr1.is_literal() {
400                return Self::impossible_literal_equality_because_types_inner(expr1, expr2);
401            }
402            if expr2.is_literal() {
403                return Self::impossible_literal_equality_because_types_inner(expr2, expr1);
404            }
405        }
406        false
407    }
408
409    fn impossible_literal_equality_because_types_inner(
410        literal: &MirScalarExpr,
411        other_side: &MirScalarExpr,
412    ) -> bool {
413        assert!(matches!(literal, MirScalarExpr::Literal(..)));
414
415        let temp_storage = &RowArena::new();
416        let eval = |e: &MirScalarExpr| {
417            MirScalarExpr::literal(e.eval(&[], temp_storage), e.typ(&[]).scalar_type)
418        };
419
420        if let MirScalarExpr::CallUnary { func, .. } = other_side {
421            if let Some(inverse_func) = func.inverse() {
422                if inverse_func.preserves_uniqueness()
423                    && eval(&MirScalarExpr::CallUnary {
424                        func: inverse_func,
425                        expr: Box::new(literal.clone()),
426                    })
427                    .is_literal_err()
428                {
429                    return true;
430                }
431            }
432        }
433
434        false
435    }
436
437    /// Determines if `self` is
438    /// `<expr> < <literal>` or
439    /// `<expr> > <literal>` or
440    /// `<literal> < <expr>` or
441    /// `<literal> > <expr>` or
442    /// `<expr> <= <literal>` or
443    /// `<expr> >= <literal>` or
444    /// `<literal> <= <expr>` or
445    /// `<literal> >= <expr>`.
446    pub fn any_expr_ineq_literal(&self) -> bool {
447        match self {
448            MirScalarExpr::CallBinary {
449                func:
450                    BinaryFunc::Lt(_) | BinaryFunc::Lte(_) | BinaryFunc::Gt(_) | BinaryFunc::Gte(_),
451                expr1,
452                expr2,
453            } => expr1.is_literal() || expr2.is_literal(),
454            _ => false,
455        }
456    }
457
458    pub fn take(&mut self) -> Self {
459        mem::replace(self, MirScalarExpr::literal_null(ReprScalarType::String))
460    }
461
462    /// If the expression is a literal, this returns the literal's Datum or the literal's EvalError.
463    /// Otherwise, it returns None.
464    pub fn as_literal(&self) -> Option<Result<Datum<'_>, &EvalError>> {
465        if let MirScalarExpr::Literal(lit, _column_type) = self {
466            Some(lit.as_ref().map(|row| row.unpack_first()))
467        } else {
468            None
469        }
470    }
471
472    /// Flattens the two failure modes of `as_literal` into one layer of Option: returns the
473    /// literal's Datum only if the expression is a literal, and it's not a literal error.
474    pub fn as_literal_non_error(&self) -> Option<Datum<'_>> {
475        self.as_literal().map(|eval_err| eval_err.ok()).flatten()
476    }
477
478    pub fn as_literal_owned(&self) -> Option<Result<Row, EvalError>> {
479        if let MirScalarExpr::Literal(lit, _column_type) = self {
480            Some(lit.clone())
481        } else {
482            None
483        }
484    }
485
486    /// Returns a reference to the `Row` if the expression is a non-NULL `Ok` literal.
487    pub fn as_literal_non_null_row(&self) -> Option<&Row> {
488        if let MirScalarExpr::Literal(Ok(row), _) = self {
489            if !row.unpack_first().is_null() {
490                return Some(row);
491            }
492        }
493        None
494    }
495
496    pub fn as_literal_str(&self) -> Option<&str> {
497        match self.as_literal() {
498            Some(Ok(Datum::String(s))) => Some(s),
499            _ => None,
500        }
501    }
502
503    pub fn as_literal_int64(&self) -> Option<i64> {
504        match self.as_literal() {
505            Some(Ok(Datum::Int64(i))) => Some(i),
506            _ => None,
507        }
508    }
509
510    pub fn as_literal_err(&self) -> Option<&EvalError> {
511        self.as_literal().and_then(|lit| lit.err())
512    }
513
514    pub fn is_literal(&self) -> bool {
515        matches!(self, MirScalarExpr::Literal(_, _))
516    }
517
518    pub fn is_literal_true(&self) -> bool {
519        Some(Ok(Datum::True)) == self.as_literal()
520    }
521
522    pub fn is_literal_false(&self) -> bool {
523        Some(Ok(Datum::False)) == self.as_literal()
524    }
525
526    pub fn is_literal_null(&self) -> bool {
527        Some(Ok(Datum::Null)) == self.as_literal()
528    }
529
530    pub fn is_literal_ok(&self) -> bool {
531        matches!(self, MirScalarExpr::Literal(Ok(_), _typ))
532    }
533
534    pub fn is_literal_err(&self) -> bool {
535        matches!(self, MirScalarExpr::Literal(Err(_), _typ))
536    }
537
538    pub fn is_error_if_null(&self) -> bool {
539        matches!(
540            self,
541            Self::CallVariadic {
542                func: VariadicFunc::ErrorIfNull(_),
543                ..
544            }
545        )
546    }
547
548    /// If `self` expresses a temporal filter, normalize it to start with `mz_now()` and return
549    /// references.
550    ///
551    /// A temporal filter is an expression of the form `mz_now() <BINOP> <EXPR>`,
552    /// for a restricted set of `BINOP` and `EXPR` that do not themselves contain `mz_now()`.
553    /// Expressions may conform to this once their expressions are swapped.
554    ///
555    /// If the expression is not a temporal filter, it will be unchanged, and the reason for why
556    /// it's not a temporal filter is returned as a string.
557    pub fn as_mut_temporal_filter(&mut self) -> Result<(&BinaryFunc, &mut MirScalarExpr), String> {
558        if !self.contains_temporal() {
559            return Err("Does not involve mz_now()".to_string());
560        }
561        // Supported temporal predicates are exclusively binary operators.
562        if let MirScalarExpr::CallBinary { func, expr1, expr2 } = self {
563            // Attempt to put `LogicalTimestamp` in the first argument position.
564            if !expr1.contains_temporal()
565                && **expr2 == MirScalarExpr::CallUnmaterializable(UnmaterializableFunc::MzNow)
566            {
567                let new_func = match func {
568                    BinaryFunc::Eq(_) => func::Eq.into(),
569                    BinaryFunc::Lt(_) => func::Gt.into(),
570                    BinaryFunc::Lte(_) => func::Gte.into(),
571                    BinaryFunc::Gt(_) => func::Lt.into(),
572                    BinaryFunc::Gte(_) => func::Lte.into(),
573                    x => {
574                        return Err(format!("Unsupported binary temporal operation: {:?}", x));
575                    }
576                };
577                std::mem::swap(expr1, expr2);
578                *func = new_func;
579            }
580
581            // Error if MLT is referenced in an unsupported position.
582            if expr2.contains_temporal()
583                || **expr1 != MirScalarExpr::CallUnmaterializable(UnmaterializableFunc::MzNow)
584            {
585                let mode = HumanizedExplain::new(false); // no redaction
586                let bad_expr = MirScalarExpr::CallBinary {
587                    func: func.clone(),
588                    expr1: expr1.clone(),
589                    expr2: expr2.clone(),
590                };
591                return Err(format!(
592                    "Unsupported temporal predicate. Note: `mz_now()` must be directly compared to a mz_timestamp-castable expression. Expression found: {}",
593                    mode.expr(&bad_expr, None),
594                ));
595            }
596
597            Ok((&*func, expr2))
598        } else {
599            let mode = HumanizedExplain::new(false); // no redaction
600            Err(format!(
601                "Unsupported temporal predicate. Note: `mz_now()` must be directly compared to a non-temporal expression of mz_timestamp-castable type. Expression found: {}",
602                mode.expr(self, None),
603            ))
604        }
605    }
606
607    #[deprecated = "Use `might_error` instead"]
608    pub fn contains_error_if_null(&self) -> bool {
609        let mut worklist = vec![self];
610        while let Some(expr) = worklist.pop() {
611            if expr.is_error_if_null() {
612                return true;
613            }
614            worklist.extend(expr.children());
615        }
616        false
617    }
618
619    pub fn contains_err(&self) -> bool {
620        let mut worklist = vec![self];
621        while let Some(expr) = worklist.pop() {
622            if expr.is_literal_err() {
623                return true;
624            }
625            worklist.extend(expr.children());
626        }
627        false
628    }
629
630    /// A very crude approximation for scalar expressions that might produce an
631    /// error.
632    ///
633    /// Currently, this is restricted only to expressions that either contain a
634    /// literal error or a [`VariadicFunc::ErrorIfNull`] call.
635    pub fn might_error(&self) -> bool {
636        let mut worklist = vec![self];
637        while let Some(expr) = worklist.pop() {
638            if expr.is_literal_err() || expr.is_error_if_null() {
639                return true;
640            }
641            worklist.extend(expr.children());
642        }
643        false
644    }
645
646    /// Reduces a complex expression where possible.
647    ///
648    /// This function uses nullability information present in `column_types`,
649    /// and the result may only continue to be a correct transformation as
650    /// long as this information continues to hold (nullability may not hold
651    /// as expressions migrate around).
652    ///
653    /// (If you'd like to not use nullability information here, then you can
654    /// tweak the nullabilities in `column_types` before passing it to this
655    /// function, see e.g. in `EquivalenceClasses::minimize`.)
656    ///
657    /// Also performs partial canonicalization on the expression.
658    ///
659    /// ```rust
660    /// use mz_expr::MirScalarExpr;
661    /// use mz_repr::{ReprColumnType, Datum, SqlScalarType};
662    ///
663    /// let expr_0 = MirScalarExpr::column(0);
664    /// let expr_t = MirScalarExpr::literal_true();
665    /// let expr_f = MirScalarExpr::literal_false();
666    ///
667    /// let mut test =
668    /// expr_t
669    ///     .clone()
670    ///     .and(expr_f.clone())
671    ///     .if_then_else(expr_0, expr_t.clone());
672    ///
673    /// let input_type = vec![ReprColumnType::from(&SqlScalarType::Int32.nullable(false))];
674    /// test.reduce(&input_type);
675    /// assert_eq!(test, expr_t);
676    /// ```
677    /// Reduce the expression to a simpler form.
678    pub fn reduce(&mut self, column_types: &[ReprColumnType]) {
679        reduce::reduce(self, column_types);
680    }
681
682    /// Decompose an IsNull expression into a disjunction of
683    /// simpler expressions.
684    ///
685    /// Assumes that `self` is the expression inside of an IsNull.
686    /// Returns `Some(expressions)` if the outer IsNull is to be
687    /// replaced by some other expression. Note: if it returns
688    /// None, it might still have mutated *self.
689    fn decompose_is_null(&mut self) -> Option<MirScalarExpr> {
690        // TODO: allow simplification of unmaterializable functions
691
692        match self {
693            MirScalarExpr::CallUnary {
694                func,
695                expr: inner_expr,
696            } => {
697                if !func.introduces_nulls() {
698                    if func.propagates_nulls() {
699                        *self = inner_expr.take();
700                        return self.decompose_is_null();
701                    } else {
702                        // We can simplify to `false`, because the function simply can't produce
703                        // nulls at all. This is because
704                        // - !propagates_nulls means that the input type of the Rust function is not
705                        //   nullable, so the automatic null propagation won't kick in;
706                        // - !introduces_nulls means that the output type of the Rust function is
707                        //   not nullable, so the Rust function can't produce a null manually either.
708                        //
709                        // Note that we can't do this same optimization for binary and variadic
710                        // functions. This is because for binary and variadic functions the value of
711                        // propagates_nulls and introduces_nulls is not derived solely from the
712                        // input/output type nullabilities, but instead depends on what the Rust
713                        // function does. For example, list concatenation neither introduces nor
714                        // propagates nulls, but it can produce a null:
715                        // - It does not introduce nulls, because if both input lists are not null,
716                        //   then it will produce a list.
717                        // - It does not propagate nulls, because giving a null as just one of the
718                        //   arguments returns the other argument instead of null.
719                        // - It does produce a null if both arguments are null.
720                        return Some(MirScalarExpr::literal_false());
721                    }
722                }
723            }
724            MirScalarExpr::CallBinary { func, expr1, expr2 } => {
725                // (<expr1> <op> <expr2>) IS NULL can often be simplified to
726                // (<expr1> IS NULL) OR (<expr2> IS NULL).
727                if func.propagates_nulls() && !func.introduces_nulls() {
728                    let expr1 = expr1.take().call_is_null();
729                    let expr2 = expr2.take().call_is_null();
730                    return Some(expr1.or(expr2));
731                }
732            }
733            MirScalarExpr::CallVariadic { func, exprs } => {
734                if func.propagates_nulls() && !func.introduces_nulls() {
735                    let exprs = exprs.into_iter().map(|e| e.take().call_is_null()).collect();
736                    return Some(MirScalarExpr::call_variadic(Or, exprs));
737                }
738            }
739            _ => {}
740        }
741
742        None
743    }
744
745    /// Flattens a chain of calls to associative variadic functions
746    /// (For example: ORs or ANDs)
747    pub fn flatten_associative(&mut self) {
748        match self {
749            MirScalarExpr::CallVariadic {
750                exprs: outer_operands,
751                func: outer_func,
752            } if outer_func.is_associative() => {
753                *outer_operands = outer_operands
754                    .into_iter()
755                    .flat_map(|o| {
756                        if let MirScalarExpr::CallVariadic {
757                            exprs: inner_operands,
758                            func: inner_func,
759                        } = o
760                        {
761                            if *inner_func == *outer_func {
762                                mem::take(inner_operands)
763                            } else {
764                                vec![o.take()]
765                            }
766                        } else {
767                            vec![o.take()]
768                        }
769                    })
770                    .collect();
771            }
772            _ => {}
773        }
774    }
775
776    /* #region AND/OR canonicalization and transformations  */
777
778    /// Canonicalizes AND/OR, and does some straightforward simplifications
779    fn reduce_and_canonicalize_and_or(&mut self) {
780        // We do this until fixed point, because after undistribute_and_or calls us, it relies on
781        // the property that self is not an 1-arg AND/OR. Just one application of our loop body
782        // can't ensure this, because the application itself might create a 1-arg AND/OR.
783        let mut old_self = MirScalarExpr::column(0);
784        while old_self != *self {
785            old_self = self.clone();
786            match self {
787                MirScalarExpr::CallVariadic {
788                    func: func @ (VariadicFunc::And(_) | VariadicFunc::Or(_)),
789                    exprs,
790                } => {
791                    // Canonically order elements so that various deduplications work better,
792                    // e.g., in undistribute_and_or.
793                    // Also, extract_equal_or_both_null_inner depends on the args being sorted.
794                    exprs.sort();
795
796                    // x AND/OR x --> x
797                    exprs.dedup(); // this also needs the above sorting
798
799                    if exprs.len() == 1 {
800                        // AND/OR of 1 argument evaluates to that argument
801                        *self = exprs.swap_remove(0);
802                    } else if exprs.len() == 0 {
803                        // AND/OR of 0 arguments evaluates to true/false
804                        *self = func.unit_of_and_or();
805                    } else if exprs.iter().any(|e| *e == func.zero_of_and_or()) {
806                        // short-circuiting
807                        *self = func.zero_of_and_or();
808                    } else {
809                        // a AND true --> a
810                        // a OR false --> a
811                        exprs.retain(|e| *e != func.unit_of_and_or());
812                    }
813                }
814                _ => {}
815            }
816        }
817    }
818
819    /// Transforms !(a && b) into !a || !b, and !(a || b) into !a && !b
820    fn demorgans(&mut self) {
821        if let MirScalarExpr::CallUnary {
822            expr: inner,
823            func: UnaryFunc::Not(func::Not),
824        } = self
825        {
826            inner.flatten_associative();
827            match &mut **inner {
828                MirScalarExpr::CallVariadic {
829                    func: inner_func @ (VariadicFunc::And(_) | VariadicFunc::Or(_)),
830                    exprs,
831                } => {
832                    *inner_func = inner_func.switch_and_or();
833                    *exprs = exprs.into_iter().map(|e| e.take().not()).collect();
834                    *self = (*inner).take(); // Removes the outer not
835                }
836                _ => {}
837            }
838        }
839    }
840
841    /// AND/OR undistribution (factoring out) to apply at each `MirScalarExpr`.
842    ///
843    /// This method attempts to apply one of the [distribution laws][distributivity]
844    /// (in a direction opposite to the their name):
845    /// ```text
846    /// (a && b) || (a && c) --> a && (b || c)  // Undistribute-OR
847    /// (a || b) && (a || c) --> a || (b && c)  // Undistribute-AND
848    /// ```
849    /// or one of their corresponding two [absorption law][absorption] special
850    /// cases:
851    /// ```text
852    /// a || (a && c)  -->  a  // Absorb-OR
853    /// a && (a || c)  -->  a  // Absorb-AND
854    /// ```
855    ///
856    /// The method also works with more than 2 arguments at the top, e.g.
857    /// ```text
858    /// (a && b) || (a && c) || (a && d)  -->  a && (b || c || d)
859    /// ```
860    /// It can also factor out only a subset of the top arguments, e.g.
861    /// ```text
862    /// (a && b) || (a && c) || (d && e)  -->  (a && (b || c)) || (d && e)
863    /// ```
864    ///
865    /// Note that sometimes there are two overlapping possibilities to factor
866    /// out from, e.g.
867    /// ```text
868    /// (a && b) || (a && c) || (d && c)
869    /// ```
870    /// Here we can factor out `a` from from the 1. and 2. terms, or we can
871    /// factor out `c` from the 2. and 3. terms. One of these might lead to
872    /// more/better undistribution opportunities later, but we just pick one
873    /// locally, because recursively trying out all of them would lead to
874    /// exponential run time.
875    ///
876    /// The local heuristic is that we prefer a candidate that leads to an
877    /// absorption, or if there is no such one then we simply pick the first. In
878    /// case of multiple absorption candidates, it doesn't matter which one we
879    /// pick, because applying an absorption cannot adversely effect the
880    /// possibility of applying other absorptions.
881    ///
882    /// # Assumption
883    ///
884    /// Assumes that nested chains of AND/OR applications are flattened (this
885    /// can be enforced with [`Self::flatten_associative`]).
886    ///
887    /// # Examples
888    ///
889    /// Absorb-OR:
890    /// ```text
891    /// a || (a && c) || (a && d)
892    /// -->
893    /// a && (true || c || d)
894    /// -->
895    /// a && true
896    /// -->
897    /// a
898    /// ```
899    /// Here only the first step is performed by this method. The rest is done
900    /// by [`Self::reduce_and_canonicalize_and_or`] called after us in
901    /// `reduce()`.
902    ///
903    /// [distributivity]: https://en.wikipedia.org/wiki/Distributive_property
904    /// [absorption]: https://en.wikipedia.org/wiki/Absorption_law
905    fn undistribute_and_or(&mut self) {
906        // It wouldn't be strictly necessary to wrap this fn in this loop, because `reduce()` calls
907        // us in a loop anyway. However, `reduce()` tries to do many other things, so the loop here
908        // improves performance when there are several undistributions to apply in sequence, which
909        // can occur in `CanonicalizeMfp` when undoing the DNF.
910        let mut old_self = MirScalarExpr::column(0);
911        while old_self != *self {
912            old_self = self.clone();
913            self.reduce_and_canonicalize_and_or(); // We don't want to deal with 1-arg AND/OR at the top
914            if let MirScalarExpr::CallVariadic {
915                exprs: outer_operands,
916                func: outer_func @ (VariadicFunc::Or(_) | VariadicFunc::And(_)),
917            } = self
918            {
919                let inner_func = outer_func.switch_and_or();
920
921                // Make sure that each outer operand is a call to inner_func, by wrapping in a 1-arg
922                // call if necessary.
923                outer_operands.iter_mut().for_each(|o| {
924                    if !matches!(o, MirScalarExpr::CallVariadic {func: f, ..} if *f == inner_func) {
925                        *o = MirScalarExpr::CallVariadic {
926                            func: inner_func.clone(),
927                            exprs: vec![o.take()],
928                        };
929                    }
930                });
931
932                let mut inner_operands_refs: Vec<&mut Vec<MirScalarExpr>> = outer_operands
933                    .iter_mut()
934                    .map(|o| match o {
935                        MirScalarExpr::CallVariadic { func: f, exprs } if *f == inner_func => exprs,
936                        _ => unreachable!(), // the wrapping made sure that we'll get a match
937                    })
938                    .collect();
939
940                // Find inner operands to undistribute, i.e., which are in _all_ of the outer operands.
941                let mut intersection = inner_operands_refs
942                    .iter()
943                    .map(|v| (*v).clone())
944                    .reduce(|ops1, ops2| ops1.into_iter().filter(|e| ops2.contains(e)).collect())
945                    .unwrap();
946                intersection.sort();
947                intersection.dedup();
948
949                if !intersection.is_empty() {
950                    // Factor out the intersection from all the top-level args.
951
952                    // Remove the intersection from each inner operand vector.
953                    inner_operands_refs
954                        .iter_mut()
955                        .for_each(|ops| (**ops).retain(|o| !intersection.contains(o)));
956
957                    // Simplify terms that now have only 0 or 1 args due to removing the intersection.
958                    outer_operands
959                        .iter_mut()
960                        .for_each(|o| o.reduce_and_canonicalize_and_or());
961
962                    // Add the intersection at the beginning
963                    *self = MirScalarExpr::CallVariadic {
964                        func: inner_func,
965                        exprs: intersection.into_iter().chain_one(self.clone()).collect(),
966                    };
967                } else {
968                    // If the intersection was empty, that means that there is nothing we can factor out
969                    // from _all_ the top-level args. However, we might still find something to factor
970                    // out from a subset of the top-level args. To find such an opportunity, we look for
971                    // duplicates across all inner args, e.g. if we have
972                    // `(...) OR (... AND `a` AND ...) OR (...) OR (... AND `a` AND ...)`
973                    // then we'll find that `a` occurs in more than one top-level arg, so
974                    // `indexes_to_undistribute` will point us to the 2. and 4. top-level args.
975
976                    // Create (inner_operand, index) pairs, where the index is the position in
977                    // outer_operands
978                    let all_inner_operands = inner_operands_refs
979                        .iter()
980                        .enumerate()
981                        .flat_map(|(i, inner_vec)| inner_vec.iter().map(move |a| ((*a).clone(), i)))
982                        .sorted()
983                        .collect_vec();
984
985                    // Find inner operand expressions that occur in more than one top-level arg.
986                    // Each inner vector in `undistribution_opportunities` will belong to one such inner
987                    // operand expression, and it is a set of indexes pointing to top-level args where
988                    // that inner operand occurs.
989                    let undistribution_opportunities = all_inner_operands
990                        .iter()
991                        .chunk_by(|(a, _i)| a)
992                        .into_iter()
993                        .map(|(_a, g)| g.map(|(_a, i)| *i).sorted().dedup().collect_vec())
994                        .filter(|g| g.len() > 1)
995                        .collect_vec();
996
997                    // Choose one of the inner vectors from `undistribution_opportunities`.
998                    let indexes_to_undistribute = undistribution_opportunities
999                        .iter()
1000                        // Let's prefer index sets that directly lead to an absorption.
1001                        .find(|index_set| {
1002                            index_set
1003                                .iter()
1004                                .any(|i| inner_operands_refs.get(*i).unwrap().len() == 1)
1005                        })
1006                        // If we didn't find any absorption, then any index set will do.
1007                        .or_else(|| undistribution_opportunities.first())
1008                        .cloned();
1009
1010                    // In any case, undo the 1-arg wrapping that we did at the beginning.
1011                    outer_operands
1012                        .iter_mut()
1013                        .for_each(|o| o.reduce_and_canonicalize_and_or());
1014
1015                    if let Some(indexes_to_undistribute) = indexes_to_undistribute {
1016                        // Found something to undistribute from a subset of the outer operands.
1017                        // We temporarily remove these from outer_operands, call ourselves on it, and
1018                        // then push back the result.
1019                        let mut undistribute_from = MirScalarExpr::CallVariadic {
1020                            func: outer_func.clone(),
1021                            exprs: swap_remove_multiple(outer_operands, indexes_to_undistribute),
1022                        };
1023                        // By construction, the recursive call is guaranteed to hit
1024                        // the `!intersection.is_empty()` branch.
1025                        undistribute_from.undistribute_and_or();
1026                        // Append the undistributed result to outer operands that were not included in
1027                        // indexes_to_undistribute.
1028                        outer_operands.push(undistribute_from);
1029                    }
1030                }
1031            }
1032        }
1033    }
1034
1035    /* #endregion */
1036
1037    /// Adds any columns that *must* be non-Null for `self` to be non-Null.
1038    pub fn non_null_requirements(&self, columns: &mut BTreeSet<usize>) {
1039        match self {
1040            MirScalarExpr::Column(col, _name) => {
1041                columns.insert(*col);
1042            }
1043            MirScalarExpr::Literal(..) => {}
1044            MirScalarExpr::CallUnmaterializable(_) => (),
1045            MirScalarExpr::CallUnary { func, expr } => {
1046                if func.propagates_nulls() {
1047                    expr.non_null_requirements(columns);
1048                }
1049            }
1050            MirScalarExpr::CallBinary { func, expr1, expr2 } => {
1051                if func.propagates_nulls() {
1052                    expr1.non_null_requirements(columns);
1053                    expr2.non_null_requirements(columns);
1054                }
1055            }
1056            MirScalarExpr::CallVariadic { func, exprs } => {
1057                if func.propagates_nulls() {
1058                    for expr in exprs {
1059                        expr.non_null_requirements(columns);
1060                    }
1061                }
1062            }
1063            MirScalarExpr::If { .. } => (),
1064        }
1065    }
1066
1067    pub fn sql_typ(&self, column_types: &[SqlColumnType]) -> SqlColumnType {
1068        let repr_column_types = column_types.iter().map(ReprColumnType::from).collect_vec();
1069        SqlColumnType::from_repr(&self.typ(&repr_column_types))
1070    }
1071
1072    pub fn typ(&self, column_types: &[ReprColumnType]) -> ReprColumnType {
1073        match self {
1074            MirScalarExpr::Column(i, _name) => column_types[*i].clone(),
1075            MirScalarExpr::Literal(_, typ) => typ.clone(),
1076            MirScalarExpr::CallUnmaterializable(func) => func.output_type(),
1077            MirScalarExpr::CallUnary { expr, func } => func.output_type(expr.typ(column_types)),
1078            MirScalarExpr::CallBinary { expr1, expr2, func } => {
1079                func.output_type(&[expr1.typ(column_types), expr2.typ(column_types)])
1080            }
1081            MirScalarExpr::CallVariadic { exprs, func } => {
1082                func.output_type(exprs.iter().map(|e| e.typ(column_types)).collect())
1083            }
1084            MirScalarExpr::If { cond: _, then, els } => {
1085                let then_type = then.typ(column_types);
1086                let else_type = els.typ(column_types);
1087                then_type.union(&else_type).unwrap()
1088            }
1089        }
1090    }
1091
1092    /// True iff the expression contains
1093    /// `UnmaterializableFunc::MzNow`.
1094    pub fn contains_temporal(&self) -> bool {
1095        let mut contains = false;
1096        self.visit_pre(|e| {
1097            if let MirScalarExpr::CallUnmaterializable(UnmaterializableFunc::MzNow) = e {
1098                contains = true;
1099            }
1100        });
1101        contains
1102    }
1103
1104    /// True iff the expression contains an `UnmaterializableFunc`.
1105    pub fn contains_unmaterializable(&self) -> bool {
1106        let mut contains = false;
1107        self.visit_pre(|e| {
1108            if let MirScalarExpr::CallUnmaterializable(_) = e {
1109                contains = true;
1110            }
1111        });
1112        contains
1113    }
1114
1115    /// True iff the expression contains an `UnmaterializableFunc` that is not in the `exceptions`
1116    /// list.
1117    pub fn contains_unmaterializable_except(&self, exceptions: &[UnmaterializableFunc]) -> bool {
1118        let mut contains = false;
1119        self.visit_pre(|e| match e {
1120            MirScalarExpr::CallUnmaterializable(f) if !exceptions.contains(f) => contains = true,
1121            _ => (),
1122        });
1123        contains
1124    }
1125
1126    /// True iff the expression contains a `Column`.
1127    pub fn contains_column(&self) -> bool {
1128        let mut contains = false;
1129        self.visit_pre(|e| {
1130            if let MirScalarExpr::Column(_col, _name) = e {
1131                contains = true;
1132            }
1133        });
1134        contains
1135    }
1136
1137    /// True iff the expression contains a `Dummy`.
1138    pub fn contains_dummy(&self) -> bool {
1139        let mut contains = false;
1140        self.visit_pre(|e| {
1141            if let MirScalarExpr::Literal(row, _) = e {
1142                if let Ok(row) = row {
1143                    contains |= row.iter().any(|d| d.contains_dummy());
1144                }
1145            }
1146        });
1147        contains
1148    }
1149
1150    /// The size of the expression as a tree.
1151    pub fn size(&self) -> usize {
1152        let mut size = 0;
1153        self.visit_pre(&mut |_: &MirScalarExpr| {
1154            size += 1;
1155        });
1156        size
1157    }
1158}
1159
1160impl Eval for MirScalarExpr {
1161    fn eval<'a>(
1162        &'a self,
1163        datums: &[Datum<'a>],
1164        temp_storage: &'a RowArena,
1165    ) -> Result<Datum<'a>, EvalError> {
1166        match self {
1167            MirScalarExpr::Column(index, _name) => Ok(datums[*index]),
1168            MirScalarExpr::Literal(res, _column_type) => match res {
1169                Ok(row) => Ok(row.unpack_first()),
1170                Err(e) => Err(e.clone()),
1171            },
1172            // Unmaterializable functions must be transformed away before
1173            // evaluation. Their purpose is as a placeholder for data that is
1174            // not known at plan time but can be inlined before runtime.
1175            MirScalarExpr::CallUnmaterializable(x) => Err(EvalError::Internal(
1176                format!("cannot evaluate unmaterializable function: {:?}", x).into(),
1177            )),
1178            MirScalarExpr::CallUnary { func, expr } => {
1179                func.eval(datums, temp_storage, expr.as_ref())
1180            }
1181            MirScalarExpr::CallBinary { func, expr1, expr2 } => {
1182                func.eval(datums, temp_storage, &[expr1.as_ref(), expr2.as_ref()])
1183            }
1184            MirScalarExpr::CallVariadic { func, exprs } => {
1185                func.eval(datums, temp_storage, exprs.as_slice())
1186            }
1187            MirScalarExpr::If { cond, then, els } => match cond.eval(datums, temp_storage)? {
1188                Datum::True => then.eval(datums, temp_storage),
1189                Datum::False | Datum::Null => els.eval(datums, temp_storage),
1190                d => Err(EvalError::Internal(
1191                    format!("if condition evaluated to non-boolean datum: {:?}", d).into(),
1192                )),
1193            },
1194        }
1195    }
1196
1197    fn could_error(&self) -> bool {
1198        match self {
1199            MirScalarExpr::Column(_col, _name) => false,
1200            MirScalarExpr::Literal(row, ..) => row.is_err(),
1201            MirScalarExpr::CallUnmaterializable(_) => true,
1202            MirScalarExpr::CallUnary { func, expr } => func.could_error() || expr.could_error(),
1203            MirScalarExpr::CallBinary { func, expr1, expr2 } => {
1204                func.could_error() || expr1.could_error() || expr2.could_error()
1205            }
1206            MirScalarExpr::CallVariadic { func, exprs } => {
1207                func.could_error() || exprs.iter().any(|e| e.could_error())
1208            }
1209            MirScalarExpr::If { cond, then, els } => {
1210                cond.could_error() || then.could_error() || els.could_error()
1211            }
1212        }
1213    }
1214}
1215
1216impl Columns for MirScalarExpr {
1217    fn column(c: usize) -> Self {
1218        MirScalarExpr::column(c)
1219    }
1220
1221    fn is_column(&self) -> bool {
1222        matches!(self, MirScalarExpr::Column(_col, _name))
1223    }
1224
1225    fn as_column(&self) -> Option<usize> {
1226        if let MirScalarExpr::Column(c, _) = self {
1227            Some(*c)
1228        } else {
1229            None
1230        }
1231    }
1232
1233    fn as_column_mut(&mut self) -> Option<&mut usize> {
1234        if let MirScalarExpr::Column(c, _) = self {
1235            Some(c)
1236        } else {
1237            None
1238        }
1239    }
1240
1241    fn support_into(&self, support: &mut BTreeSet<usize>) {
1242        self.visit_pre(|e| {
1243            if let MirScalarExpr::Column(i, _) = e {
1244                support.insert(*i);
1245            }
1246        });
1247    }
1248
1249    fn visit_columns<F>(&mut self, mut action: F)
1250    where
1251        F: FnMut(&mut usize),
1252    {
1253        self.visit_pre_mut(|e| {
1254            if let MirScalarExpr::Column(col, _) = e {
1255                action(col);
1256            }
1257        });
1258    }
1259}
1260
1261impl VisitChildren<Self> for MirScalarExpr {
1262    fn visit_children<F>(&self, mut f: F)
1263    where
1264        F: FnMut(&Self),
1265    {
1266        use MirScalarExpr::*;
1267        match self {
1268            Column(_, _) | Literal(_, _) | CallUnmaterializable(_) => (),
1269            CallUnary { expr, .. } => {
1270                f(expr);
1271            }
1272            CallBinary { expr1, expr2, .. } => {
1273                f(expr1);
1274                f(expr2);
1275            }
1276            CallVariadic { exprs, .. } => {
1277                for expr in exprs {
1278                    f(expr);
1279                }
1280            }
1281            If { cond, then, els } => {
1282                f(cond);
1283                f(then);
1284                f(els);
1285            }
1286        }
1287    }
1288
1289    fn visit_mut_children<F>(&mut self, mut f: F)
1290    where
1291        F: FnMut(&mut Self),
1292    {
1293        use MirScalarExpr::*;
1294        match self {
1295            Column(_, _) | Literal(_, _) | CallUnmaterializable(_) => (),
1296            CallUnary { expr, .. } => {
1297                f(expr);
1298            }
1299            CallBinary { expr1, expr2, .. } => {
1300                f(expr1);
1301                f(expr2);
1302            }
1303            CallVariadic { exprs, .. } => {
1304                for expr in exprs {
1305                    f(expr);
1306                }
1307            }
1308            If { cond, then, els } => {
1309                f(cond);
1310                f(then);
1311                f(els);
1312            }
1313        }
1314    }
1315
1316    fn try_visit_children<F, E>(&self, mut f: F) -> Result<(), E>
1317    where
1318        F: FnMut(&Self) -> Result<(), E>,
1319    {
1320        use MirScalarExpr::*;
1321        match self {
1322            Column(_, _) | Literal(_, _) | CallUnmaterializable(_) => (),
1323            CallUnary { expr, .. } => {
1324                f(expr)?;
1325            }
1326            CallBinary { expr1, expr2, .. } => {
1327                f(expr1)?;
1328                f(expr2)?;
1329            }
1330            CallVariadic { exprs, .. } => {
1331                for expr in exprs {
1332                    f(expr)?;
1333                }
1334            }
1335            If { cond, then, els } => {
1336                f(cond)?;
1337                f(then)?;
1338                f(els)?;
1339            }
1340        }
1341        Ok(())
1342    }
1343
1344    fn try_visit_mut_children<F, E>(&mut self, mut f: F) -> Result<(), E>
1345    where
1346        F: FnMut(&mut Self) -> Result<(), E>,
1347    {
1348        use MirScalarExpr::*;
1349        match self {
1350            Column(_, _) | Literal(_, _) | CallUnmaterializable(_) => (),
1351            CallUnary { expr, .. } => {
1352                f(expr)?;
1353            }
1354            CallBinary { expr1, expr2, .. } => {
1355                f(expr1)?;
1356                f(expr2)?;
1357            }
1358            CallVariadic { exprs, .. } => {
1359                for expr in exprs {
1360                    f(expr)?;
1361                }
1362            }
1363            If { cond, then, els } => {
1364                f(cond)?;
1365                f(then)?;
1366                f(els)?;
1367            }
1368        }
1369        Ok(())
1370    }
1371
1372    fn children<'a>(&'a self) -> impl DoubleEndedIterator<Item = &'a Self>
1373    where
1374        Self: 'a,
1375    {
1376        self.children()
1377    }
1378
1379    fn children_mut<'a>(&'a mut self) -> impl DoubleEndedIterator<Item = &'a mut Self>
1380    where
1381        Self: 'a,
1382    {
1383        self.children_mut()
1384    }
1385}
1386
1387impl MirScalarExpr {
1388    /// Iterates through references to child expressions.
1389    pub fn children(&self) -> impl DoubleEndedIterator<Item = &Self> {
1390        let mut first = None;
1391        let mut second = None;
1392        let mut third = None;
1393        let mut variadic = None;
1394
1395        use MirScalarExpr::*;
1396        match self {
1397            Column(_, _) | Literal(_, _) | CallUnmaterializable(_) => (),
1398            CallUnary { expr, .. } => {
1399                first = Some(&**expr);
1400            }
1401            CallBinary { expr1, expr2, .. } => {
1402                first = Some(&**expr1);
1403                second = Some(&**expr2);
1404            }
1405            CallVariadic { exprs, .. } => {
1406                variadic = Some(exprs);
1407            }
1408            If { cond, then, els } => {
1409                first = Some(&**cond);
1410                second = Some(&**then);
1411                third = Some(&**els);
1412            }
1413        }
1414
1415        first
1416            .into_iter()
1417            .chain(second)
1418            .chain(third)
1419            .chain(variadic.into_iter().flatten())
1420    }
1421
1422    /// Iterates through mutable references to child expressions.
1423    pub fn children_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Self> {
1424        let mut first = None;
1425        let mut second = None;
1426        let mut third = None;
1427        let mut variadic = None;
1428
1429        use MirScalarExpr::*;
1430        match self {
1431            Column(_, _) | Literal(_, _) | CallUnmaterializable(_) => (),
1432            CallUnary { expr, .. } => {
1433                first = Some(&mut **expr);
1434            }
1435            CallBinary { expr1, expr2, .. } => {
1436                first = Some(&mut **expr1);
1437                second = Some(&mut **expr2);
1438            }
1439            CallVariadic { exprs, .. } => {
1440                variadic = Some(exprs);
1441            }
1442            If { cond, then, els } => {
1443                first = Some(&mut **cond);
1444                second = Some(&mut **then);
1445                third = Some(&mut **els);
1446            }
1447        }
1448
1449        first
1450            .into_iter()
1451            .chain(second)
1452            .chain(third)
1453            .chain(variadic.into_iter().flatten())
1454    }
1455
1456    /// Visits all subexpressions in DFS preorder.
1457    pub fn visit_pre<F>(&self, mut f: F)
1458    where
1459        F: FnMut(&Self),
1460    {
1461        let mut worklist = vec![self];
1462        while let Some(e) = worklist.pop() {
1463            f(e);
1464            worklist.extend(e.children().rev());
1465        }
1466    }
1467
1468    /// Iterative pre-order visitor.
1469    pub fn visit_pre_mut<F: FnMut(&mut Self)>(&mut self, mut f: F) {
1470        let mut worklist = vec![self];
1471        while let Some(expr) = worklist.pop() {
1472            f(expr);
1473            worklist.extend(expr.children_mut().rev());
1474        }
1475    }
1476}
1477
1478/// Filter characteristics that are used for ordering join inputs.
1479/// This can be created for a `Vec<MirScalarExpr>`, which represents an AND of predicates.
1480///
1481/// The fields are ordered based on heuristic assumptions about their typical selectivity, so that
1482/// Ord gives the right ordering for join inputs. Bigger is better, i.e., will tend to come earlier
1483/// than other inputs.
1484#[derive(
1485    Eq,
1486    PartialEq,
1487    Ord,
1488    PartialOrd,
1489    Debug,
1490    Clone,
1491    Serialize,
1492    Deserialize,
1493    Hash
1494)]
1495pub struct FilterCharacteristics {
1496    // `<expr> = <literal>` appears in the filter.
1497    // Excludes cases where NOT appears anywhere above the literal equality.
1498    literal_equality: bool,
1499    // (Assuming a random string of lower-case characters, `LIKE 'a%'` has a selectivity of 1/26.)
1500    like: bool,
1501    is_null: bool,
1502    // Number of Vec elements that involve inequality predicates. (A BETWEEN is represented as two
1503    // inequality predicates.)
1504    // Excludes cases where NOT appears around the literal inequality.
1505    // Note that for inequality predicates, some databases assume 1/3 selectivity in the absence of
1506    // concrete statistics.
1507    literal_inequality: usize,
1508    /// Any filter, except ones involving `IS NOT NULL`, because those are too common.
1509    /// Can be true by itself, or any other field being true can also make this true.
1510    /// `NOT LIKE` is only in this category.
1511    /// `!=` is only in this category.
1512    /// `NOT (a = b)` is turned into `!=` by `reduce` before us!
1513    any_filter: bool,
1514}
1515
1516impl BitOrAssign for FilterCharacteristics {
1517    fn bitor_assign(&mut self, rhs: Self) {
1518        self.literal_equality |= rhs.literal_equality;
1519        self.like |= rhs.like;
1520        self.is_null |= rhs.is_null;
1521        self.literal_inequality += rhs.literal_inequality;
1522        self.any_filter |= rhs.any_filter;
1523    }
1524}
1525
1526impl FilterCharacteristics {
1527    pub fn none() -> FilterCharacteristics {
1528        FilterCharacteristics {
1529            literal_equality: false,
1530            like: false,
1531            is_null: false,
1532            literal_inequality: 0,
1533            any_filter: false,
1534        }
1535    }
1536
1537    pub fn explain(&self) -> String {
1538        let mut e = "".to_owned();
1539        if self.literal_equality {
1540            e.push_str("e");
1541        }
1542        if self.like {
1543            e.push_str("l");
1544        }
1545        if self.is_null {
1546            e.push_str("n");
1547        }
1548        for _ in 0..self.literal_inequality {
1549            e.push_str("i");
1550        }
1551        if self.any_filter {
1552            e.push_str("f");
1553        }
1554        e
1555    }
1556
1557    pub fn filter_characteristics(
1558        filters: &Vec<MirScalarExpr>,
1559    ) -> Result<FilterCharacteristics, RecursionLimitError> {
1560        let mut literal_equality = false;
1561        let mut like = false;
1562        let mut is_null = false;
1563        let mut literal_inequality = 0;
1564        let mut any_filter = false;
1565        filters.iter().try_for_each(|f| {
1566            let mut literal_inequality_in_current_filter = false;
1567            let mut is_not_null_in_current_filter = false;
1568            f.visit_pre_with_context(
1569                false,
1570                &mut |not_in_parent_chain, expr| {
1571                    not_in_parent_chain
1572                        || matches!(
1573                            expr,
1574                            MirScalarExpr::CallUnary {
1575                                func: UnaryFunc::Not(func::Not),
1576                                ..
1577                            }
1578                        )
1579                },
1580                &mut |not_in_parent_chain, expr| {
1581                    if !not_in_parent_chain {
1582                        if expr.any_expr_eq_literal().is_some() {
1583                            literal_equality = true;
1584                        }
1585                        if expr.any_expr_ineq_literal() {
1586                            literal_inequality_in_current_filter = true;
1587                        }
1588                        if matches!(
1589                            expr,
1590                            MirScalarExpr::CallUnary {
1591                                func: UnaryFunc::IsLikeMatch(_),
1592                                ..
1593                            }
1594                        ) {
1595                            like = true;
1596                        }
1597                    };
1598                    if matches!(
1599                        expr,
1600                        MirScalarExpr::CallUnary {
1601                            func: UnaryFunc::IsNull(crate::func::IsNull),
1602                            ..
1603                        }
1604                    ) {
1605                        if *not_in_parent_chain {
1606                            is_not_null_in_current_filter = true;
1607                        } else {
1608                            is_null = true;
1609                        }
1610                    }
1611                },
1612            );
1613            if literal_inequality_in_current_filter {
1614                literal_inequality += 1;
1615            }
1616            if !is_not_null_in_current_filter {
1617                // We want to ignore `IS NOT NULL` for `any_filter`.
1618                any_filter = true;
1619            }
1620            Ok(())
1621        })?;
1622        Ok(FilterCharacteristics {
1623            literal_equality,
1624            like,
1625            is_null,
1626            literal_inequality,
1627            any_filter,
1628        })
1629    }
1630
1631    pub fn add_literal_equality(&mut self) {
1632        self.literal_equality = true;
1633    }
1634
1635    pub fn worst_case_scaling_factor(&self) -> f64 {
1636        let mut factor = 1.0;
1637
1638        if self.literal_equality {
1639            factor *= 0.1;
1640        }
1641
1642        if self.is_null {
1643            factor *= 0.1;
1644        }
1645
1646        if self.literal_inequality >= 2 {
1647            factor *= 0.25;
1648        } else if self.literal_inequality == 1 {
1649            factor *= 0.33;
1650        }
1651
1652        // catch various negated filters, treat them pessimistically
1653        if !(self.literal_equality || self.is_null || self.literal_inequality > 0)
1654            && self.any_filter
1655        {
1656            factor *= 0.9;
1657        }
1658
1659        factor
1660    }
1661}
1662
1663#[derive(
1664    Ord,
1665    PartialOrd,
1666    Copy,
1667    Clone,
1668    Debug,
1669    Eq,
1670    PartialEq,
1671    Serialize,
1672    Deserialize,
1673    Hash
1674)]
1675#[cfg_attr(any(test, feature = "proptest"), derive(Arbitrary))]
1676pub enum DomainLimit {
1677    None,
1678    Inclusive(i64),
1679    Exclusive(i64),
1680}
1681
1682impl RustType<ProtoDomainLimit> for DomainLimit {
1683    fn into_proto(&self) -> ProtoDomainLimit {
1684        use proto_domain_limit::Kind::*;
1685        let kind = match self {
1686            DomainLimit::None => None(()),
1687            DomainLimit::Inclusive(v) => Inclusive(*v),
1688            DomainLimit::Exclusive(v) => Exclusive(*v),
1689        };
1690        ProtoDomainLimit { kind: Some(kind) }
1691    }
1692
1693    fn from_proto(proto: ProtoDomainLimit) -> Result<Self, TryFromProtoError> {
1694        use proto_domain_limit::Kind::*;
1695        if let Some(kind) = proto.kind {
1696            match kind {
1697                None(()) => Ok(DomainLimit::None),
1698                Inclusive(v) => Ok(DomainLimit::Inclusive(v)),
1699                Exclusive(v) => Ok(DomainLimit::Exclusive(v)),
1700            }
1701        } else {
1702            Err(TryFromProtoError::missing_field("ProtoDomainLimit::kind"))
1703        }
1704    }
1705}
1706
1707#[derive(
1708    Ord,
1709    PartialOrd,
1710    Clone,
1711    Debug,
1712    Eq,
1713    PartialEq,
1714    Serialize,
1715    Deserialize,
1716    Hash
1717)]
1718#[cfg_attr(any(test, feature = "proptest"), derive(Arbitrary))]
1719pub enum EvalError {
1720    CharacterNotValidForEncoding(i32),
1721    CharacterTooLargeForEncoding(i32),
1722    DateBinOutOfRange(Box<str>),
1723    DivisionByZero,
1724    Unsupported {
1725        feature: Box<str>,
1726        discussion_no: Option<usize>,
1727    },
1728    FloatOverflow,
1729    FloatUnderflow,
1730    NumericFieldOverflow,
1731    Float32OutOfRange(Box<str>),
1732    Float64OutOfRange(Box<str>),
1733    Int16OutOfRange(Box<str>),
1734    Int32OutOfRange(Box<str>),
1735    Int64OutOfRange(Box<str>),
1736    UInt16OutOfRange(Box<str>),
1737    UInt32OutOfRange(Box<str>),
1738    UInt64OutOfRange(Box<str>),
1739    MzTimestampOutOfRange(Box<str>),
1740    MzTimestampStepOverflow,
1741    OidOutOfRange(Box<str>),
1742    IntervalOutOfRange(Box<str>),
1743    TimestampCannotBeNan,
1744    TimestampOutOfRange,
1745    DateOutOfRange,
1746    CharOutOfRange,
1747    IndexOutOfRange {
1748        provided: i32,
1749        // The last valid index position, i.e. `v.len() - 1`
1750        valid_end: i32,
1751    },
1752    InvalidBase64Equals,
1753    InvalidBase64Symbol(char),
1754    InvalidBase64EndSequence,
1755    InvalidTimezone(Box<str>),
1756    InvalidTimezoneInterval,
1757    InvalidTimezoneConversion,
1758    InvalidIanaTimezoneId(Box<str>),
1759    InvalidLayer {
1760        max_layer: usize,
1761        val: i64,
1762    },
1763    InvalidArray(InvalidArrayError),
1764    InvalidEncodingName(Box<str>),
1765    InvalidHashAlgorithm(Box<str>),
1766    InvalidByteSequence {
1767        byte_sequence: Box<str>,
1768        encoding_name: Box<str>,
1769    },
1770    InvalidJsonbCast {
1771        from: Box<str>,
1772        to: Box<str>,
1773    },
1774    InvalidRegex(Box<str>),
1775    InvalidRegexFlag(char),
1776    InvalidParameterValue(Box<str>),
1777    InvalidDatePart(Box<str>),
1778    KeyCannotBeNull,
1779    NegSqrt,
1780    NegLimit,
1781    NullCharacterNotPermitted,
1782    UnknownUnits(Box<str>),
1783    UnsupportedUnits(Box<str>, Box<str>),
1784    UnterminatedLikeEscapeSequence,
1785    Parse(ParseError),
1786    ParseHex(ParseHexError),
1787    Internal(Box<str>),
1788    InfinityOutOfDomain(Box<str>),
1789    NegativeOutOfDomain(Box<str>),
1790    ZeroOutOfDomain(Box<str>),
1791    OutOfDomain(DomainLimit, DomainLimit, Box<str>),
1792    ComplexOutOfRange(Box<str>),
1793    MultipleRowsFromSubquery,
1794    NegativeRowsFromSubquery,
1795    Undefined(Box<str>),
1796    LikePatternTooLong,
1797    LikeEscapeTooLong,
1798    StringValueTooLong {
1799        target_type: Box<str>,
1800        length: usize,
1801    },
1802    MultidimensionalArrayRemovalNotSupported,
1803    IncompatibleArrayDimensions {
1804        dims: Option<(usize, usize)>,
1805    },
1806    TypeFromOid(Box<str>),
1807    InvalidRange(InvalidRangeError),
1808    InvalidRoleId(Box<str>),
1809    InvalidPrivileges(Box<str>),
1810    InvalidCatalogJson(Box<str>),
1811    LetRecLimitExceeded(Box<str>),
1812    MultiDimensionalArraySearch,
1813    MustNotBeNull(Box<str>),
1814    InvalidIdentifier {
1815        ident: Box<str>,
1816        detail: Option<Box<str>>,
1817    },
1818    ArrayFillWrongArraySubscripts,
1819    // TODO: propagate this check more widely throughout the expr crate
1820    MaxArraySizeExceeded(usize),
1821    DateDiffOverflow {
1822        unit: Box<str>,
1823        a: Box<str>,
1824        b: Box<str>,
1825    },
1826    // The error for ErrorIfNull; this should not be used in other contexts as a generic error
1827    // printer.
1828    IfNullError(Box<str>),
1829    LengthTooLarge,
1830    AclArrayNullElement,
1831    MzAclArrayNullElement,
1832    PrettyError(Box<str>),
1833    RedactError(Box<str>),
1834}
1835
1836impl fmt::Display for EvalError {
1837    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1838        match self {
1839            EvalError::CharacterNotValidForEncoding(v) => {
1840                write!(f, "requested character not valid for encoding: {v}")
1841            }
1842            EvalError::CharacterTooLargeForEncoding(v) => {
1843                write!(f, "requested character too large for encoding: {v}")
1844            }
1845            EvalError::DateBinOutOfRange(message) => f.write_str(message),
1846            EvalError::DivisionByZero => f.write_str("division by zero"),
1847            EvalError::Unsupported {
1848                feature,
1849                discussion_no,
1850            } => {
1851                write!(f, "{} not yet supported", feature)?;
1852                if let Some(discussion_no) = discussion_no {
1853                    write!(
1854                        f,
1855                        ", see https://github.com/MaterializeInc/materialize/discussions/{} for more details",
1856                        discussion_no
1857                    )?;
1858                }
1859                Ok(())
1860            }
1861            EvalError::FloatOverflow => f.write_str("value out of range: overflow"),
1862            EvalError::FloatUnderflow => f.write_str("value out of range: underflow"),
1863            EvalError::NumericFieldOverflow => f.write_str("numeric field overflow"),
1864            EvalError::Float32OutOfRange(val) => write!(f, "{} real out of range", val.quoted()),
1865            EvalError::Float64OutOfRange(val) => {
1866                write!(f, "{} double precision out of range", val.quoted())
1867            }
1868            EvalError::Int16OutOfRange(val) => write!(f, "{} smallint out of range", val.quoted()),
1869            EvalError::Int32OutOfRange(val) => write!(f, "{} integer out of range", val.quoted()),
1870            EvalError::Int64OutOfRange(val) => write!(f, "{} bigint out of range", val.quoted()),
1871            EvalError::UInt16OutOfRange(val) => write!(f, "{} uint2 out of range", val.quoted()),
1872            EvalError::UInt32OutOfRange(val) => write!(f, "{} uint4 out of range", val.quoted()),
1873            EvalError::UInt64OutOfRange(val) => write!(f, "{} uint8 out of range", val.quoted()),
1874            EvalError::MzTimestampOutOfRange(val) => {
1875                write!(f, "{} mz_timestamp out of range", val.quoted())
1876            }
1877            EvalError::MzTimestampStepOverflow => f.write_str("step mz_timestamp overflow"),
1878            EvalError::OidOutOfRange(val) => write!(f, "{} OID out of range", val.quoted()),
1879            EvalError::IntervalOutOfRange(val) => {
1880                write!(f, "{} interval out of range", val.quoted())
1881            }
1882            EvalError::TimestampCannotBeNan => f.write_str("timestamp cannot be NaN"),
1883            EvalError::TimestampOutOfRange => f.write_str("timestamp out of range"),
1884            EvalError::DateOutOfRange => f.write_str("date out of range"),
1885            EvalError::CharOutOfRange => f.write_str("\"char\" out of range"),
1886            EvalError::IndexOutOfRange {
1887                provided,
1888                valid_end,
1889            } => write!(f, "index {provided} out of valid range, 0..{valid_end}",),
1890            EvalError::InvalidBase64Equals => {
1891                f.write_str("unexpected \"=\" while decoding base64 sequence")
1892            }
1893            EvalError::InvalidBase64Symbol(c) => write!(
1894                f,
1895                "invalid symbol \"{}\" found while decoding base64 sequence",
1896                c.escape_default()
1897            ),
1898            EvalError::InvalidBase64EndSequence => f.write_str("invalid base64 end sequence"),
1899            EvalError::InvalidJsonbCast { from, to } => {
1900                write!(f, "cannot cast jsonb {} to type {}", from, to)
1901            }
1902            EvalError::InvalidTimezone(tz) => write!(f, "invalid time zone '{}'", tz),
1903            EvalError::InvalidTimezoneInterval => {
1904                f.write_str("timezone interval must not contain months or years")
1905            }
1906            EvalError::InvalidTimezoneConversion => f.write_str("invalid timezone conversion"),
1907            EvalError::InvalidIanaTimezoneId(tz) => {
1908                write!(f, "invalid IANA Time Zone Database identifier: '{}'", tz)
1909            }
1910            EvalError::InvalidLayer { max_layer, val } => write!(
1911                f,
1912                "invalid layer: {}; must use value within [1, {}]",
1913                val, max_layer
1914            ),
1915            EvalError::InvalidArray(e) => e.fmt(f),
1916            EvalError::InvalidEncodingName(name) => write!(f, "invalid encoding name '{}'", name),
1917            EvalError::InvalidHashAlgorithm(alg) => write!(f, "invalid hash algorithm '{}'", alg),
1918            EvalError::InvalidByteSequence {
1919                byte_sequence,
1920                encoding_name,
1921            } => write!(
1922                f,
1923                "invalid byte sequence '{}' for encoding '{}'",
1924                byte_sequence, encoding_name
1925            ),
1926            EvalError::InvalidDatePart(part) => write!(f, "invalid datepart {}", part.quoted()),
1927            EvalError::KeyCannotBeNull => f.write_str("key cannot be null"),
1928            EvalError::NegSqrt => f.write_str("cannot take square root of a negative number"),
1929            EvalError::NegLimit => f.write_str("LIMIT must not be negative"),
1930            EvalError::NullCharacterNotPermitted => f.write_str("null character not permitted"),
1931            EvalError::InvalidRegex(e) => write!(f, "invalid regular expression: {}", e),
1932            EvalError::InvalidRegexFlag(c) => write!(f, "invalid regular expression flag: {}", c),
1933            EvalError::InvalidParameterValue(s) => f.write_str(s),
1934            EvalError::UnknownUnits(units) => write!(f, "unit '{}' not recognized", units),
1935            EvalError::UnsupportedUnits(units, typ) => {
1936                write!(f, "unit '{}' not supported for type {}", units, typ)
1937            }
1938            EvalError::UnterminatedLikeEscapeSequence => {
1939                f.write_str("unterminated escape sequence in LIKE")
1940            }
1941            EvalError::Parse(e) => e.fmt(f),
1942            EvalError::PrettyError(e) => e.fmt(f),
1943            EvalError::RedactError(e) => e.fmt(f),
1944            EvalError::ParseHex(e) => e.fmt(f),
1945            EvalError::Internal(s) => write!(f, "internal error: {}", s),
1946            EvalError::InfinityOutOfDomain(s) => {
1947                write!(f, "function {} is only defined for finite arguments", s)
1948            }
1949            EvalError::NegativeOutOfDomain(s) => {
1950                write!(f, "function {} is not defined for negative numbers", s)
1951            }
1952            EvalError::ZeroOutOfDomain(s) => {
1953                write!(f, "function {} is not defined for zero", s)
1954            }
1955            EvalError::OutOfDomain(lower, upper, s) => {
1956                use DomainLimit::*;
1957                write!(f, "function {s} is defined for numbers ")?;
1958                match (lower, upper) {
1959                    (Inclusive(n), None) => write!(f, "greater than or equal to {n}"),
1960                    (Exclusive(n), None) => write!(f, "greater than {n}"),
1961                    (None, Inclusive(n)) => write!(f, "less than or equal to {n}"),
1962                    (None, Exclusive(n)) => write!(f, "less than {n}"),
1963                    (Inclusive(lo), Inclusive(hi)) => write!(f, "between {lo} and {hi} inclusive"),
1964                    (Exclusive(lo), Exclusive(hi)) => write!(f, "between {lo} and {hi} exclusive"),
1965                    (Inclusive(lo), Exclusive(hi)) => {
1966                        write!(f, "between {lo} inclusive and {hi} exclusive")
1967                    }
1968                    (Exclusive(lo), Inclusive(hi)) => {
1969                        write!(f, "between {lo} exclusive and {hi} inclusive")
1970                    }
1971                    (None, None) => panic!("invalid domain error"),
1972                }
1973            }
1974            EvalError::ComplexOutOfRange(s) => {
1975                write!(f, "function {} cannot return complex numbers", s)
1976            }
1977            EvalError::MultipleRowsFromSubquery => {
1978                write!(f, "more than one record produced in subquery")
1979            }
1980            EvalError::NegativeRowsFromSubquery => {
1981                write!(f, "negative number of rows produced in subquery")
1982            }
1983            EvalError::Undefined(s) => {
1984                write!(f, "{} is undefined", s)
1985            }
1986            EvalError::LikePatternTooLong => {
1987                write!(f, "LIKE pattern exceeds maximum length")
1988            }
1989            EvalError::LikeEscapeTooLong => {
1990                write!(f, "invalid escape string")
1991            }
1992            EvalError::StringValueTooLong {
1993                target_type,
1994                length,
1995            } => {
1996                write!(f, "value too long for type {}({})", target_type, length)
1997            }
1998            EvalError::MultidimensionalArrayRemovalNotSupported => {
1999                write!(
2000                    f,
2001                    "removing elements from multidimensional arrays is not supported"
2002                )
2003            }
2004            EvalError::IncompatibleArrayDimensions { dims: _ } => {
2005                write!(f, "cannot concatenate incompatible arrays")
2006            }
2007            EvalError::TypeFromOid(msg) => write!(f, "{msg}"),
2008            EvalError::InvalidRange(e) => e.fmt(f),
2009            EvalError::InvalidRoleId(msg) => write!(f, "{msg}"),
2010            EvalError::InvalidPrivileges(privilege) => {
2011                write!(f, "unrecognized privilege type: {privilege}")
2012            }
2013            EvalError::InvalidCatalogJson(msg) => {
2014                write!(f, "invalid catalog JSON: {msg}")
2015            }
2016            EvalError::LetRecLimitExceeded(max_iters) => {
2017                write!(
2018                    f,
2019                    "Recursive query exceeded the recursion limit {}. (Use RETURN AT RECURSION LIMIT to not error, but return the current state as the final result when reaching the limit.)",
2020                    max_iters
2021                )
2022            }
2023            EvalError::MultiDimensionalArraySearch => write!(
2024                f,
2025                "searching for elements in multidimensional arrays is not supported"
2026            ),
2027            EvalError::MustNotBeNull(v) => write!(f, "{v} must not be null"),
2028            EvalError::InvalidIdentifier { ident, .. } => {
2029                write!(f, "string is not a valid identifier: {}", ident.quoted())
2030            }
2031            EvalError::ArrayFillWrongArraySubscripts => {
2032                f.write_str("wrong number of array subscripts")
2033            }
2034            EvalError::MaxArraySizeExceeded(max_size) => {
2035                write!(
2036                    f,
2037                    "array size exceeds the maximum allowed ({max_size} bytes)"
2038                )
2039            }
2040            EvalError::DateDiffOverflow { unit, a, b } => {
2041                write!(f, "datediff overflow, {unit} of {a}, {b}")
2042            }
2043            EvalError::IfNullError(s) => f.write_str(s),
2044            EvalError::LengthTooLarge => write!(f, "requested length too large"),
2045            EvalError::AclArrayNullElement => write!(f, "ACL arrays must not contain null values"),
2046            EvalError::MzAclArrayNullElement => {
2047                write!(f, "MZ_ACL arrays must not contain null values")
2048            }
2049        }
2050    }
2051}
2052
2053impl EvalError {
2054    pub fn detail(&self) -> Option<String> {
2055        match self {
2056            EvalError::IncompatibleArrayDimensions { dims: None } => Some(
2057                "Arrays with differing dimensions are not compatible for concatenation.".into(),
2058            ),
2059            EvalError::IncompatibleArrayDimensions {
2060                dims: Some((a_dims, b_dims)),
2061            } => Some(format!(
2062                "Arrays of {} and {} dimensions are not compatible for concatenation.",
2063                a_dims, b_dims
2064            )),
2065            EvalError::InvalidIdentifier { detail, .. } => detail.as_deref().map(Into::into),
2066            EvalError::ArrayFillWrongArraySubscripts => {
2067                Some("Low bound array has different size than dimensions array.".into())
2068            }
2069            _ => None,
2070        }
2071    }
2072
2073    pub fn hint(&self) -> Option<String> {
2074        match self {
2075            EvalError::InvalidBase64EndSequence => Some(
2076                "Input data is missing padding, is truncated, or is otherwise corrupted.".into(),
2077            ),
2078            EvalError::LikeEscapeTooLong => {
2079                Some("Escape string must be empty or one character.".into())
2080            }
2081            EvalError::MzTimestampOutOfRange(_) => Some(
2082                "Integer, numeric, and text casts to mz_timestamp must be in the form of whole \
2083                milliseconds since the Unix epoch. Values with fractional parts cannot be \
2084                converted to mz_timestamp."
2085                    .into(),
2086            ),
2087            _ => None,
2088        }
2089    }
2090}
2091
2092impl std::error::Error for EvalError {}
2093
2094impl From<ParseError> for EvalError {
2095    fn from(e: ParseError) -> EvalError {
2096        EvalError::Parse(e)
2097    }
2098}
2099
2100impl From<ParseHexError> for EvalError {
2101    fn from(e: ParseHexError) -> EvalError {
2102        EvalError::ParseHex(e)
2103    }
2104}
2105
2106impl From<InvalidArrayError> for EvalError {
2107    fn from(e: InvalidArrayError) -> EvalError {
2108        EvalError::InvalidArray(e)
2109    }
2110}
2111
2112impl From<RegexCompilationError> for EvalError {
2113    fn from(e: RegexCompilationError) -> EvalError {
2114        EvalError::InvalidRegex(e.to_string().into())
2115    }
2116}
2117
2118impl From<TypeFromOidError> for EvalError {
2119    fn from(e: TypeFromOidError) -> EvalError {
2120        EvalError::TypeFromOid(e.to_string().into())
2121    }
2122}
2123
2124impl From<DateError> for EvalError {
2125    fn from(e: DateError) -> EvalError {
2126        match e {
2127            DateError::OutOfRange => EvalError::DateOutOfRange,
2128        }
2129    }
2130}
2131
2132impl From<TimestampError> for EvalError {
2133    fn from(e: TimestampError) -> EvalError {
2134        match e {
2135            TimestampError::OutOfRange => EvalError::TimestampOutOfRange,
2136        }
2137    }
2138}
2139
2140impl From<InvalidRangeError> for EvalError {
2141    fn from(e: InvalidRangeError) -> EvalError {
2142        EvalError::InvalidRange(e)
2143    }
2144}
2145
2146impl RustType<ProtoEvalError> for EvalError {
2147    fn into_proto(&self) -> ProtoEvalError {
2148        use proto_eval_error::Kind::*;
2149        use proto_eval_error::*;
2150        let kind = match self {
2151            EvalError::CharacterNotValidForEncoding(v) => CharacterNotValidForEncoding(*v),
2152            EvalError::CharacterTooLargeForEncoding(v) => CharacterTooLargeForEncoding(*v),
2153            EvalError::DateBinOutOfRange(v) => DateBinOutOfRange(v.into_proto()),
2154            EvalError::DivisionByZero => DivisionByZero(()),
2155            EvalError::Unsupported {
2156                feature,
2157                discussion_no,
2158            } => Unsupported(ProtoUnsupported {
2159                feature: feature.into_proto(),
2160                discussion_no: discussion_no.into_proto(),
2161            }),
2162            EvalError::FloatOverflow => FloatOverflow(()),
2163            EvalError::FloatUnderflow => FloatUnderflow(()),
2164            EvalError::NumericFieldOverflow => NumericFieldOverflow(()),
2165            EvalError::Float32OutOfRange(val) => Float32OutOfRange(ProtoValueOutOfRange {
2166                value: val.to_string(),
2167            }),
2168            EvalError::Float64OutOfRange(val) => Float64OutOfRange(ProtoValueOutOfRange {
2169                value: val.to_string(),
2170            }),
2171            EvalError::Int16OutOfRange(val) => Int16OutOfRange(ProtoValueOutOfRange {
2172                value: val.to_string(),
2173            }),
2174            EvalError::Int32OutOfRange(val) => Int32OutOfRange(ProtoValueOutOfRange {
2175                value: val.to_string(),
2176            }),
2177            EvalError::Int64OutOfRange(val) => Int64OutOfRange(ProtoValueOutOfRange {
2178                value: val.to_string(),
2179            }),
2180            EvalError::UInt16OutOfRange(val) => Uint16OutOfRange(ProtoValueOutOfRange {
2181                value: val.to_string(),
2182            }),
2183            EvalError::UInt32OutOfRange(val) => Uint32OutOfRange(ProtoValueOutOfRange {
2184                value: val.to_string(),
2185            }),
2186            EvalError::UInt64OutOfRange(val) => Uint64OutOfRange(ProtoValueOutOfRange {
2187                value: val.to_string(),
2188            }),
2189            EvalError::MzTimestampOutOfRange(val) => MzTimestampOutOfRange(ProtoValueOutOfRange {
2190                value: val.to_string(),
2191            }),
2192            EvalError::MzTimestampStepOverflow => MzTimestampStepOverflow(()),
2193            EvalError::OidOutOfRange(val) => OidOutOfRange(ProtoValueOutOfRange {
2194                value: val.to_string(),
2195            }),
2196            EvalError::IntervalOutOfRange(val) => IntervalOutOfRange(ProtoValueOutOfRange {
2197                value: val.to_string(),
2198            }),
2199            EvalError::TimestampCannotBeNan => TimestampCannotBeNan(()),
2200            EvalError::TimestampOutOfRange => TimestampOutOfRange(()),
2201            EvalError::DateOutOfRange => DateOutOfRange(()),
2202            EvalError::CharOutOfRange => CharOutOfRange(()),
2203            EvalError::IndexOutOfRange {
2204                provided,
2205                valid_end,
2206            } => IndexOutOfRange(ProtoIndexOutOfRange {
2207                provided: *provided,
2208                valid_end: *valid_end,
2209            }),
2210            EvalError::InvalidBase64Equals => InvalidBase64Equals(()),
2211            EvalError::InvalidBase64Symbol(sym) => InvalidBase64Symbol(sym.into_proto()),
2212            EvalError::InvalidBase64EndSequence => InvalidBase64EndSequence(()),
2213            EvalError::InvalidTimezone(tz) => InvalidTimezone(tz.into_proto()),
2214            EvalError::InvalidTimezoneInterval => InvalidTimezoneInterval(()),
2215            EvalError::InvalidTimezoneConversion => InvalidTimezoneConversion(()),
2216            EvalError::InvalidLayer { max_layer, val } => InvalidLayer(ProtoInvalidLayer {
2217                max_layer: max_layer.into_proto(),
2218                val: *val,
2219            }),
2220            EvalError::InvalidArray(error) => InvalidArray(error.into_proto()),
2221            EvalError::InvalidEncodingName(v) => InvalidEncodingName(v.into_proto()),
2222            EvalError::InvalidHashAlgorithm(v) => InvalidHashAlgorithm(v.into_proto()),
2223            EvalError::InvalidByteSequence {
2224                byte_sequence,
2225                encoding_name,
2226            } => InvalidByteSequence(ProtoInvalidByteSequence {
2227                byte_sequence: byte_sequence.into_proto(),
2228                encoding_name: encoding_name.into_proto(),
2229            }),
2230            EvalError::InvalidJsonbCast { from, to } => InvalidJsonbCast(ProtoInvalidJsonbCast {
2231                from: from.into_proto(),
2232                to: to.into_proto(),
2233            }),
2234            EvalError::InvalidRegex(v) => InvalidRegex(v.into_proto()),
2235            EvalError::InvalidRegexFlag(v) => InvalidRegexFlag(v.into_proto()),
2236            EvalError::InvalidParameterValue(v) => InvalidParameterValue(v.into_proto()),
2237            EvalError::InvalidDatePart(part) => InvalidDatePart(part.into_proto()),
2238            EvalError::KeyCannotBeNull => KeyCannotBeNull(()),
2239            EvalError::NegSqrt => NegSqrt(()),
2240            EvalError::NegLimit => NegLimit(()),
2241            EvalError::NullCharacterNotPermitted => NullCharacterNotPermitted(()),
2242            EvalError::UnknownUnits(v) => UnknownUnits(v.into_proto()),
2243            EvalError::UnsupportedUnits(units, typ) => UnsupportedUnits(ProtoUnsupportedUnits {
2244                units: units.into_proto(),
2245                typ: typ.into_proto(),
2246            }),
2247            EvalError::UnterminatedLikeEscapeSequence => UnterminatedLikeEscapeSequence(()),
2248            EvalError::Parse(error) => Parse(error.into_proto()),
2249            EvalError::PrettyError(error) => PrettyError(error.into_proto()),
2250            EvalError::RedactError(error) => RedactError(error.into_proto()),
2251            EvalError::ParseHex(error) => ParseHex(error.into_proto()),
2252            EvalError::Internal(v) => Internal(v.into_proto()),
2253            EvalError::InfinityOutOfDomain(v) => InfinityOutOfDomain(v.into_proto()),
2254            EvalError::NegativeOutOfDomain(v) => NegativeOutOfDomain(v.into_proto()),
2255            EvalError::ZeroOutOfDomain(v) => ZeroOutOfDomain(v.into_proto()),
2256            EvalError::OutOfDomain(lower, upper, id) => OutOfDomain(ProtoOutOfDomain {
2257                lower: Some(lower.into_proto()),
2258                upper: Some(upper.into_proto()),
2259                id: id.into_proto(),
2260            }),
2261            EvalError::ComplexOutOfRange(v) => ComplexOutOfRange(v.into_proto()),
2262            EvalError::MultipleRowsFromSubquery => MultipleRowsFromSubquery(()),
2263            EvalError::NegativeRowsFromSubquery => NegativeRowsFromSubquery(()),
2264            EvalError::Undefined(v) => Undefined(v.into_proto()),
2265            EvalError::LikePatternTooLong => LikePatternTooLong(()),
2266            EvalError::LikeEscapeTooLong => LikeEscapeTooLong(()),
2267            EvalError::StringValueTooLong {
2268                target_type,
2269                length,
2270            } => StringValueTooLong(ProtoStringValueTooLong {
2271                target_type: target_type.into_proto(),
2272                length: length.into_proto(),
2273            }),
2274            EvalError::MultidimensionalArrayRemovalNotSupported => {
2275                MultidimensionalArrayRemovalNotSupported(())
2276            }
2277            EvalError::IncompatibleArrayDimensions { dims } => {
2278                IncompatibleArrayDimensions(ProtoIncompatibleArrayDimensions {
2279                    dims: dims.into_proto(),
2280                })
2281            }
2282            EvalError::TypeFromOid(v) => TypeFromOid(v.into_proto()),
2283            EvalError::InvalidRange(error) => InvalidRange(error.into_proto()),
2284            EvalError::InvalidRoleId(v) => InvalidRoleId(v.into_proto()),
2285            EvalError::InvalidPrivileges(v) => InvalidPrivileges(v.into_proto()),
2286            EvalError::InvalidCatalogJson(v) => InvalidCatalogJson(v.into_proto()),
2287            EvalError::LetRecLimitExceeded(v) => WmrRecursionLimitExceeded(v.into_proto()),
2288            EvalError::MultiDimensionalArraySearch => MultiDimensionalArraySearch(()),
2289            EvalError::MustNotBeNull(v) => MustNotBeNull(v.into_proto()),
2290            EvalError::InvalidIdentifier { ident, detail } => {
2291                InvalidIdentifier(ProtoInvalidIdentifier {
2292                    ident: ident.into_proto(),
2293                    detail: detail.into_proto(),
2294                })
2295            }
2296            EvalError::ArrayFillWrongArraySubscripts => ArrayFillWrongArraySubscripts(()),
2297            EvalError::MaxArraySizeExceeded(max_size) => {
2298                MaxArraySizeExceeded(u64::cast_from(*max_size))
2299            }
2300            EvalError::DateDiffOverflow { unit, a, b } => DateDiffOverflow(ProtoDateDiffOverflow {
2301                unit: unit.into_proto(),
2302                a: a.into_proto(),
2303                b: b.into_proto(),
2304            }),
2305            EvalError::IfNullError(s) => IfNullError(s.into_proto()),
2306            EvalError::LengthTooLarge => LengthTooLarge(()),
2307            EvalError::AclArrayNullElement => AclArrayNullElement(()),
2308            EvalError::MzAclArrayNullElement => MzAclArrayNullElement(()),
2309            EvalError::InvalidIanaTimezoneId(s) => InvalidIanaTimezoneId(s.into_proto()),
2310        };
2311        ProtoEvalError { kind: Some(kind) }
2312    }
2313
2314    fn from_proto(proto: ProtoEvalError) -> Result<Self, TryFromProtoError> {
2315        use proto_eval_error::Kind::*;
2316        match proto.kind {
2317            Some(kind) => match kind {
2318                CharacterNotValidForEncoding(v) => Ok(EvalError::CharacterNotValidForEncoding(v)),
2319                CharacterTooLargeForEncoding(v) => Ok(EvalError::CharacterTooLargeForEncoding(v)),
2320                DateBinOutOfRange(v) => Ok(EvalError::DateBinOutOfRange(v.into())),
2321                DivisionByZero(()) => Ok(EvalError::DivisionByZero),
2322                Unsupported(v) => Ok(EvalError::Unsupported {
2323                    feature: v.feature.into(),
2324                    discussion_no: v.discussion_no.into_rust()?,
2325                }),
2326                FloatOverflow(()) => Ok(EvalError::FloatOverflow),
2327                FloatUnderflow(()) => Ok(EvalError::FloatUnderflow),
2328                NumericFieldOverflow(()) => Ok(EvalError::NumericFieldOverflow),
2329                Float32OutOfRange(val) => Ok(EvalError::Float32OutOfRange(val.value.into())),
2330                Float64OutOfRange(val) => Ok(EvalError::Float64OutOfRange(val.value.into())),
2331                Int16OutOfRange(val) => Ok(EvalError::Int16OutOfRange(val.value.into())),
2332                Int32OutOfRange(val) => Ok(EvalError::Int32OutOfRange(val.value.into())),
2333                Int64OutOfRange(val) => Ok(EvalError::Int64OutOfRange(val.value.into())),
2334                Uint16OutOfRange(val) => Ok(EvalError::UInt16OutOfRange(val.value.into())),
2335                Uint32OutOfRange(val) => Ok(EvalError::UInt32OutOfRange(val.value.into())),
2336                Uint64OutOfRange(val) => Ok(EvalError::UInt64OutOfRange(val.value.into())),
2337                MzTimestampOutOfRange(val) => {
2338                    Ok(EvalError::MzTimestampOutOfRange(val.value.into()))
2339                }
2340                MzTimestampStepOverflow(()) => Ok(EvalError::MzTimestampStepOverflow),
2341                OidOutOfRange(val) => Ok(EvalError::OidOutOfRange(val.value.into())),
2342                IntervalOutOfRange(val) => Ok(EvalError::IntervalOutOfRange(val.value.into())),
2343                TimestampCannotBeNan(()) => Ok(EvalError::TimestampCannotBeNan),
2344                TimestampOutOfRange(()) => Ok(EvalError::TimestampOutOfRange),
2345                DateOutOfRange(()) => Ok(EvalError::DateOutOfRange),
2346                CharOutOfRange(()) => Ok(EvalError::CharOutOfRange),
2347                IndexOutOfRange(v) => Ok(EvalError::IndexOutOfRange {
2348                    provided: v.provided,
2349                    valid_end: v.valid_end,
2350                }),
2351                InvalidBase64Equals(()) => Ok(EvalError::InvalidBase64Equals),
2352                InvalidBase64Symbol(v) => char::from_proto(v).map(EvalError::InvalidBase64Symbol),
2353                InvalidBase64EndSequence(()) => Ok(EvalError::InvalidBase64EndSequence),
2354                InvalidTimezone(v) => Ok(EvalError::InvalidTimezone(v.into())),
2355                InvalidTimezoneInterval(()) => Ok(EvalError::InvalidTimezoneInterval),
2356                InvalidTimezoneConversion(()) => Ok(EvalError::InvalidTimezoneConversion),
2357                InvalidLayer(v) => Ok(EvalError::InvalidLayer {
2358                    max_layer: usize::from_proto(v.max_layer)?,
2359                    val: v.val,
2360                }),
2361                InvalidArray(error) => Ok(EvalError::InvalidArray(error.into_rust()?)),
2362                InvalidEncodingName(v) => Ok(EvalError::InvalidEncodingName(v.into())),
2363                InvalidHashAlgorithm(v) => Ok(EvalError::InvalidHashAlgorithm(v.into())),
2364                InvalidByteSequence(v) => Ok(EvalError::InvalidByteSequence {
2365                    byte_sequence: v.byte_sequence.into(),
2366                    encoding_name: v.encoding_name.into(),
2367                }),
2368                InvalidJsonbCast(v) => Ok(EvalError::InvalidJsonbCast {
2369                    from: v.from.into(),
2370                    to: v.to.into(),
2371                }),
2372                InvalidRegex(v) => Ok(EvalError::InvalidRegex(v.into())),
2373                InvalidRegexFlag(v) => Ok(EvalError::InvalidRegexFlag(char::from_proto(v)?)),
2374                InvalidParameterValue(v) => Ok(EvalError::InvalidParameterValue(v.into())),
2375                InvalidDatePart(part) => Ok(EvalError::InvalidDatePart(part.into())),
2376                KeyCannotBeNull(()) => Ok(EvalError::KeyCannotBeNull),
2377                NegSqrt(()) => Ok(EvalError::NegSqrt),
2378                NegLimit(()) => Ok(EvalError::NegLimit),
2379                NullCharacterNotPermitted(()) => Ok(EvalError::NullCharacterNotPermitted),
2380                UnknownUnits(v) => Ok(EvalError::UnknownUnits(v.into())),
2381                UnsupportedUnits(v) => {
2382                    Ok(EvalError::UnsupportedUnits(v.units.into(), v.typ.into()))
2383                }
2384                UnterminatedLikeEscapeSequence(()) => Ok(EvalError::UnterminatedLikeEscapeSequence),
2385                Parse(error) => Ok(EvalError::Parse(error.into_rust()?)),
2386                ParseHex(error) => Ok(EvalError::ParseHex(error.into_rust()?)),
2387                Internal(v) => Ok(EvalError::Internal(v.into())),
2388                InfinityOutOfDomain(v) => Ok(EvalError::InfinityOutOfDomain(v.into())),
2389                NegativeOutOfDomain(v) => Ok(EvalError::NegativeOutOfDomain(v.into())),
2390                ZeroOutOfDomain(v) => Ok(EvalError::ZeroOutOfDomain(v.into())),
2391                OutOfDomain(v) => Ok(EvalError::OutOfDomain(
2392                    v.lower.into_rust_if_some("ProtoDomainLimit::lower")?,
2393                    v.upper.into_rust_if_some("ProtoDomainLimit::upper")?,
2394                    v.id.into(),
2395                )),
2396                ComplexOutOfRange(v) => Ok(EvalError::ComplexOutOfRange(v.into())),
2397                MultipleRowsFromSubquery(()) => Ok(EvalError::MultipleRowsFromSubquery),
2398                NegativeRowsFromSubquery(()) => Ok(EvalError::NegativeRowsFromSubquery),
2399                Undefined(v) => Ok(EvalError::Undefined(v.into())),
2400                LikePatternTooLong(()) => Ok(EvalError::LikePatternTooLong),
2401                LikeEscapeTooLong(()) => Ok(EvalError::LikeEscapeTooLong),
2402                StringValueTooLong(v) => Ok(EvalError::StringValueTooLong {
2403                    target_type: v.target_type.into(),
2404                    length: usize::from_proto(v.length)?,
2405                }),
2406                MultidimensionalArrayRemovalNotSupported(()) => {
2407                    Ok(EvalError::MultidimensionalArrayRemovalNotSupported)
2408                }
2409                IncompatibleArrayDimensions(v) => Ok(EvalError::IncompatibleArrayDimensions {
2410                    dims: v.dims.into_rust()?,
2411                }),
2412                TypeFromOid(v) => Ok(EvalError::TypeFromOid(v.into())),
2413                InvalidRange(e) => Ok(EvalError::InvalidRange(e.into_rust()?)),
2414                InvalidRoleId(v) => Ok(EvalError::InvalidRoleId(v.into())),
2415                InvalidPrivileges(v) => Ok(EvalError::InvalidPrivileges(v.into())),
2416                InvalidCatalogJson(v) => Ok(EvalError::InvalidCatalogJson(v.into())),
2417                WmrRecursionLimitExceeded(v) => Ok(EvalError::LetRecLimitExceeded(v.into())),
2418                MultiDimensionalArraySearch(()) => Ok(EvalError::MultiDimensionalArraySearch),
2419                MustNotBeNull(v) => Ok(EvalError::MustNotBeNull(v.into())),
2420                InvalidIdentifier(v) => Ok(EvalError::InvalidIdentifier {
2421                    ident: v.ident.into(),
2422                    detail: v.detail.into_rust()?,
2423                }),
2424                ArrayFillWrongArraySubscripts(()) => Ok(EvalError::ArrayFillWrongArraySubscripts),
2425                MaxArraySizeExceeded(max_size) => {
2426                    Ok(EvalError::MaxArraySizeExceeded(usize::cast_from(max_size)))
2427                }
2428                DateDiffOverflow(v) => Ok(EvalError::DateDiffOverflow {
2429                    unit: v.unit.into(),
2430                    a: v.a.into(),
2431                    b: v.b.into(),
2432                }),
2433                IfNullError(v) => Ok(EvalError::IfNullError(v.into())),
2434                LengthTooLarge(()) => Ok(EvalError::LengthTooLarge),
2435                AclArrayNullElement(()) => Ok(EvalError::AclArrayNullElement),
2436                MzAclArrayNullElement(()) => Ok(EvalError::MzAclArrayNullElement),
2437                InvalidIanaTimezoneId(s) => Ok(EvalError::InvalidIanaTimezoneId(s.into())),
2438                PrettyError(s) => Ok(EvalError::PrettyError(s.into())),
2439                RedactError(s) => Ok(EvalError::RedactError(s.into())),
2440            },
2441            None => Err(TryFromProtoError::missing_field("ProtoEvalError::kind")),
2442        }
2443    }
2444}
2445
2446impl RustType<ProtoDims> for (usize, usize) {
2447    fn into_proto(&self) -> ProtoDims {
2448        ProtoDims {
2449            f0: self.0.into_proto(),
2450            f1: self.1.into_proto(),
2451        }
2452    }
2453
2454    fn from_proto(proto: ProtoDims) -> Result<Self, TryFromProtoError> {
2455        Ok((proto.f0.into_rust()?, proto.f1.into_rust()?))
2456    }
2457}
2458
2459#[cfg(test)]
2460mod tests {
2461    use super::*;
2462    use crate::scalar::func::variadic::Coalesce;
2463
2464    #[mz_ore::test]
2465    #[cfg_attr(miri, ignore)] // error: unsupported operation: can't call foreign function `rust_psm_stack_pointer` on OS `linux`
2466    fn test_reduce() {
2467        let relation_type: Vec<ReprColumnType> = vec![
2468            ReprScalarType::Int64.nullable(true),
2469            ReprScalarType::Int64.nullable(true),
2470            ReprScalarType::Int64.nullable(false),
2471        ]
2472        .into_iter()
2473        .collect();
2474        let col = MirScalarExpr::column;
2475        let int64_typ = ReprScalarType::Int64;
2476        let err = |e| MirScalarExpr::literal(Err(e), int64_typ.clone());
2477        let lit = |i| MirScalarExpr::literal_ok(Datum::Int64(i), int64_typ.clone());
2478        let null = || MirScalarExpr::literal_null(int64_typ.clone());
2479
2480        struct TestCase {
2481            input: MirScalarExpr,
2482            output: MirScalarExpr,
2483        }
2484
2485        let test_cases = vec![
2486            TestCase {
2487                input: MirScalarExpr::call_variadic(Coalesce, vec![lit(1)]),
2488                output: lit(1),
2489            },
2490            TestCase {
2491                input: MirScalarExpr::call_variadic(Coalesce, vec![lit(1), lit(2)]),
2492                output: lit(1),
2493            },
2494            TestCase {
2495                input: MirScalarExpr::call_variadic(Coalesce, vec![null(), lit(2), null()]),
2496                output: lit(2),
2497            },
2498            TestCase {
2499                input: MirScalarExpr::call_variadic(
2500                    Coalesce,
2501                    vec![null(), col(0), null(), col(1), lit(2), lit(3)],
2502                ),
2503                output: MirScalarExpr::call_variadic(Coalesce, vec![col(0), col(1), lit(2)]),
2504            },
2505            TestCase {
2506                input: MirScalarExpr::call_variadic(Coalesce, vec![col(0), col(2), col(1)]),
2507                output: MirScalarExpr::call_variadic(Coalesce, vec![col(0), col(2)]),
2508            },
2509            TestCase {
2510                input: MirScalarExpr::call_variadic(
2511                    Coalesce,
2512                    vec![lit(1), err(EvalError::DivisionByZero)],
2513                ),
2514                output: lit(1),
2515            },
2516            TestCase {
2517                input: MirScalarExpr::call_variadic(
2518                    Coalesce,
2519                    vec![
2520                        null(),
2521                        err(EvalError::DivisionByZero),
2522                        err(EvalError::NumericFieldOverflow),
2523                    ],
2524                ),
2525                output: err(EvalError::DivisionByZero),
2526            },
2527        ];
2528
2529        for tc in test_cases {
2530            let mut actual = tc.input.clone();
2531            actual.reduce(&relation_type);
2532            assert!(
2533                actual == tc.output,
2534                "input: {}\nactual: {}\nexpected: {}",
2535                tc.input,
2536                actual,
2537                tc.output
2538            );
2539        }
2540    }
2541
2542    /// Exercises the `unsafe` pointer stack in [`Visit::visit_mut_post`] with a
2543    /// closure that *replaces subtrees* (`*expr = ...`). Miri's aliasing model
2544    /// should shout if the "stack mirrors the call stack" becomes untrue.
2545    #[mz_ore::test]
2546    fn test_visit_mut_post_replace_subtrees() {
2547        let col = MirScalarExpr::column;
2548        let mut expr = col(0).if_then_else(col(1).if_then_else(col(2), col(3)), col(4));
2549
2550        expr.visit_mut_post(&mut |expr: &mut MirScalarExpr| match expr {
2551            MirScalarExpr::Column(n, _) => *n += 1,
2552            MirScalarExpr::If { then, .. } => {
2553                let then = then.take();
2554                *expr = then;
2555            }
2556            _ => {}
2557        });
2558
2559        // collapses to then-most branch
2560        assert_eq!(expr, col(3));
2561    }
2562
2563    /// Exercises the `unsafe` pointer stack in [`Visit::visit_mut_pre_post`] with
2564    /// a `pre` that both *replaces the visited node wholesale* (`*expr = ...`)
2565    /// and *returns an explicit child set* borrowed from the freshly written
2566    /// value. Miri's aliasing model should shout if the "stack mirrors the call
2567    /// stack" becomes untrue.
2568    #[mz_ore::test]
2569    fn test_visit_mut_pre_post_explicit_children() {
2570        let col = MirScalarExpr::column;
2571        let mut expr = col(5)
2572            .if_then_else(col(6), col(7))
2573            .if_then_else(col(1).if_then_else(col(2), col(3)), col(4));
2574
2575        // turns conditions into column 0 in pre
2576        // doesn't traverse conditions of ifs
2577        // adds 10 to all column refs in post (but not in conditions!)
2578        expr.visit_mut_pre_post(
2579            &mut |expr: &mut MirScalarExpr| -> Option<Vec<&mut MirScalarExpr>> {
2580                if let MirScalarExpr::If { .. } = expr {
2581                    let MirScalarExpr::If { then, els, .. } = expr else {
2582                        unreachable!()
2583                    };
2584                    let then = then.take();
2585                    let els = els.take();
2586                    *expr = MirScalarExpr::column(0).if_then_else(then, els);
2587
2588                    let MirScalarExpr::If { then, els, .. } = expr else {
2589                        unreachable!()
2590                    };
2591                    Some(vec![then.as_mut(), els.as_mut()])
2592                } else {
2593                    // Leaves recurse with their default (empty) child set.
2594                    None
2595                }
2596            },
2597            &mut |expr: &mut MirScalarExpr| {
2598                if let MirScalarExpr::Column(n, _) = expr {
2599                    *n += 10;
2600                }
2601            },
2602        );
2603
2604        // conditions become 0; everyone else += 10
2605        let expected = col(0).if_then_else(col(0).if_then_else(col(12), col(13)), col(14));
2606        assert_eq!(expr, expected);
2607    }
2608}