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::{BTreeMap, BTreeSet};
11use std::ops::BitOrAssign;
12use std::{fmt, mem};
13
14use itertools::Itertools;
15use mz_lowertest::MzReflect;
16use mz_ore::cast::CastFrom;
17use mz_ore::collections::CollectionExt;
18use mz_ore::iter::IteratorExt;
19use mz_ore::stack::RecursionLimitError;
20use mz_ore::str::StrExt;
21use mz_ore::vec::swap_remove_multiple;
22use mz_pgrepr::TypeFromOidError;
23use mz_pgtz::timezone::TimezoneSpec;
24use mz_proto::{IntoRustIfSome, ProtoType, RustType, TryFromProtoError};
25use mz_repr::adt::array::InvalidArrayError;
26use mz_repr::adt::date::DateError;
27use mz_repr::adt::datetime::DateTimeUnits;
28use mz_repr::adt::range::InvalidRangeError;
29use mz_repr::adt::regex::Regex;
30use mz_repr::adt::timestamp::TimestampError;
31use mz_repr::strconv::{ParseError, ParseHexError};
32use mz_repr::{ColumnType, Datum, Row, RowArena, ScalarType, arb_datum};
33use proptest::prelude::*;
34use proptest_derive::Arbitrary;
35use serde::{Deserialize, Serialize};
36
37use crate::scalar::func::format::DateTimeFormat;
38use crate::scalar::func::{
39    BinaryFunc, UnaryFunc, UnmaterializableFunc, VariadicFunc, parse_timezone,
40    regexp_replace_parse_flags,
41};
42use crate::scalar::proto_eval_error::proto_incompatible_array_dimensions::ProtoDims;
43use crate::scalar::proto_mir_scalar_expr::*;
44use crate::visit::{Visit, VisitChildren};
45
46pub mod func;
47pub mod like_pattern;
48
49include!(concat!(env!("OUT_DIR"), "/mz_expr.scalar.rs"));
50
51#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, MzReflect)]
52pub enum MirScalarExpr {
53    /// A column of the input row
54    Column(usize),
55    /// A literal value.
56    /// (Stored as a row, because we can't own a Datum)
57    Literal(Result<Row, EvalError>, ColumnType),
58    /// A call to an unmaterializable function.
59    ///
60    /// These functions cannot be evaluated by `MirScalarExpr::eval`. They must
61    /// be transformed away by a higher layer.
62    CallUnmaterializable(UnmaterializableFunc),
63    /// A function call that takes one expression as an argument.
64    CallUnary {
65        func: UnaryFunc,
66        expr: Box<MirScalarExpr>,
67    },
68    /// A function call that takes two expressions as arguments.
69    CallBinary {
70        func: BinaryFunc,
71        expr1: Box<MirScalarExpr>,
72        expr2: Box<MirScalarExpr>,
73    },
74    /// A function call that takes an arbitrary number of arguments.
75    CallVariadic {
76        func: VariadicFunc,
77        exprs: Vec<MirScalarExpr>,
78    },
79    /// Conditionally evaluated expressions.
80    ///
81    /// It is important that `then` and `els` only be evaluated if
82    /// `cond` is true or not, respectively. This is the only way
83    /// users can guard execution (other logical operator do not
84    /// short-circuit) and we need to preserve that.
85    If {
86        cond: Box<MirScalarExpr>,
87        then: Box<MirScalarExpr>,
88        els: Box<MirScalarExpr>,
89    },
90}
91
92impl Arbitrary for MirScalarExpr {
93    type Parameters = ();
94    type Strategy = BoxedStrategy<MirScalarExpr>;
95
96    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
97        let leaf = prop::strategy::Union::new(vec![
98            (0..10_usize).prop_map(MirScalarExpr::Column).boxed(),
99            (arb_datum(), any::<ScalarType>())
100                .prop_map(|(datum, typ)| MirScalarExpr::literal(Ok((&datum).into()), typ))
101                .boxed(),
102            (any::<EvalError>(), any::<ScalarType>())
103                .prop_map(|(err, typ)| MirScalarExpr::literal(Err(err), typ))
104                .boxed(),
105            any::<UnmaterializableFunc>()
106                .prop_map(MirScalarExpr::CallUnmaterializable)
107                .boxed(),
108        ]);
109        leaf.prop_recursive(3, 6, 7, |inner| {
110            prop::strategy::Union::new(vec![
111                (
112                    any::<VariadicFunc>(),
113                    prop::collection::vec(inner.clone(), 1..5),
114                )
115                    .prop_map(|(func, exprs)| MirScalarExpr::CallVariadic { func, exprs })
116                    .boxed(),
117                (any::<BinaryFunc>(), inner.clone(), inner.clone())
118                    .prop_map(|(func, expr1, expr2)| MirScalarExpr::CallBinary {
119                        func,
120                        expr1: Box::new(expr1),
121                        expr2: Box::new(expr2),
122                    })
123                    .boxed(),
124                (inner.clone(), inner.clone(), inner.clone())
125                    .prop_map(|(cond, then, els)| MirScalarExpr::If {
126                        cond: Box::new(cond),
127                        then: Box::new(then),
128                        els: Box::new(els),
129                    })
130                    .boxed(),
131                (any::<UnaryFunc>(), inner)
132                    .prop_map(|(func, expr)| MirScalarExpr::CallUnary {
133                        func,
134                        expr: Box::new(expr),
135                    })
136                    .boxed(),
137            ])
138        })
139        .boxed()
140    }
141}
142
143impl RustType<ProtoMirScalarExpr> for MirScalarExpr {
144    fn into_proto(&self) -> ProtoMirScalarExpr {
145        use proto_mir_scalar_expr::Kind::*;
146        ProtoMirScalarExpr {
147            kind: Some(match self {
148                MirScalarExpr::Column(i) => Column(i.into_proto()),
149                MirScalarExpr::Literal(lit, typ) => Literal(ProtoLiteral {
150                    lit: Some(lit.into_proto()),
151                    typ: Some(typ.into_proto()),
152                }),
153                MirScalarExpr::CallUnmaterializable(func) => {
154                    CallUnmaterializable(func.into_proto())
155                }
156                MirScalarExpr::CallUnary { func, expr } => CallUnary(Box::new(ProtoCallUnary {
157                    func: Some(Box::new(func.into_proto())),
158                    expr: Some(expr.into_proto()),
159                })),
160                MirScalarExpr::CallBinary { func, expr1, expr2 } => {
161                    CallBinary(Box::new(ProtoCallBinary {
162                        func: Some(func.into_proto()),
163                        expr1: Some(expr1.into_proto()),
164                        expr2: Some(expr2.into_proto()),
165                    }))
166                }
167                MirScalarExpr::CallVariadic { func, exprs } => CallVariadic(ProtoCallVariadic {
168                    func: Some(func.into_proto()),
169                    exprs: exprs.into_proto(),
170                }),
171                MirScalarExpr::If { cond, then, els } => If(Box::new(ProtoIf {
172                    cond: Some(cond.into_proto()),
173                    then: Some(then.into_proto()),
174                    els: Some(els.into_proto()),
175                })),
176            }),
177        }
178    }
179
180    fn from_proto(proto: ProtoMirScalarExpr) -> Result<Self, TryFromProtoError> {
181        use proto_mir_scalar_expr::Kind::*;
182        let kind = proto
183            .kind
184            .ok_or_else(|| TryFromProtoError::missing_field("ProtoMirScalarExpr::kind"))?;
185        Ok(match kind {
186            Column(i) => MirScalarExpr::Column(usize::from_proto(i)?),
187            Literal(ProtoLiteral { lit, typ }) => MirScalarExpr::Literal(
188                lit.into_rust_if_some("ProtoLiteral::lit")?,
189                typ.into_rust_if_some("ProtoLiteral::typ")?,
190            ),
191            CallUnmaterializable(func) => MirScalarExpr::CallUnmaterializable(func.into_rust()?),
192            CallUnary(call_unary) => MirScalarExpr::CallUnary {
193                func: call_unary.func.into_rust_if_some("ProtoCallUnary::func")?,
194                expr: call_unary.expr.into_rust_if_some("ProtoCallUnary::expr")?,
195            },
196            CallBinary(call_binary) => MirScalarExpr::CallBinary {
197                func: call_binary
198                    .func
199                    .into_rust_if_some("ProtoCallBinary::func")?,
200                expr1: call_binary
201                    .expr1
202                    .into_rust_if_some("ProtoCallBinary::expr1")?,
203                expr2: call_binary
204                    .expr2
205                    .into_rust_if_some("ProtoCallBinary::expr2")?,
206            },
207            CallVariadic(ProtoCallVariadic { func, exprs }) => MirScalarExpr::CallVariadic {
208                func: func.into_rust_if_some("ProtoCallVariadic::func")?,
209                exprs: exprs.into_rust()?,
210            },
211            If(if_struct) => MirScalarExpr::If {
212                cond: if_struct.cond.into_rust_if_some("ProtoIf::cond")?,
213                then: if_struct.then.into_rust_if_some("ProtoIf::then")?,
214                els: if_struct.els.into_rust_if_some("ProtoIf::els")?,
215            },
216        })
217    }
218}
219
220impl RustType<proto_literal::ProtoLiteralData> for Result<Row, EvalError> {
221    fn into_proto(&self) -> proto_literal::ProtoLiteralData {
222        use proto_literal::proto_literal_data::Result::*;
223        proto_literal::ProtoLiteralData {
224            result: Some(match self {
225                Result::Ok(row) => Ok(row.into_proto()),
226                Result::Err(err) => Err(err.into_proto()),
227            }),
228        }
229    }
230
231    fn from_proto(proto: proto_literal::ProtoLiteralData) -> Result<Self, TryFromProtoError> {
232        use proto_literal::proto_literal_data::Result::*;
233        match proto.result {
234            Some(Ok(row)) => Result::Ok(Result::Ok(
235                (&row)
236                    .try_into()
237                    .map_err(TryFromProtoError::RowConversionError)?,
238            )),
239            Some(Err(err)) => Result::Ok(Result::Err(err.into_rust()?)),
240            None => Result::Err(TryFromProtoError::missing_field("ProtoLiteralData::result")),
241        }
242    }
243}
244
245impl MirScalarExpr {
246    pub fn columns(is: &[usize]) -> Vec<MirScalarExpr> {
247        is.iter().map(|i| MirScalarExpr::Column(*i)).collect()
248    }
249
250    pub fn column(column: usize) -> Self {
251        MirScalarExpr::Column(column)
252    }
253
254    pub fn literal(res: Result<Datum, EvalError>, typ: ScalarType) -> Self {
255        let typ = typ.nullable(matches!(res, Ok(Datum::Null)));
256        let row = res.map(|datum| Row::pack_slice(&[datum]));
257        MirScalarExpr::Literal(row, typ)
258    }
259
260    pub fn literal_ok(datum: Datum, typ: ScalarType) -> Self {
261        MirScalarExpr::literal(Ok(datum), typ)
262    }
263
264    pub fn literal_null(typ: ScalarType) -> Self {
265        MirScalarExpr::literal_ok(Datum::Null, typ)
266    }
267
268    pub fn literal_false() -> Self {
269        MirScalarExpr::literal_ok(Datum::False, ScalarType::Bool)
270    }
271
272    pub fn literal_true() -> Self {
273        MirScalarExpr::literal_ok(Datum::True, ScalarType::Bool)
274    }
275
276    pub fn call_unary(self, func: UnaryFunc) -> Self {
277        MirScalarExpr::CallUnary {
278            func,
279            expr: Box::new(self),
280        }
281    }
282
283    pub fn call_binary(self, other: Self, func: BinaryFunc) -> Self {
284        MirScalarExpr::CallBinary {
285            func,
286            expr1: Box::new(self),
287            expr2: Box::new(other),
288        }
289    }
290
291    pub fn if_then_else(self, t: Self, f: Self) -> Self {
292        MirScalarExpr::If {
293            cond: Box::new(self),
294            then: Box::new(t),
295            els: Box::new(f),
296        }
297    }
298
299    pub fn or(self, other: Self) -> Self {
300        MirScalarExpr::CallVariadic {
301            func: VariadicFunc::Or,
302            exprs: vec![self, other],
303        }
304    }
305
306    pub fn and(self, other: Self) -> Self {
307        MirScalarExpr::CallVariadic {
308            func: VariadicFunc::And,
309            exprs: vec![self, other],
310        }
311    }
312
313    pub fn not(self) -> Self {
314        self.call_unary(UnaryFunc::Not(func::Not))
315    }
316
317    pub fn call_is_null(self) -> Self {
318        self.call_unary(UnaryFunc::IsNull(func::IsNull))
319    }
320
321    /// Match AND or OR on self and get the args. If no match, then interpret self as if it were
322    /// wrapped in a 1-arg AND/OR.
323    pub fn and_or_args(&self, func_to_match: VariadicFunc) -> Vec<MirScalarExpr> {
324        assert!(func_to_match == VariadicFunc::Or || func_to_match == VariadicFunc::And);
325        match self {
326            MirScalarExpr::CallVariadic { func, exprs } if *func == func_to_match => exprs.clone(),
327            _ => vec![self.clone()],
328        }
329    }
330
331    /// Try to match a literal equality involving the given expression on one side.
332    /// Return the (non-null) literal and a bool that indicates whether an inversion was needed.
333    ///
334    /// More specifically:
335    /// If `self` is an equality with a `null` literal on any side, then the match fails!
336    /// Otherwise: for a given `expr`, if `self` is `<expr> = <literal>` or `<literal> = <expr>`
337    /// then return `Some((<literal>, false))`. In addition to just trying to match `<expr>` as it
338    /// is, we also try to remove an invertible function call (such as a cast). If the match
339    /// succeeds with the inversion, then return `Some((<inverted-literal>, true))`. For more
340    /// details on the inversion, see `invert_casts_on_expr_eq_literal_inner`.
341    pub fn expr_eq_literal(&self, expr: &MirScalarExpr) -> Option<(Row, bool)> {
342        if let MirScalarExpr::CallBinary {
343            func: BinaryFunc::Eq,
344            expr1,
345            expr2,
346        } = self
347        {
348            if expr1.is_literal_null() || expr2.is_literal_null() {
349                return None;
350            }
351            if let Some(Ok(lit)) = expr1.as_literal_owned() {
352                return Self::expr_eq_literal_inner(expr, lit, expr1, expr2);
353            }
354            if let Some(Ok(lit)) = expr2.as_literal_owned() {
355                return Self::expr_eq_literal_inner(expr, lit, expr2, expr1);
356            }
357        }
358        None
359    }
360
361    fn expr_eq_literal_inner(
362        expr_to_match: &MirScalarExpr,
363        literal: Row,
364        literal_expr: &MirScalarExpr,
365        other_side: &MirScalarExpr,
366    ) -> Option<(Row, bool)> {
367        if other_side == expr_to_match {
368            return Some((literal, false));
369        } else {
370            // expr didn't exactly match. See if we can match it by inverse-casting.
371            let (cast_removed, inv_cast_lit) =
372                Self::invert_casts_on_expr_eq_literal_inner(other_side, literal_expr);
373            if &cast_removed == expr_to_match {
374                if let Some(Ok(inv_cast_lit_row)) = inv_cast_lit.as_literal_owned() {
375                    return Some((inv_cast_lit_row, true));
376                }
377            }
378        }
379        None
380    }
381
382    /// If `self` is `<expr> = <literal>` or `<literal> = <expr>` then
383    /// return `<expr>`. It also tries to remove a cast (or other invertible function call) from
384    /// `<expr>` before returning it, see `invert_casts_on_expr_eq_literal_inner`.
385    pub fn any_expr_eq_literal(&self) -> Option<MirScalarExpr> {
386        if let MirScalarExpr::CallBinary {
387            func: BinaryFunc::Eq,
388            expr1,
389            expr2,
390        } = self
391        {
392            if expr1.is_literal() {
393                let (expr, _literal) = Self::invert_casts_on_expr_eq_literal_inner(expr2, expr1);
394                return Some(expr);
395            }
396            if expr2.is_literal() {
397                let (expr, _literal) = Self::invert_casts_on_expr_eq_literal_inner(expr1, expr2);
398                return Some(expr);
399            }
400        }
401        None
402    }
403
404    /// If the given `MirScalarExpr` is a literal equality where one side is an invertible function
405    /// call, then calls the inverse function on both sides of the equality and returns the modified
406    /// version of the given `MirScalarExpr`. Otherwise, it returns the original expression.
407    /// For more details, see `invert_casts_on_expr_eq_literal_inner`.
408    pub fn invert_casts_on_expr_eq_literal(&self) -> MirScalarExpr {
409        if let MirScalarExpr::CallBinary {
410            func: BinaryFunc::Eq,
411            expr1,
412            expr2,
413        } = self
414        {
415            if expr1.is_literal() {
416                let (expr, literal) = Self::invert_casts_on_expr_eq_literal_inner(expr2, expr1);
417                return MirScalarExpr::CallBinary {
418                    func: BinaryFunc::Eq,
419                    expr1: Box::new(literal),
420                    expr2: Box::new(expr),
421                };
422            }
423            if expr2.is_literal() {
424                let (expr, literal) = Self::invert_casts_on_expr_eq_literal_inner(expr1, expr2);
425                return MirScalarExpr::CallBinary {
426                    func: BinaryFunc::Eq,
427                    expr1: Box::new(literal),
428                    expr2: Box::new(expr),
429                };
430            }
431            // Note: The above return statements should be consistent in whether they put the
432            // literal in expr1 or expr2, for the deduplication in CanonicalizeMfp to work.
433        }
434        self.clone()
435    }
436
437    /// Given an `<expr>` and a `<literal>` that were taken out from `<expr> = <literal>` or
438    /// `<literal> = <expr>`, it tries to simplify the equality by applying the inverse function of
439    /// the outermost function call of `<expr>` (if exists):
440    ///
441    /// `<literal> = func(<inner_expr>)`, where `func` is invertible
442    ///  -->
443    /// `<func^-1(literal)> = <inner_expr>`
444    /// if `func^-1(literal)` doesn't error out, and both `func` and `func^-1` preserve uniqueness.
445    ///
446    /// The return value is the `<inner_expr>` and the literal value that we get by applying the
447    /// inverse function.
448    fn invert_casts_on_expr_eq_literal_inner(
449        expr: &MirScalarExpr,
450        literal: &MirScalarExpr,
451    ) -> (MirScalarExpr, MirScalarExpr) {
452        assert!(matches!(literal, MirScalarExpr::Literal(..)));
453
454        let temp_storage = &RowArena::new();
455        let eval = |e: &MirScalarExpr| {
456            MirScalarExpr::literal(e.eval(&[], temp_storage), e.typ(&[]).scalar_type)
457        };
458
459        if let MirScalarExpr::CallUnary {
460            func,
461            expr: inner_expr,
462        } = expr
463        {
464            if let Some(inverse_func) = func.inverse() {
465                // We don't want to remove a function call that doesn't preserve uniqueness, e.g.,
466                // if `f` is a float, we don't want to inverse-cast `f::INT = 0`, because the
467                // inserted int-to-float cast wouldn't be able to invert the rounding.
468                // Also, we don't want to insert a function call that doesn't preserve
469                // uniqueness. E.g., if `a` has an integer type, we don't want to do
470                // a surprise rounding for `WHERE a = 3.14`.
471                if func.preserves_uniqueness() && inverse_func.preserves_uniqueness() {
472                    let lit_inv = eval(&MirScalarExpr::CallUnary {
473                        func: inverse_func,
474                        expr: Box::new(literal.clone()),
475                    });
476                    // The evaluation can error out, e.g., when casting a too large int32 to int16.
477                    // This case is handled by `impossible_literal_equality_because_types`.
478                    if !lit_inv.is_literal_err() {
479                        return (*inner_expr.clone(), lit_inv);
480                    }
481                }
482            }
483        }
484        (expr.clone(), literal.clone())
485    }
486
487    /// Tries to remove a cast (or other invertible function) in the same way as
488    /// `invert_casts_on_expr_eq_literal`, but if calling the inverse function fails on the literal,
489    /// then it deems the equality to be impossible. For example if `a` is a smallint column, then
490    /// it catches `a::integer = 1000000` to be an always false predicate (where the `::integer`
491    /// could have been inserted implicitly).
492    pub fn impossible_literal_equality_because_types(&self) -> bool {
493        if let MirScalarExpr::CallBinary {
494            func: BinaryFunc::Eq,
495            expr1,
496            expr2,
497        } = self
498        {
499            if expr1.is_literal() {
500                return Self::impossible_literal_equality_because_types_inner(expr1, expr2);
501            }
502            if expr2.is_literal() {
503                return Self::impossible_literal_equality_because_types_inner(expr2, expr1);
504            }
505        }
506        false
507    }
508
509    fn impossible_literal_equality_because_types_inner(
510        literal: &MirScalarExpr,
511        other_side: &MirScalarExpr,
512    ) -> bool {
513        assert!(matches!(literal, MirScalarExpr::Literal(..)));
514
515        let temp_storage = &RowArena::new();
516        let eval = |e: &MirScalarExpr| {
517            MirScalarExpr::literal(e.eval(&[], temp_storage), e.typ(&[]).scalar_type)
518        };
519
520        if let MirScalarExpr::CallUnary { func, .. } = other_side {
521            if let Some(inverse_func) = func.inverse() {
522                if inverse_func.preserves_uniqueness()
523                    && eval(&MirScalarExpr::CallUnary {
524                        func: inverse_func,
525                        expr: Box::new(literal.clone()),
526                    })
527                    .is_literal_err()
528                {
529                    return true;
530                }
531            }
532        }
533
534        false
535    }
536
537    /// Determines if `self` is
538    /// `<expr> < <literal>` or
539    /// `<expr> > <literal>` or
540    /// `<literal> < <expr>` or
541    /// `<literal> > <expr>` or
542    /// `<expr> <= <literal>` or
543    /// `<expr> >= <literal>` or
544    /// `<literal> <= <expr>` or
545    /// `<literal> >= <expr>`.
546    pub fn any_expr_ineq_literal(&self) -> bool {
547        match self {
548            MirScalarExpr::CallBinary {
549                func: BinaryFunc::Lt | BinaryFunc::Lte | BinaryFunc::Gt | BinaryFunc::Gte,
550                expr1,
551                expr2,
552            } => expr1.is_literal() || expr2.is_literal(),
553            _ => false,
554        }
555    }
556
557    /// Rewrites column indices with their value in `permutation`.
558    ///
559    /// This method is applicable even when `permutation` is not a
560    /// strict permutation, and it only needs to have entries for
561    /// each column referenced in `self`.
562    pub fn permute(&mut self, permutation: &[usize]) {
563        self.visit_columns(|c| *c = permutation[*c]);
564    }
565
566    /// Rewrites column indices with their value in `permutation`.
567    ///
568    /// This method is applicable even when `permutation` is not a
569    /// strict permutation, and it only needs to have entries for
570    /// each column referenced in `self`.
571    pub fn permute_map(&mut self, permutation: &BTreeMap<usize, usize>) {
572        self.visit_columns(|c| *c = permutation[c]);
573    }
574
575    /// Visits each column reference and applies `action` to the column.
576    ///
577    /// Useful for remapping columns, or for collecting expression support.
578    pub fn visit_columns<F>(&mut self, mut action: F)
579    where
580        F: FnMut(&mut usize),
581    {
582        self.visit_pre_mut(|e| {
583            if let MirScalarExpr::Column(col) = e {
584                action(col);
585            }
586        });
587    }
588
589    pub fn support(&self) -> BTreeSet<usize> {
590        let mut support = BTreeSet::new();
591        self.support_into(&mut support);
592        support
593    }
594
595    pub fn support_into(&self, support: &mut BTreeSet<usize>) {
596        self.visit_pre(|e| {
597            if let MirScalarExpr::Column(i) = e {
598                support.insert(*i);
599            }
600        });
601    }
602
603    pub fn take(&mut self) -> Self {
604        mem::replace(self, MirScalarExpr::literal_null(ScalarType::String))
605    }
606
607    pub fn as_literal(&self) -> Option<Result<Datum, &EvalError>> {
608        if let MirScalarExpr::Literal(lit, _column_type) = self {
609            Some(lit.as_ref().map(|row| row.unpack_first()))
610        } else {
611            None
612        }
613    }
614
615    pub fn as_literal_owned(&self) -> Option<Result<Row, EvalError>> {
616        if let MirScalarExpr::Literal(lit, _column_type) = self {
617            Some(lit.clone())
618        } else {
619            None
620        }
621    }
622
623    pub fn as_literal_str(&self) -> Option<&str> {
624        match self.as_literal() {
625            Some(Ok(Datum::String(s))) => Some(s),
626            _ => None,
627        }
628    }
629
630    pub fn as_literal_int64(&self) -> Option<i64> {
631        match self.as_literal() {
632            Some(Ok(Datum::Int64(i))) => Some(i),
633            _ => None,
634        }
635    }
636
637    pub fn as_literal_err(&self) -> Option<&EvalError> {
638        self.as_literal().and_then(|lit| lit.err())
639    }
640
641    pub fn is_literal(&self) -> bool {
642        matches!(self, MirScalarExpr::Literal(_, _))
643    }
644
645    pub fn is_literal_true(&self) -> bool {
646        Some(Ok(Datum::True)) == self.as_literal()
647    }
648
649    pub fn is_literal_false(&self) -> bool {
650        Some(Ok(Datum::False)) == self.as_literal()
651    }
652
653    pub fn is_literal_null(&self) -> bool {
654        Some(Ok(Datum::Null)) == self.as_literal()
655    }
656
657    pub fn is_literal_ok(&self) -> bool {
658        matches!(self, MirScalarExpr::Literal(Ok(_), _typ))
659    }
660
661    pub fn is_literal_err(&self) -> bool {
662        matches!(self, MirScalarExpr::Literal(Err(_), _typ))
663    }
664
665    pub fn is_column(&self) -> bool {
666        matches!(self, MirScalarExpr::Column(_))
667    }
668
669    pub fn is_error_if_null(&self) -> bool {
670        matches!(
671            self,
672            Self::CallVariadic {
673                func: VariadicFunc::ErrorIfNull,
674                ..
675            }
676        )
677    }
678
679    #[deprecated = "Use `might_error` instead"]
680    pub fn contains_error_if_null(&self) -> bool {
681        let mut worklist = vec![self];
682        while let Some(expr) = worklist.pop() {
683            if expr.is_error_if_null() {
684                return true;
685            }
686            worklist.extend(expr.children());
687        }
688        false
689    }
690
691    /// A very crude approximation for scalar expressions that might produce an
692    /// error.
693    ///
694    /// Currently, this is restricted only to expressions that either contain a
695    /// literal error or a [`VariadicFunc::ErrorIfNull`] call.
696    pub fn might_error(&self) -> bool {
697        let mut worklist = vec![self];
698        while let Some(expr) = worklist.pop() {
699            if expr.is_literal_err() || expr.is_error_if_null() {
700                return true;
701            }
702            worklist.extend(expr.children());
703        }
704        false
705    }
706
707    /// If self is a column, return the column index, otherwise `None`.
708    pub fn as_column(&self) -> Option<usize> {
709        if let MirScalarExpr::Column(c) = self {
710            Some(*c)
711        } else {
712            None
713        }
714    }
715
716    /// Reduces a complex expression where possible.
717    ///
718    /// This function uses nullability information present in `column_types`,
719    /// and the result may only continue to be a correct transformation as
720    /// long as this information continues to hold (nullability may not hold
721    /// as expressions migrate around).
722    ///
723    /// (If you'd like to not use nullability information here, then you can
724    /// tweak the nullabilities in `column_types` before passing it to this
725    /// function, see e.g. in `EquivalenceClasses::minimize`.)
726    ///
727    /// Also performs partial canonicalization on the expression.
728    ///
729    /// ```rust
730    /// use mz_expr::MirScalarExpr;
731    /// use mz_repr::{ColumnType, Datum, ScalarType};
732    ///
733    /// let expr_0 = MirScalarExpr::Column(0);
734    /// let expr_t = MirScalarExpr::literal_true();
735    /// let expr_f = MirScalarExpr::literal_false();
736    ///
737    /// let mut test =
738    /// expr_t
739    ///     .clone()
740    ///     .and(expr_f.clone())
741    ///     .if_then_else(expr_0, expr_t.clone());
742    ///
743    /// let input_type = vec![ScalarType::Int32.nullable(false)];
744    /// test.reduce(&input_type);
745    /// assert_eq!(test, expr_t);
746    /// ```
747    pub fn reduce(&mut self, column_types: &[ColumnType]) {
748        let temp_storage = &RowArena::new();
749        let eval = |e: &MirScalarExpr| {
750            MirScalarExpr::literal(e.eval(&[], temp_storage), e.typ(column_types).scalar_type)
751        };
752
753        // Simplifications run in a loop until `self` no longer changes.
754        let mut old_self = MirScalarExpr::column(0);
755        while old_self != *self {
756            old_self = self.clone();
757            #[allow(deprecated)]
758            self.visit_mut_pre_post_nolimit(
759                &mut |e| {
760                    match e {
761                        MirScalarExpr::CallUnary { func, expr } => {
762                            if *func == UnaryFunc::IsNull(func::IsNull) {
763                                if !expr.typ(column_types).nullable {
764                                    *e = MirScalarExpr::literal_false();
765                                } else {
766                                    // Try to at least decompose IsNull into a disjunction
767                                    // of simpler IsNull subexpressions.
768                                    if let Some(expr) = expr.decompose_is_null() {
769                                        *e = expr
770                                    }
771                                }
772                            } else if *func == UnaryFunc::Not(func::Not) {
773                                // Push down not expressions
774                                match &mut **expr {
775                                    // Two negates cancel each other out.
776                                    MirScalarExpr::CallUnary {
777                                        expr: inner_expr,
778                                        func: UnaryFunc::Not(func::Not),
779                                    } => *e = inner_expr.take(),
780                                    // Transforms `NOT(a <op> b)` to `a negate(<op>) b`
781                                    // if a negation exists.
782                                    MirScalarExpr::CallBinary { expr1, expr2, func } => {
783                                        if let Some(negated_func) = func.negate() {
784                                            *e = MirScalarExpr::CallBinary {
785                                                expr1: Box::new(expr1.take()),
786                                                expr2: Box::new(expr2.take()),
787                                                func: negated_func,
788                                            }
789                                        }
790                                    }
791                                    MirScalarExpr::CallVariadic { .. } => {
792                                        e.demorgans();
793                                    }
794                                    _ => {}
795                                }
796                            }
797                        }
798                        _ => {}
799                    };
800                    None
801                },
802                &mut |e| match e {
803                    // Evaluate and pull up constants
804                    MirScalarExpr::Column(_)
805                    | MirScalarExpr::Literal(_, _)
806                    | MirScalarExpr::CallUnmaterializable(_) => (),
807                    MirScalarExpr::CallUnary { func, expr } => {
808                        if expr.is_literal() && *func != UnaryFunc::Panic(func::Panic) {
809                            *e = eval(e);
810                        } else if let UnaryFunc::RecordGet(func::RecordGet(i)) = *func {
811                            if let MirScalarExpr::CallVariadic {
812                                func: VariadicFunc::RecordCreate { .. },
813                                exprs,
814                            } = &mut **expr
815                            {
816                                *e = exprs.swap_remove(i);
817                            }
818                        }
819                    }
820                    MirScalarExpr::CallBinary { func, expr1, expr2 } => {
821                        if expr1.is_literal() && expr2.is_literal() {
822                            *e = eval(e);
823                        } else if (expr1.is_literal_null() || expr2.is_literal_null())
824                            && func.propagates_nulls()
825                        {
826                            *e = MirScalarExpr::literal_null(e.typ(column_types).scalar_type);
827                        } else if let Some(err) = expr1.as_literal_err() {
828                            *e = MirScalarExpr::literal(
829                                Err(err.clone()),
830                                e.typ(column_types).scalar_type,
831                            );
832                        } else if let Some(err) = expr2.as_literal_err() {
833                            *e = MirScalarExpr::literal(
834                                Err(err.clone()),
835                                e.typ(column_types).scalar_type,
836                            );
837                        } else if let BinaryFunc::IsLikeMatch { case_insensitive } = func {
838                            if expr2.is_literal() {
839                                // We can at least precompile the regex.
840                                let pattern = expr2.as_literal_str().unwrap();
841                                *e = match like_pattern::compile(pattern, *case_insensitive) {
842                                    Ok(matcher) => expr1.take().call_unary(UnaryFunc::IsLikeMatch(
843                                        func::IsLikeMatch(matcher),
844                                    )),
845                                    Err(err) => MirScalarExpr::literal(
846                                        Err(err),
847                                        e.typ(column_types).scalar_type,
848                                    ),
849                                };
850                            }
851                        } else if let BinaryFunc::IsRegexpMatch { case_insensitive } = func {
852                            if let MirScalarExpr::Literal(Ok(row), _) = &**expr2 {
853                                *e = match Regex::new(
854                                    row.unpack_first().unwrap_str(),
855                                    *case_insensitive,
856                                ) {
857                                    Ok(regex) => expr1.take().call_unary(UnaryFunc::IsRegexpMatch(
858                                        func::IsRegexpMatch(regex),
859                                    )),
860                                    Err(err) => MirScalarExpr::literal(
861                                        Err(err.into()),
862                                        e.typ(column_types).scalar_type,
863                                    ),
864                                };
865                            }
866                        } else if *func == BinaryFunc::ExtractInterval && expr1.is_literal() {
867                            let units = expr1.as_literal_str().unwrap();
868                            *e = match units.parse::<DateTimeUnits>() {
869                                Ok(units) => MirScalarExpr::CallUnary {
870                                    func: UnaryFunc::ExtractInterval(func::ExtractInterval(units)),
871                                    expr: Box::new(expr2.take()),
872                                },
873                                Err(_) => MirScalarExpr::literal(
874                                    Err(EvalError::UnknownUnits(units.into())),
875                                    e.typ(column_types).scalar_type,
876                                ),
877                            }
878                        } else if *func == BinaryFunc::ExtractTime && expr1.is_literal() {
879                            let units = expr1.as_literal_str().unwrap();
880                            *e = match units.parse::<DateTimeUnits>() {
881                                Ok(units) => MirScalarExpr::CallUnary {
882                                    func: UnaryFunc::ExtractTime(func::ExtractTime(units)),
883                                    expr: Box::new(expr2.take()),
884                                },
885                                Err(_) => MirScalarExpr::literal(
886                                    Err(EvalError::UnknownUnits(units.into())),
887                                    e.typ(column_types).scalar_type,
888                                ),
889                            }
890                        } else if *func == BinaryFunc::ExtractTimestamp && expr1.is_literal() {
891                            let units = expr1.as_literal_str().unwrap();
892                            *e = match units.parse::<DateTimeUnits>() {
893                                Ok(units) => MirScalarExpr::CallUnary {
894                                    func: UnaryFunc::ExtractTimestamp(func::ExtractTimestamp(
895                                        units,
896                                    )),
897                                    expr: Box::new(expr2.take()),
898                                },
899                                Err(_) => MirScalarExpr::literal(
900                                    Err(EvalError::UnknownUnits(units.into())),
901                                    e.typ(column_types).scalar_type,
902                                ),
903                            }
904                        } else if *func == BinaryFunc::ExtractTimestampTz && expr1.is_literal() {
905                            let units = expr1.as_literal_str().unwrap();
906                            *e = match units.parse::<DateTimeUnits>() {
907                                Ok(units) => MirScalarExpr::CallUnary {
908                                    func: UnaryFunc::ExtractTimestampTz(func::ExtractTimestampTz(
909                                        units,
910                                    )),
911                                    expr: Box::new(expr2.take()),
912                                },
913                                Err(_) => MirScalarExpr::literal(
914                                    Err(EvalError::UnknownUnits(units.into())),
915                                    e.typ(column_types).scalar_type,
916                                ),
917                            }
918                        } else if *func == BinaryFunc::ExtractDate && expr1.is_literal() {
919                            let units = expr1.as_literal_str().unwrap();
920                            *e = match units.parse::<DateTimeUnits>() {
921                                Ok(units) => MirScalarExpr::CallUnary {
922                                    func: UnaryFunc::ExtractDate(func::ExtractDate(units)),
923                                    expr: Box::new(expr2.take()),
924                                },
925                                Err(_) => MirScalarExpr::literal(
926                                    Err(EvalError::UnknownUnits(units.into())),
927                                    e.typ(column_types).scalar_type,
928                                ),
929                            }
930                        } else if *func == BinaryFunc::DatePartInterval && expr1.is_literal() {
931                            let units = expr1.as_literal_str().unwrap();
932                            *e = match units.parse::<DateTimeUnits>() {
933                                Ok(units) => MirScalarExpr::CallUnary {
934                                    func: UnaryFunc::DatePartInterval(func::DatePartInterval(
935                                        units,
936                                    )),
937                                    expr: Box::new(expr2.take()),
938                                },
939                                Err(_) => MirScalarExpr::literal(
940                                    Err(EvalError::UnknownUnits(units.into())),
941                                    e.typ(column_types).scalar_type,
942                                ),
943                            }
944                        } else if *func == BinaryFunc::DatePartTime && expr1.is_literal() {
945                            let units = expr1.as_literal_str().unwrap();
946                            *e = match units.parse::<DateTimeUnits>() {
947                                Ok(units) => MirScalarExpr::CallUnary {
948                                    func: UnaryFunc::DatePartTime(func::DatePartTime(units)),
949                                    expr: Box::new(expr2.take()),
950                                },
951                                Err(_) => MirScalarExpr::literal(
952                                    Err(EvalError::UnknownUnits(units.into())),
953                                    e.typ(column_types).scalar_type,
954                                ),
955                            }
956                        } else if *func == BinaryFunc::DatePartTimestamp && expr1.is_literal() {
957                            let units = expr1.as_literal_str().unwrap();
958                            *e = match units.parse::<DateTimeUnits>() {
959                                Ok(units) => MirScalarExpr::CallUnary {
960                                    func: UnaryFunc::DatePartTimestamp(func::DatePartTimestamp(
961                                        units,
962                                    )),
963                                    expr: Box::new(expr2.take()),
964                                },
965                                Err(_) => MirScalarExpr::literal(
966                                    Err(EvalError::UnknownUnits(units.into())),
967                                    e.typ(column_types).scalar_type,
968                                ),
969                            }
970                        } else if *func == BinaryFunc::DatePartTimestampTz && expr1.is_literal() {
971                            let units = expr1.as_literal_str().unwrap();
972                            *e = match units.parse::<DateTimeUnits>() {
973                                Ok(units) => MirScalarExpr::CallUnary {
974                                    func: UnaryFunc::DatePartTimestampTz(
975                                        func::DatePartTimestampTz(units),
976                                    ),
977                                    expr: Box::new(expr2.take()),
978                                },
979                                Err(_) => MirScalarExpr::literal(
980                                    Err(EvalError::UnknownUnits(units.into())),
981                                    e.typ(column_types).scalar_type,
982                                ),
983                            }
984                        } else if *func == BinaryFunc::DateTruncTimestamp && expr1.is_literal() {
985                            let units = expr1.as_literal_str().unwrap();
986                            *e = match units.parse::<DateTimeUnits>() {
987                                Ok(units) => MirScalarExpr::CallUnary {
988                                    func: UnaryFunc::DateTruncTimestamp(func::DateTruncTimestamp(
989                                        units,
990                                    )),
991                                    expr: Box::new(expr2.take()),
992                                },
993                                Err(_) => MirScalarExpr::literal(
994                                    Err(EvalError::UnknownUnits(units.into())),
995                                    e.typ(column_types).scalar_type,
996                                ),
997                            }
998                        } else if *func == BinaryFunc::DateTruncTimestampTz && expr1.is_literal() {
999                            let units = expr1.as_literal_str().unwrap();
1000                            *e = match units.parse::<DateTimeUnits>() {
1001                                Ok(units) => MirScalarExpr::CallUnary {
1002                                    func: UnaryFunc::DateTruncTimestampTz(
1003                                        func::DateTruncTimestampTz(units),
1004                                    ),
1005                                    expr: Box::new(expr2.take()),
1006                                },
1007                                Err(_) => MirScalarExpr::literal(
1008                                    Err(EvalError::UnknownUnits(units.into())),
1009                                    e.typ(column_types).scalar_type,
1010                                ),
1011                            }
1012                        } else if *func == BinaryFunc::TimezoneTimestamp && expr1.is_literal() {
1013                            // If the timezone argument is a literal, and we're applying the function on many rows at the same
1014                            // time we really don't want to parse it again and again, so we parse it once and embed it into the
1015                            // UnaryFunc enum. The memory footprint of Timezone is small (8 bytes).
1016                            let tz = expr1.as_literal_str().unwrap();
1017                            *e = match parse_timezone(tz, TimezoneSpec::Posix) {
1018                                Ok(tz) => MirScalarExpr::CallUnary {
1019                                    func: UnaryFunc::TimezoneTimestamp(func::TimezoneTimestamp(tz)),
1020                                    expr: Box::new(expr2.take()),
1021                                },
1022                                Err(err) => MirScalarExpr::literal(
1023                                    Err(err),
1024                                    e.typ(column_types).scalar_type,
1025                                ),
1026                            }
1027                        } else if *func == BinaryFunc::TimezoneTimestampTz && expr1.is_literal() {
1028                            let tz = expr1.as_literal_str().unwrap();
1029                            *e = match parse_timezone(tz, TimezoneSpec::Posix) {
1030                                Ok(tz) => MirScalarExpr::CallUnary {
1031                                    func: UnaryFunc::TimezoneTimestampTz(
1032                                        func::TimezoneTimestampTz(tz),
1033                                    ),
1034                                    expr: Box::new(expr2.take()),
1035                                },
1036                                Err(err) => MirScalarExpr::literal(
1037                                    Err(err),
1038                                    e.typ(column_types).scalar_type,
1039                                ),
1040                            }
1041                        } else if *func == BinaryFunc::ToCharTimestamp && expr2.is_literal() {
1042                            let format_str = expr2.as_literal_str().unwrap();
1043                            *e = MirScalarExpr::CallUnary {
1044                                func: UnaryFunc::ToCharTimestamp(func::ToCharTimestamp {
1045                                    format_string: format_str.to_string(),
1046                                    format: DateTimeFormat::compile(format_str),
1047                                }),
1048                                expr: Box::new(expr1.take()),
1049                            };
1050                        } else if *func == BinaryFunc::ToCharTimestampTz && expr2.is_literal() {
1051                            let format_str = expr2.as_literal_str().unwrap();
1052                            *e = MirScalarExpr::CallUnary {
1053                                func: UnaryFunc::ToCharTimestampTz(func::ToCharTimestampTz {
1054                                    format_string: format_str.to_string(),
1055                                    format: DateTimeFormat::compile(format_str),
1056                                }),
1057                                expr: Box::new(expr1.take()),
1058                            };
1059                        } else if matches!(*func, BinaryFunc::Eq | BinaryFunc::NotEq)
1060                            && expr2 < expr1
1061                        {
1062                            // Canonically order elements so that deduplication works better.
1063                            // Also, the below `Literal([c1, c2]) = record_create(e1, e2)` matching
1064                            // relies on this canonical ordering.
1065                            mem::swap(expr1, expr2);
1066                        } else if let (
1067                            BinaryFunc::Eq,
1068                            MirScalarExpr::Literal(
1069                                Ok(lit_row),
1070                                ColumnType {
1071                                    scalar_type:
1072                                        ScalarType::Record {
1073                                            fields: field_types,
1074                                            ..
1075                                        },
1076                                    ..
1077                                },
1078                            ),
1079                            MirScalarExpr::CallVariadic {
1080                                func: VariadicFunc::RecordCreate { .. },
1081                                exprs: rec_create_args,
1082                            },
1083                        ) = (&*func, &**expr1, &**expr2)
1084                        {
1085                            // Literal([c1, c2]) = record_create(e1, e2)
1086                            //  -->
1087                            // c1 = e1 AND c2 = e2
1088                            //
1089                            // (Records are represented as lists.)
1090                            //
1091                            // `MapFilterProject::literal_constraints` relies on this transform,
1092                            // because `(e1,e2) IN ((1,2))` is desugared using `record_create`.
1093                            match lit_row.unpack_first() {
1094                                Datum::List(datum_list) => {
1095                                    *e = MirScalarExpr::CallVariadic {
1096                                        func: VariadicFunc::And,
1097                                        exprs: itertools::izip!(
1098                                            datum_list.iter(),
1099                                            field_types,
1100                                            rec_create_args
1101                                        )
1102                                        .map(|(d, (_, typ), a)| MirScalarExpr::CallBinary {
1103                                            func: BinaryFunc::Eq,
1104                                            expr1: Box::new(MirScalarExpr::Literal(
1105                                                Ok(Row::pack_slice(&[d])),
1106                                                typ.clone(),
1107                                            )),
1108                                            expr2: Box::new(a.clone()),
1109                                        })
1110                                        .collect(),
1111                                    };
1112                                }
1113                                _ => {}
1114                            }
1115                        } else if let (
1116                            BinaryFunc::Eq,
1117                            MirScalarExpr::CallVariadic {
1118                                func: VariadicFunc::RecordCreate { .. },
1119                                exprs: rec_create_args1,
1120                            },
1121                            MirScalarExpr::CallVariadic {
1122                                func: VariadicFunc::RecordCreate { .. },
1123                                exprs: rec_create_args2,
1124                            },
1125                        ) = (&*func, &**expr1, &**expr2)
1126                        {
1127                            // record_create(a1, a2, ...) = record_create(b1, b2, ...)
1128                            //  -->
1129                            // a1 = b1 AND a2 = b2 AND ...
1130                            //
1131                            // This is similar to the previous reduction, but this one kicks in also
1132                            // when only some (or none) of the record fields are literals. This
1133                            // enables the discovery of literal constraints for those fields.
1134                            //
1135                            // Note that there is a similar decomposition in
1136                            // `mz_sql::plan::transform_ast::Desugarer`, but that is earlier in the
1137                            // pipeline than the compilation of IN lists to `record_create`.
1138                            *e = MirScalarExpr::CallVariadic {
1139                                func: VariadicFunc::And,
1140                                exprs: rec_create_args1
1141                                    .into_iter()
1142                                    .zip(rec_create_args2)
1143                                    .map(|(a, b)| MirScalarExpr::CallBinary {
1144                                        func: BinaryFunc::Eq,
1145                                        expr1: Box::new(a.clone()),
1146                                        expr2: Box::new(b.clone()),
1147                                    })
1148                                    .collect(),
1149                            }
1150                        }
1151                    }
1152                    MirScalarExpr::CallVariadic { .. } => {
1153                        e.flatten_associative();
1154                        let (func, exprs) = match e {
1155                            MirScalarExpr::CallVariadic { func, exprs } => (func, exprs),
1156                            _ => unreachable!("`flatten_associative` shouldn't change node type"),
1157                        };
1158                        if *func == VariadicFunc::Coalesce {
1159                            // If all inputs are null, output is null. This check must
1160                            // be done before `exprs.retain...` because `e.typ` requires
1161                            // > 0 `exprs` remain.
1162                            if exprs.iter().all(|expr| expr.is_literal_null()) {
1163                                *e = MirScalarExpr::literal_null(e.typ(column_types).scalar_type);
1164                                return;
1165                            }
1166
1167                            // Remove any null values if not all values are null.
1168                            exprs.retain(|e| !e.is_literal_null());
1169
1170                            // Find the first argument that is a literal or non-nullable
1171                            // column. All arguments after it get ignored, so throw them
1172                            // away. This intentionally throws away errors that can
1173                            // never happen.
1174                            if let Some(i) = exprs
1175                                .iter()
1176                                .position(|e| e.is_literal() || !e.typ(column_types).nullable)
1177                            {
1178                                exprs.truncate(i + 1);
1179                            }
1180
1181                            // Deduplicate arguments in cases like `coalesce(#0, #0)`.
1182                            let mut prior_exprs = BTreeSet::new();
1183                            exprs.retain(|e| prior_exprs.insert(e.clone()));
1184
1185                            if let Some(expr) = exprs.iter_mut().find(|e| e.is_literal_err()) {
1186                                // One of the remaining arguments is an error, so
1187                                // just replace the entire coalesce with that error.
1188                                *e = expr.take();
1189                            } else if exprs.len() == 1 {
1190                                // Only one argument, so the coalesce is a no-op.
1191                                *e = exprs[0].take();
1192                            }
1193                        } else if exprs.iter().all(|e| e.is_literal()) {
1194                            *e = eval(e);
1195                        } else if func.propagates_nulls()
1196                            && exprs.iter().any(|e| e.is_literal_null())
1197                        {
1198                            *e = MirScalarExpr::literal_null(e.typ(column_types).scalar_type);
1199                        } else if let Some(err) = exprs.iter().find_map(|e| e.as_literal_err()) {
1200                            *e = MirScalarExpr::literal(
1201                                Err(err.clone()),
1202                                e.typ(column_types).scalar_type,
1203                            );
1204                        } else if *func == VariadicFunc::RegexpMatch
1205                            && exprs[1].is_literal()
1206                            && exprs.get(2).map_or(true, |e| e.is_literal())
1207                        {
1208                            let needle = exprs[1].as_literal_str().unwrap();
1209                            let flags = match exprs.len() {
1210                                3 => exprs[2].as_literal_str().unwrap(),
1211                                _ => "",
1212                            };
1213                            *e = match func::build_regex(needle, flags) {
1214                                Ok(regex) => mem::take(exprs)
1215                                    .into_first()
1216                                    .call_unary(UnaryFunc::RegexpMatch(func::RegexpMatch(regex))),
1217                                Err(err) => MirScalarExpr::literal(
1218                                    Err(err),
1219                                    e.typ(column_types).scalar_type,
1220                                ),
1221                            };
1222                        } else if *func == VariadicFunc::RegexpReplace
1223                            && exprs[1].is_literal()
1224                            && exprs.get(3).map_or(true, |e| e.is_literal())
1225                        {
1226                            let pattern = exprs[1].as_literal_str().unwrap();
1227                            let flags = exprs
1228                                .get(3)
1229                                .map_or("", |expr| expr.as_literal_str().unwrap());
1230                            let (limit, flags) = regexp_replace_parse_flags(flags);
1231
1232                            // The behavior of `regexp_replace` is that if the data is `NULL`, the
1233                            // function returns `NULL`, independently of whether the pattern or
1234                            // flags are correct. We need to check for this case and introduce an
1235                            // if-then-else on the error path to only surface the error if the first
1236                            // input is non-NULL.
1237                            *e = match func::build_regex(pattern, &flags) {
1238                                Ok(regex) => {
1239                                    let mut exprs = mem::take(exprs);
1240                                    let replacement = exprs.swap_remove(2);
1241                                    let source = exprs.swap_remove(0);
1242                                    source.call_binary(
1243                                        replacement,
1244                                        BinaryFunc::RegexpReplace { regex, limit },
1245                                    )
1246                                }
1247                                Err(err) => {
1248                                    let mut exprs = mem::take(exprs);
1249                                    let source = exprs.swap_remove(0);
1250                                    let scalar_type = e.typ(column_types).scalar_type;
1251                                    // We need to return `NULL` on `NULL` input, and error otherwise.
1252                                    source.call_is_null().if_then_else(
1253                                        MirScalarExpr::literal_null(scalar_type.clone()),
1254                                        MirScalarExpr::literal(Err(err), scalar_type),
1255                                    )
1256                                }
1257                            };
1258                        } else if *func == VariadicFunc::RegexpSplitToArray
1259                            && exprs[1].is_literal()
1260                            && exprs.get(2).map_or(true, |e| e.is_literal())
1261                        {
1262                            let needle = exprs[1].as_literal_str().unwrap();
1263                            let flags = match exprs.len() {
1264                                3 => exprs[2].as_literal_str().unwrap(),
1265                                _ => "",
1266                            };
1267                            *e = match func::build_regex(needle, flags) {
1268                                Ok(regex) => mem::take(exprs).into_first().call_unary(
1269                                    UnaryFunc::RegexpSplitToArray(func::RegexpSplitToArray(regex)),
1270                                ),
1271                                Err(err) => MirScalarExpr::literal(
1272                                    Err(err),
1273                                    e.typ(column_types).scalar_type,
1274                                ),
1275                            };
1276                        } else if *func == VariadicFunc::ListIndex && is_list_create_call(&exprs[0])
1277                        {
1278                            // We are looking for ListIndex(ListCreate, literal), and eliminate
1279                            // both the ListIndex and the ListCreate. E.g.: `LIST[f1,f2][2]` --> `f2`
1280                            let ind_exprs = exprs.split_off(1);
1281                            let top_list_create = exprs.swap_remove(0);
1282                            *e = reduce_list_create_list_index_literal(top_list_create, ind_exprs);
1283                        } else if *func == VariadicFunc::Or || *func == VariadicFunc::And {
1284                            // Note: It's important that we have called `flatten_associative` above.
1285                            e.undistribute_and_or();
1286                            e.reduce_and_canonicalize_and_or();
1287                        } else if let VariadicFunc::TimezoneTime = func {
1288                            if exprs[0].is_literal() && exprs[2].is_literal_ok() {
1289                                let tz = exprs[0].as_literal_str().unwrap();
1290                                *e = match parse_timezone(tz, TimezoneSpec::Posix) {
1291                                    Ok(tz) => MirScalarExpr::CallUnary {
1292                                        func: UnaryFunc::TimezoneTime(func::TimezoneTime {
1293                                            tz,
1294                                            wall_time: exprs[2]
1295                                                .as_literal()
1296                                                .unwrap()
1297                                                .unwrap()
1298                                                .unwrap_timestamptz()
1299                                                .naive_utc(),
1300                                        }),
1301                                        expr: Box::new(exprs[1].take()),
1302                                    },
1303                                    Err(err) => MirScalarExpr::literal(
1304                                        Err(err),
1305                                        e.typ(column_types).scalar_type,
1306                                    ),
1307                                }
1308                            }
1309                        }
1310                    }
1311                    MirScalarExpr::If { cond, then, els } => {
1312                        if let Some(literal) = cond.as_literal() {
1313                            match literal {
1314                                Ok(Datum::True) => *e = then.take(),
1315                                Ok(Datum::False) | Ok(Datum::Null) => *e = els.take(),
1316                                Err(err) => {
1317                                    *e = MirScalarExpr::Literal(
1318                                        Err(err.clone()),
1319                                        then.typ(column_types)
1320                                            .union(&els.typ(column_types))
1321                                            .unwrap(),
1322                                    )
1323                                }
1324                                _ => unreachable!(),
1325                            }
1326                        } else if then == els {
1327                            *e = then.take();
1328                        } else if then.is_literal_ok()
1329                            && els.is_literal_ok()
1330                            && then.typ(column_types).scalar_type == ScalarType::Bool
1331                            && els.typ(column_types).scalar_type == ScalarType::Bool
1332                        {
1333                            match (then.as_literal(), els.as_literal()) {
1334                                // Note: NULLs from the condition should not be propagated to the result
1335                                // of the expression.
1336                                (Some(Ok(Datum::True)), _) => {
1337                                    // Rewritten as ((<cond> IS NOT NULL) AND (<cond>)) OR (<els>)
1338                                    // NULL <cond> results in: (FALSE AND NULL) OR (<els>) => (<els>)
1339                                    *e = cond
1340                                        .clone()
1341                                        .call_is_null()
1342                                        .not()
1343                                        .and(cond.take())
1344                                        .or(els.take());
1345                                }
1346                                (Some(Ok(Datum::False)), _) => {
1347                                    // Rewritten as ((NOT <cond>) OR (<cond> IS NULL)) AND (<els>)
1348                                    // NULL <cond> results in: (NULL OR TRUE) AND (<els>) => TRUE AND (<els>) => (<els>)
1349                                    *e = cond
1350                                        .clone()
1351                                        .not()
1352                                        .or(cond.take().call_is_null())
1353                                        .and(els.take());
1354                                }
1355                                (_, Some(Ok(Datum::True))) => {
1356                                    // Rewritten as (NOT <cond>) OR (<cond> IS NULL) OR (<then>)
1357                                    // NULL <cond> results in: NULL OR TRUE OR (<then>) => TRUE
1358                                    *e = cond
1359                                        .clone()
1360                                        .not()
1361                                        .or(cond.take().call_is_null())
1362                                        .or(then.take());
1363                                }
1364                                (_, Some(Ok(Datum::False))) => {
1365                                    // Rewritten as (<cond> IS NOT NULL) AND (<cond>) AND (<then>)
1366                                    // NULL <cond> results in: FALSE AND NULL AND (<then>) => FALSE
1367                                    *e = cond
1368                                        .clone()
1369                                        .call_is_null()
1370                                        .not()
1371                                        .and(cond.take())
1372                                        .and(then.take());
1373                                }
1374                                _ => {}
1375                            }
1376                        } else {
1377                            // Equivalent expression structure would allow us to push the `If` into the expression.
1378                            // For example, `IF <cond> THEN x = y ELSE x = z` becomes `x = IF <cond> THEN y ELSE z`.
1379                            //
1380                            // We have to also make sure that the expressions that will end up in
1381                            // the two `If` branches have unionable types. Otherwise, the `If` could
1382                            // not be typed by `typ`. An example where this could cause an issue is
1383                            // when pulling out `cast_jsonbable_to_jsonb`, which accepts a wide
1384                            // range of input types. (In theory, we could still do the optimization
1385                            // in this case by inserting appropriate casts, but this corner case is
1386                            // not worth the complication for now.)
1387                            // See https://github.com/MaterializeInc/database-issues/issues/9182
1388                            match (&mut **then, &mut **els) {
1389                                (
1390                                    MirScalarExpr::CallUnary { func: f1, expr: e1 },
1391                                    MirScalarExpr::CallUnary { func: f2, expr: e2 },
1392                                ) if f1 == f2
1393                                    && e1
1394                                        .typ(column_types)
1395                                        .union(&e2.typ(column_types))
1396                                        .is_ok() =>
1397                                {
1398                                    *e = cond
1399                                        .take()
1400                                        .if_then_else(e1.take(), e2.take())
1401                                        .call_unary(f1.clone());
1402                                }
1403                                (
1404                                    MirScalarExpr::CallBinary {
1405                                        func: f1,
1406                                        expr1: e1a,
1407                                        expr2: e2a,
1408                                    },
1409                                    MirScalarExpr::CallBinary {
1410                                        func: f2,
1411                                        expr1: e1b,
1412                                        expr2: e2b,
1413                                    },
1414                                ) if f1 == f2
1415                                    && e1a == e1b
1416                                    && e2a
1417                                        .typ(column_types)
1418                                        .union(&e2b.typ(column_types))
1419                                        .is_ok() =>
1420                                {
1421                                    *e = e1a.take().call_binary(
1422                                        cond.take().if_then_else(e2a.take(), e2b.take()),
1423                                        f1.clone(),
1424                                    );
1425                                }
1426                                (
1427                                    MirScalarExpr::CallBinary {
1428                                        func: f1,
1429                                        expr1: e1a,
1430                                        expr2: e2a,
1431                                    },
1432                                    MirScalarExpr::CallBinary {
1433                                        func: f2,
1434                                        expr1: e1b,
1435                                        expr2: e2b,
1436                                    },
1437                                ) if f1 == f2
1438                                    && e2a == e2b
1439                                    && e1a
1440                                        .typ(column_types)
1441                                        .union(&e1b.typ(column_types))
1442                                        .is_ok() =>
1443                                {
1444                                    *e = cond
1445                                        .take()
1446                                        .if_then_else(e1a.take(), e1b.take())
1447                                        .call_binary(e2a.take(), f1.clone());
1448                                }
1449                                _ => {}
1450                            }
1451                        }
1452                    }
1453                },
1454            );
1455        }
1456
1457        /* #region `reduce_list_create_list_index_literal` and helper functions */
1458
1459        fn list_create_type(list_create: &MirScalarExpr) -> ScalarType {
1460            if let MirScalarExpr::CallVariadic {
1461                func: VariadicFunc::ListCreate { elem_type: typ },
1462                ..
1463            } = list_create
1464            {
1465                (*typ).clone()
1466            } else {
1467                unreachable!()
1468            }
1469        }
1470
1471        fn is_list_create_call(expr: &MirScalarExpr) -> bool {
1472            matches!(
1473                expr,
1474                MirScalarExpr::CallVariadic {
1475                    func: VariadicFunc::ListCreate { .. },
1476                    ..
1477                }
1478            )
1479        }
1480
1481        /// Partial-evaluates a list indexing with a literal directly after a list creation.
1482        ///
1483        /// Multi-dimensional lists are handled by a single call to this function, with multiple
1484        /// elements in index_exprs (of which not all need to be literals), and nested ListCreates
1485        /// in list_create_to_reduce.
1486        ///
1487        /// # Examples
1488        ///
1489        /// `LIST[f1,f2][2]` --> `f2`.
1490        ///
1491        /// A multi-dimensional list, with only some of the indexes being literals:
1492        /// `LIST[[[f1, f2], [f3, f4]], [[f5, f6], [f7, f8]]] [2][n][2]` --> `LIST[f6, f8] [n]`
1493        ///
1494        /// See more examples in list.slt.
1495        fn reduce_list_create_list_index_literal(
1496            mut list_create_to_reduce: MirScalarExpr,
1497            mut index_exprs: Vec<MirScalarExpr>,
1498        ) -> MirScalarExpr {
1499            // We iterate over the index_exprs and remove literals, but keep non-literals.
1500            // When we encounter a non-literal, we need to dig into the nested ListCreates:
1501            // `list_create_mut_refs` will contain all the ListCreates of the current level. If an
1502            // element of `list_create_mut_refs` is not actually a ListCreate, then we break out of
1503            // the loop. When we remove a literal, we need to partial-evaluate all ListCreates
1504            // that are at the current level (except those that disappeared due to
1505            // literals at earlier levels), and change each element in `list_create_mut_refs`
1506            // to the result of the partial evaluation.
1507            let mut list_create_mut_refs = vec![&mut list_create_to_reduce];
1508            let mut i = 0;
1509            while i < index_exprs.len()
1510                && list_create_mut_refs
1511                    .iter()
1512                    .all(|lc| is_list_create_call(lc))
1513            {
1514                if index_exprs[i].is_literal_ok() {
1515                    // We can remove this index.
1516                    let removed_index = index_exprs.remove(i);
1517                    let index_i64 = match removed_index.as_literal().unwrap().unwrap() {
1518                        Datum::Int64(sql_index_i64) => sql_index_i64 - 1,
1519                        _ => unreachable!(), // always an Int64, see plan_index_list
1520                    };
1521                    // For each list_create referenced by list_create_mut_refs, substitute it by its
1522                    // `index`th argument (or null).
1523                    for list_create in &mut list_create_mut_refs {
1524                        let list_create_args = match list_create {
1525                            MirScalarExpr::CallVariadic {
1526                                func: VariadicFunc::ListCreate { .. },
1527                                exprs,
1528                            } => exprs,
1529                            _ => unreachable!(), // func cannot be anything else than a ListCreate
1530                        };
1531                        // ListIndex gives null on an out-of-bounds index
1532                        if index_i64 >= 0 && index_i64 < list_create_args.len().try_into().unwrap()
1533                        {
1534                            let index: usize = index_i64.try_into().unwrap();
1535                            **list_create = list_create_args.swap_remove(index);
1536                        } else {
1537                            let typ = list_create_type(list_create);
1538                            **list_create = MirScalarExpr::literal_null(typ);
1539                        }
1540                    }
1541                } else {
1542                    // We can't remove this index, so we can't reduce any of the ListCreates at this
1543                    // level. So we change list_create_mut_refs to refer to all the arguments of all
1544                    // the ListCreates currently referenced by list_create_mut_refs.
1545                    list_create_mut_refs = list_create_mut_refs
1546                        .into_iter()
1547                        .flat_map(|list_create| match list_create {
1548                            MirScalarExpr::CallVariadic {
1549                                func: VariadicFunc::ListCreate { .. },
1550                                exprs: list_create_args,
1551                            } => list_create_args,
1552                            // func cannot be anything else than a ListCreate
1553                            _ => unreachable!(),
1554                        })
1555                        .collect();
1556                    i += 1; // next index_expr
1557                }
1558            }
1559            // If all list indexes have been evaluated, return the reduced expression.
1560            // Otherwise, rebuild the ListIndex call with the remaining ListCreates and indexes.
1561            if index_exprs.is_empty() {
1562                assert_eq!(list_create_mut_refs.len(), 1);
1563                list_create_to_reduce
1564            } else {
1565                let mut exprs: Vec<MirScalarExpr> = vec![list_create_to_reduce];
1566                exprs.append(&mut index_exprs);
1567                MirScalarExpr::CallVariadic {
1568                    func: VariadicFunc::ListIndex,
1569                    exprs,
1570                }
1571            }
1572        }
1573
1574        /* #endregion */
1575    }
1576
1577    /// Decompose an IsNull expression into a disjunction of
1578    /// simpler expressions.
1579    ///
1580    /// Assumes that `self` is the expression inside of an IsNull.
1581    /// Returns `Some(expressions)` if the outer IsNull is to be
1582    /// replaced by some other expression. Note: if it returns
1583    /// None, it might still have mutated *self.
1584    fn decompose_is_null(&mut self) -> Option<MirScalarExpr> {
1585        // TODO: allow simplification of unmaterializable functions
1586
1587        match self {
1588            MirScalarExpr::CallUnary {
1589                func,
1590                expr: inner_expr,
1591            } => {
1592                if !func.introduces_nulls() {
1593                    if func.propagates_nulls() {
1594                        *self = inner_expr.take();
1595                        return self.decompose_is_null();
1596                    } else {
1597                        // Different from CallBinary and CallVariadic, because of determinism. See
1598                        // https://materializeinc.slack.com/archives/C01BE3RN82F/p1657644478517709
1599                        return Some(MirScalarExpr::literal_false());
1600                    }
1601                }
1602            }
1603            MirScalarExpr::CallBinary { func, expr1, expr2 } => {
1604                // (<expr1> <op> <expr2>) IS NULL can often be simplified to
1605                // (<expr1> IS NULL) OR (<expr2> IS NULL).
1606                if func.propagates_nulls() && !func.introduces_nulls() {
1607                    let expr1 = expr1.take().call_is_null();
1608                    let expr2 = expr2.take().call_is_null();
1609                    return Some(expr1.or(expr2));
1610                }
1611            }
1612            MirScalarExpr::CallVariadic { func, exprs } => {
1613                if func.propagates_nulls() && !func.introduces_nulls() {
1614                    let exprs = exprs.into_iter().map(|e| e.take().call_is_null()).collect();
1615                    return Some(MirScalarExpr::CallVariadic {
1616                        func: VariadicFunc::Or,
1617                        exprs,
1618                    });
1619                }
1620            }
1621            _ => {}
1622        }
1623
1624        None
1625    }
1626
1627    /// Flattens a chain of calls to associative variadic functions
1628    /// (For example: ORs or ANDs)
1629    pub fn flatten_associative(&mut self) {
1630        match self {
1631            MirScalarExpr::CallVariadic {
1632                exprs: outer_operands,
1633                func: outer_func,
1634            } if outer_func.is_associative() => {
1635                *outer_operands = outer_operands
1636                    .into_iter()
1637                    .flat_map(|o| {
1638                        if let MirScalarExpr::CallVariadic {
1639                            exprs: inner_operands,
1640                            func: inner_func,
1641                        } = o
1642                        {
1643                            if *inner_func == *outer_func {
1644                                mem::take(inner_operands)
1645                            } else {
1646                                vec![o.take()]
1647                            }
1648                        } else {
1649                            vec![o.take()]
1650                        }
1651                    })
1652                    .collect();
1653            }
1654            _ => {}
1655        }
1656    }
1657
1658    /* #region AND/OR canonicalization and transformations  */
1659
1660    /// Canonicalizes AND/OR, and does some straightforward simplifications
1661    fn reduce_and_canonicalize_and_or(&mut self) {
1662        // We do this until fixed point, because after undistribute_and_or calls us, it relies on
1663        // the property that self is not an 1-arg AND/OR. Just one application of our loop body
1664        // can't ensure this, because the application itself might create a 1-arg AND/OR.
1665        let mut old_self = MirScalarExpr::column(0);
1666        while old_self != *self {
1667            old_self = self.clone();
1668            match self {
1669                MirScalarExpr::CallVariadic {
1670                    func: func @ (VariadicFunc::And | VariadicFunc::Or),
1671                    exprs,
1672                } => {
1673                    // Canonically order elements so that various deduplications work better,
1674                    // e.g., in undistribute_and_or.
1675                    // Also, extract_equal_or_both_null_inner depends on the args being sorted.
1676                    exprs.sort();
1677
1678                    // x AND/OR x --> x
1679                    exprs.dedup(); // this also needs the above sorting
1680
1681                    if exprs.len() == 1 {
1682                        // AND/OR of 1 argument evaluates to that argument
1683                        *self = exprs.swap_remove(0);
1684                    } else if exprs.len() == 0 {
1685                        // AND/OR of 0 arguments evaluates to true/false
1686                        *self = func.unit_of_and_or();
1687                    } else if exprs.iter().any(|e| *e == func.zero_of_and_or()) {
1688                        // short-circuiting
1689                        *self = func.zero_of_and_or();
1690                    } else {
1691                        // a AND true --> a
1692                        // a OR false --> a
1693                        exprs.retain(|e| *e != func.unit_of_and_or());
1694                    }
1695                }
1696                _ => {}
1697            }
1698        }
1699    }
1700
1701    /// Transforms !(a && b) into !a || !b, and !(a || b) into !a && !b
1702    fn demorgans(&mut self) {
1703        if let MirScalarExpr::CallUnary {
1704            expr: inner,
1705            func: UnaryFunc::Not(func::Not),
1706        } = self
1707        {
1708            inner.flatten_associative();
1709            match &mut **inner {
1710                MirScalarExpr::CallVariadic {
1711                    func: inner_func @ (VariadicFunc::And | VariadicFunc::Or),
1712                    exprs,
1713                } => {
1714                    *inner_func = inner_func.switch_and_or();
1715                    *exprs = exprs.into_iter().map(|e| e.take().not()).collect();
1716                    *self = (*inner).take(); // Removes the outer not
1717                }
1718                _ => {}
1719            }
1720        }
1721    }
1722
1723    /// AND/OR undistribution (factoring out) to apply at each `MirScalarExpr`.
1724    ///
1725    /// This method attempts to apply one of the [distribution laws][distributivity]
1726    /// (in a direction opposite to the their name):
1727    /// ```text
1728    /// (a && b) || (a && c) --> a && (b || c)  // Undistribute-OR
1729    /// (a || b) && (a || c) --> a || (b && c)  // Undistribute-AND
1730    /// ```
1731    /// or one of their corresponding two [absorption law][absorption] special
1732    /// cases:
1733    /// ```text
1734    /// a || (a && c)  -->  a  // Absorb-OR
1735    /// a && (a || c)  -->  a  // Absorb-AND
1736    /// ```
1737    ///
1738    /// The method also works with more than 2 arguments at the top, e.g.
1739    /// ```text
1740    /// (a && b) || (a && c) || (a && d)  -->  a && (b || c || d)
1741    /// ```
1742    /// It can also factor out only a subset of the top arguments, e.g.
1743    /// ```text
1744    /// (a && b) || (a && c) || (d && e)  -->  (a && (b || c)) || (d && e)
1745    /// ```
1746    ///
1747    /// Note that sometimes there are two overlapping possibilities to factor
1748    /// out from, e.g.
1749    /// ```text
1750    /// (a && b) || (a && c) || (d && c)
1751    /// ```
1752    /// Here we can factor out `a` from from the 1. and 2. terms, or we can
1753    /// factor out `c` from the 2. and 3. terms. One of these might lead to
1754    /// more/better undistribution opportunities later, but we just pick one
1755    /// locally, because recursively trying out all of them would lead to
1756    /// exponential run time.
1757    ///
1758    /// The local heuristic is that we prefer a candidate that leads to an
1759    /// absorption, or if there is no such one then we simply pick the first. In
1760    /// case of multiple absorption candidates, it doesn't matter which one we
1761    /// pick, because applying an absorption cannot adversely effect the
1762    /// possibility of applying other absorptions.
1763    ///
1764    /// # Assumption
1765    ///
1766    /// Assumes that nested chains of AND/OR applications are flattened (this
1767    /// can be enforced with [`Self::flatten_associative`]).
1768    ///
1769    /// # Examples
1770    ///
1771    /// Absorb-OR:
1772    /// ```text
1773    /// a || (a && c) || (a && d)
1774    /// -->
1775    /// a && (true || c || d)
1776    /// -->
1777    /// a && true
1778    /// -->
1779    /// a
1780    /// ```
1781    /// Here only the first step is performed by this method. The rest is done
1782    /// by [`Self::reduce_and_canonicalize_and_or`] called after us in
1783    /// `reduce()`.
1784    ///
1785    /// [distributivity]: https://en.wikipedia.org/wiki/Distributive_property
1786    /// [absorption]: https://en.wikipedia.org/wiki/Absorption_law
1787    fn undistribute_and_or(&mut self) {
1788        // It wouldn't be strictly necessary to wrap this fn in this loop, because `reduce()` calls
1789        // us in a loop anyway. However, `reduce()` tries to do many other things, so the loop here
1790        // improves performance when there are several undistributions to apply in sequence, which
1791        // can occur in `CanonicalizeMfp` when undoing the DNF.
1792        let mut old_self = MirScalarExpr::column(0);
1793        while old_self != *self {
1794            old_self = self.clone();
1795            self.reduce_and_canonicalize_and_or(); // We don't want to deal with 1-arg AND/OR at the top
1796            if let MirScalarExpr::CallVariadic {
1797                exprs: outer_operands,
1798                func: outer_func @ (VariadicFunc::Or | VariadicFunc::And),
1799            } = self
1800            {
1801                let inner_func = outer_func.switch_and_or();
1802
1803                // Make sure that each outer operand is a call to inner_func, by wrapping in a 1-arg
1804                // call if necessary.
1805                outer_operands.iter_mut().for_each(|o| {
1806                    if !matches!(o, MirScalarExpr::CallVariadic {func: f, ..} if *f == inner_func) {
1807                        *o = MirScalarExpr::CallVariadic {
1808                            func: inner_func.clone(),
1809                            exprs: vec![o.take()],
1810                        };
1811                    }
1812                });
1813
1814                let mut inner_operands_refs: Vec<&mut Vec<MirScalarExpr>> = outer_operands
1815                    .iter_mut()
1816                    .map(|o| match o {
1817                        MirScalarExpr::CallVariadic { func: f, exprs } if *f == inner_func => exprs,
1818                        _ => unreachable!(), // the wrapping made sure that we'll get a match
1819                    })
1820                    .collect();
1821
1822                // Find inner operands to undistribute, i.e., which are in _all_ of the outer operands.
1823                let mut intersection = inner_operands_refs
1824                    .iter()
1825                    .map(|v| (*v).clone())
1826                    .reduce(|ops1, ops2| ops1.into_iter().filter(|e| ops2.contains(e)).collect())
1827                    .unwrap();
1828                intersection.sort();
1829                intersection.dedup();
1830
1831                if !intersection.is_empty() {
1832                    // Factor out the intersection from all the top-level args.
1833
1834                    // Remove the intersection from each inner operand vector.
1835                    inner_operands_refs
1836                        .iter_mut()
1837                        .for_each(|ops| (**ops).retain(|o| !intersection.contains(o)));
1838
1839                    // Simplify terms that now have only 0 or 1 args due to removing the intersection.
1840                    outer_operands
1841                        .iter_mut()
1842                        .for_each(|o| o.reduce_and_canonicalize_and_or());
1843
1844                    // Add the intersection at the beginning
1845                    *self = MirScalarExpr::CallVariadic {
1846                        func: inner_func,
1847                        exprs: intersection.into_iter().chain_one(self.clone()).collect(),
1848                    };
1849                } else {
1850                    // If the intersection was empty, that means that there is nothing we can factor out
1851                    // from _all_ the top-level args. However, we might still find something to factor
1852                    // out from a subset of the top-level args. To find such an opportunity, we look for
1853                    // duplicates across all inner args, e.g. if we have
1854                    // `(...) OR (... AND `a` AND ...) OR (...) OR (... AND `a` AND ...)`
1855                    // then we'll find that `a` occurs in more than one top-level arg, so
1856                    // `indexes_to_undistribute` will point us to the 2. and 4. top-level args.
1857
1858                    // Create (inner_operand, index) pairs, where the index is the position in
1859                    // outer_operands
1860                    let all_inner_operands = inner_operands_refs
1861                        .iter()
1862                        .enumerate()
1863                        .flat_map(|(i, inner_vec)| inner_vec.iter().map(move |a| ((*a).clone(), i)))
1864                        .sorted()
1865                        .collect_vec();
1866
1867                    // Find inner operand expressions that occur in more than one top-level arg.
1868                    // Each inner vector in `undistribution_opportunities` will belong to one such inner
1869                    // operand expression, and it is a set of indexes pointing to top-level args where
1870                    // that inner operand occurs.
1871                    let undistribution_opportunities = all_inner_operands
1872                        .iter()
1873                        .group_by(|(a, _i)| a)
1874                        .into_iter()
1875                        .map(|(_a, g)| g.map(|(_a, i)| *i).sorted().dedup().collect_vec())
1876                        .filter(|g| g.len() > 1)
1877                        .collect_vec();
1878
1879                    // Choose one of the inner vectors from `undistribution_opportunities`.
1880                    let indexes_to_undistribute = undistribution_opportunities
1881                        .iter()
1882                        // Let's prefer index sets that directly lead to an absorption.
1883                        .find(|index_set| {
1884                            index_set
1885                                .iter()
1886                                .any(|i| inner_operands_refs.get(*i).unwrap().len() == 1)
1887                        })
1888                        // If we didn't find any absorption, then any index set will do.
1889                        .or_else(|| undistribution_opportunities.first())
1890                        .cloned();
1891
1892                    // In any case, undo the 1-arg wrapping that we did at the beginning.
1893                    outer_operands
1894                        .iter_mut()
1895                        .for_each(|o| o.reduce_and_canonicalize_and_or());
1896
1897                    if let Some(indexes_to_undistribute) = indexes_to_undistribute {
1898                        // Found something to undistribute from a subset of the outer operands.
1899                        // We temporarily remove these from outer_operands, call ourselves on it, and
1900                        // then push back the result.
1901                        let mut undistribute_from = MirScalarExpr::CallVariadic {
1902                            func: outer_func.clone(),
1903                            exprs: swap_remove_multiple(outer_operands, indexes_to_undistribute),
1904                        };
1905                        // By construction, the recursive call is guaranteed to hit
1906                        // the `!intersection.is_empty()` branch.
1907                        undistribute_from.undistribute_and_or();
1908                        // Append the undistributed result to outer operands that were not included in
1909                        // indexes_to_undistribute.
1910                        outer_operands.push(undistribute_from);
1911                    }
1912                }
1913            }
1914        }
1915    }
1916
1917    /* #endregion */
1918
1919    /// Adds any columns that *must* be non-Null for `self` to be non-Null.
1920    pub fn non_null_requirements(&self, columns: &mut BTreeSet<usize>) {
1921        match self {
1922            MirScalarExpr::Column(col) => {
1923                columns.insert(*col);
1924            }
1925            MirScalarExpr::Literal(..) => {}
1926            MirScalarExpr::CallUnmaterializable(_) => (),
1927            MirScalarExpr::CallUnary { func, expr } => {
1928                if func.propagates_nulls() {
1929                    expr.non_null_requirements(columns);
1930                }
1931            }
1932            MirScalarExpr::CallBinary { func, expr1, expr2 } => {
1933                if func.propagates_nulls() {
1934                    expr1.non_null_requirements(columns);
1935                    expr2.non_null_requirements(columns);
1936                }
1937            }
1938            MirScalarExpr::CallVariadic { func, exprs } => {
1939                if func.propagates_nulls() {
1940                    for expr in exprs {
1941                        expr.non_null_requirements(columns);
1942                    }
1943                }
1944            }
1945            MirScalarExpr::If { .. } => (),
1946        }
1947    }
1948
1949    pub fn typ(&self, column_types: &[ColumnType]) -> ColumnType {
1950        match self {
1951            MirScalarExpr::Column(i) => column_types[*i].clone(),
1952            MirScalarExpr::Literal(_, typ) => typ.clone(),
1953            MirScalarExpr::CallUnmaterializable(func) => func.output_type(),
1954            MirScalarExpr::CallUnary { expr, func } => func.output_type(expr.typ(column_types)),
1955            MirScalarExpr::CallBinary { expr1, expr2, func } => {
1956                func.output_type(expr1.typ(column_types), expr2.typ(column_types))
1957            }
1958            MirScalarExpr::CallVariadic { exprs, func } => {
1959                func.output_type(exprs.iter().map(|e| e.typ(column_types)).collect())
1960            }
1961            MirScalarExpr::If { cond: _, then, els } => {
1962                let then_type = then.typ(column_types);
1963                let else_type = els.typ(column_types);
1964                then_type.union(&else_type).unwrap()
1965            }
1966        }
1967    }
1968
1969    pub fn eval<'a>(
1970        &'a self,
1971        datums: &[Datum<'a>],
1972        temp_storage: &'a RowArena,
1973    ) -> Result<Datum<'a>, EvalError> {
1974        match self {
1975            MirScalarExpr::Column(index) => Ok(datums[*index].clone()),
1976            MirScalarExpr::Literal(res, _column_type) => match res {
1977                Ok(row) => Ok(row.unpack_first()),
1978                Err(e) => Err(e.clone()),
1979            },
1980            // Unmaterializable functions must be transformed away before
1981            // evaluation. Their purpose is as a placeholder for data that is
1982            // not known at plan time but can be inlined before runtime.
1983            MirScalarExpr::CallUnmaterializable(x) => Err(EvalError::Internal(
1984                format!("cannot evaluate unmaterializable function: {:?}", x).into(),
1985            )),
1986            MirScalarExpr::CallUnary { func, expr } => func.eval(datums, temp_storage, expr),
1987            MirScalarExpr::CallBinary { func, expr1, expr2 } => {
1988                func.eval(datums, temp_storage, expr1, expr2)
1989            }
1990            MirScalarExpr::CallVariadic { func, exprs } => func.eval(datums, temp_storage, exprs),
1991            MirScalarExpr::If { cond, then, els } => match cond.eval(datums, temp_storage)? {
1992                Datum::True => then.eval(datums, temp_storage),
1993                Datum::False | Datum::Null => els.eval(datums, temp_storage),
1994                d => Err(EvalError::Internal(
1995                    format!("if condition evaluated to non-boolean datum: {:?}", d).into(),
1996                )),
1997            },
1998        }
1999    }
2000
2001    /// True iff the expression contains
2002    /// `UnmaterializableFunc::MzNow`.
2003    pub fn contains_temporal(&self) -> bool {
2004        let mut contains = false;
2005        self.visit_pre(|e| {
2006            if let MirScalarExpr::CallUnmaterializable(UnmaterializableFunc::MzNow) = e {
2007                contains = true;
2008            }
2009        });
2010        contains
2011    }
2012
2013    /// True iff the expression contains an `UnmaterializableFunc`.
2014    pub fn contains_unmaterializable(&self) -> bool {
2015        let mut contains = false;
2016        self.visit_pre(|e| {
2017            if let MirScalarExpr::CallUnmaterializable(_) = e {
2018                contains = true;
2019            }
2020        });
2021        contains
2022    }
2023
2024    /// True iff the expression contains an `UnmaterializableFunc` that is not in the `exceptions`
2025    /// list.
2026    pub fn contains_unmaterializable_except(&self, exceptions: &[UnmaterializableFunc]) -> bool {
2027        let mut contains = false;
2028        self.visit_pre(|e| match e {
2029            MirScalarExpr::CallUnmaterializable(f) if !exceptions.contains(f) => contains = true,
2030            _ => (),
2031        });
2032        contains
2033    }
2034
2035    /// True iff the expression contains a `Column`.
2036    pub fn contains_column(&self) -> bool {
2037        let mut contains = false;
2038        self.visit_pre(|e| {
2039            if let MirScalarExpr::Column(_) = e {
2040                contains = true;
2041            }
2042        });
2043        contains
2044    }
2045
2046    /// True iff the expression contains a `Dummy`.
2047    pub fn contains_dummy(&self) -> bool {
2048        let mut contains = false;
2049        self.visit_pre(|e| {
2050            if let MirScalarExpr::Literal(row, _) = e {
2051                if let Ok(row) = row {
2052                    contains |= row.iter().any(|d| d == Datum::Dummy);
2053                }
2054            }
2055        });
2056        contains
2057    }
2058
2059    /// The size of the expression as a tree.
2060    pub fn size(&self) -> usize {
2061        let mut size = 0;
2062        self.visit_pre(&mut |_: &MirScalarExpr| {
2063            size += 1;
2064        });
2065        size
2066    }
2067}
2068
2069impl MirScalarExpr {
2070    /// True iff evaluation could possibly error on non-error input `Datum`.
2071    pub fn could_error(&self) -> bool {
2072        match self {
2073            MirScalarExpr::Column(_col) => false,
2074            MirScalarExpr::Literal(row, ..) => row.is_err(),
2075            MirScalarExpr::CallUnmaterializable(_) => true,
2076            MirScalarExpr::CallUnary { func, expr } => func.could_error() || expr.could_error(),
2077            MirScalarExpr::CallBinary { func, expr1, expr2 } => {
2078                func.could_error() || expr1.could_error() || expr2.could_error()
2079            }
2080            MirScalarExpr::CallVariadic { func, exprs } => {
2081                func.could_error() || exprs.iter().any(|e| e.could_error())
2082            }
2083            MirScalarExpr::If { cond, then, els } => {
2084                cond.could_error() || then.could_error() || els.could_error()
2085            }
2086        }
2087    }
2088}
2089
2090impl VisitChildren<Self> for MirScalarExpr {
2091    fn visit_children<F>(&self, mut f: F)
2092    where
2093        F: FnMut(&Self),
2094    {
2095        use MirScalarExpr::*;
2096        match self {
2097            Column(_) | Literal(_, _) | CallUnmaterializable(_) => (),
2098            CallUnary { expr, .. } => {
2099                f(expr);
2100            }
2101            CallBinary { expr1, expr2, .. } => {
2102                f(expr1);
2103                f(expr2);
2104            }
2105            CallVariadic { exprs, .. } => {
2106                for expr in exprs {
2107                    f(expr);
2108                }
2109            }
2110            If { cond, then, els } => {
2111                f(cond);
2112                f(then);
2113                f(els);
2114            }
2115        }
2116    }
2117
2118    fn visit_mut_children<F>(&mut self, mut f: F)
2119    where
2120        F: FnMut(&mut Self),
2121    {
2122        use MirScalarExpr::*;
2123        match self {
2124            Column(_) | Literal(_, _) | CallUnmaterializable(_) => (),
2125            CallUnary { expr, .. } => {
2126                f(expr);
2127            }
2128            CallBinary { expr1, expr2, .. } => {
2129                f(expr1);
2130                f(expr2);
2131            }
2132            CallVariadic { exprs, .. } => {
2133                for expr in exprs {
2134                    f(expr);
2135                }
2136            }
2137            If { cond, then, els } => {
2138                f(cond);
2139                f(then);
2140                f(els);
2141            }
2142        }
2143    }
2144
2145    fn try_visit_children<F, E>(&self, mut f: F) -> Result<(), E>
2146    where
2147        F: FnMut(&Self) -> Result<(), E>,
2148        E: From<RecursionLimitError>,
2149    {
2150        use MirScalarExpr::*;
2151        match self {
2152            Column(_) | Literal(_, _) | CallUnmaterializable(_) => (),
2153            CallUnary { expr, .. } => {
2154                f(expr)?;
2155            }
2156            CallBinary { expr1, expr2, .. } => {
2157                f(expr1)?;
2158                f(expr2)?;
2159            }
2160            CallVariadic { exprs, .. } => {
2161                for expr in exprs {
2162                    f(expr)?;
2163                }
2164            }
2165            If { cond, then, els } => {
2166                f(cond)?;
2167                f(then)?;
2168                f(els)?;
2169            }
2170        }
2171        Ok(())
2172    }
2173
2174    fn try_visit_mut_children<F, E>(&mut self, mut f: F) -> Result<(), E>
2175    where
2176        F: FnMut(&mut Self) -> Result<(), E>,
2177        E: From<RecursionLimitError>,
2178    {
2179        use MirScalarExpr::*;
2180        match self {
2181            Column(_) | Literal(_, _) | CallUnmaterializable(_) => (),
2182            CallUnary { expr, .. } => {
2183                f(expr)?;
2184            }
2185            CallBinary { expr1, expr2, .. } => {
2186                f(expr1)?;
2187                f(expr2)?;
2188            }
2189            CallVariadic { exprs, .. } => {
2190                for expr in exprs {
2191                    f(expr)?;
2192                }
2193            }
2194            If { cond, then, els } => {
2195                f(cond)?;
2196                f(then)?;
2197                f(els)?;
2198            }
2199        }
2200        Ok(())
2201    }
2202}
2203
2204impl MirScalarExpr {
2205    /// Iterates through references to child expressions.
2206    pub fn children(&self) -> impl DoubleEndedIterator<Item = &Self> {
2207        let mut first = None;
2208        let mut second = None;
2209        let mut third = None;
2210        let mut variadic = None;
2211
2212        use MirScalarExpr::*;
2213        match self {
2214            Column(_) | Literal(_, _) | CallUnmaterializable(_) => (),
2215            CallUnary { expr, .. } => {
2216                first = Some(&**expr);
2217            }
2218            CallBinary { expr1, expr2, .. } => {
2219                first = Some(&**expr1);
2220                second = Some(&**expr2);
2221            }
2222            CallVariadic { exprs, .. } => {
2223                variadic = Some(exprs);
2224            }
2225            If { cond, then, els } => {
2226                first = Some(&**cond);
2227                second = Some(&**then);
2228                third = Some(&**els);
2229            }
2230        }
2231
2232        first
2233            .into_iter()
2234            .chain(second)
2235            .chain(third)
2236            .chain(variadic.into_iter().flatten())
2237    }
2238
2239    /// Iterates through mutable references to child expressions.
2240    pub fn children_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Self> {
2241        let mut first = None;
2242        let mut second = None;
2243        let mut third = None;
2244        let mut variadic = None;
2245
2246        use MirScalarExpr::*;
2247        match self {
2248            Column(_) | Literal(_, _) | CallUnmaterializable(_) => (),
2249            CallUnary { expr, .. } => {
2250                first = Some(&mut **expr);
2251            }
2252            CallBinary { expr1, expr2, .. } => {
2253                first = Some(&mut **expr1);
2254                second = Some(&mut **expr2);
2255            }
2256            CallVariadic { exprs, .. } => {
2257                variadic = Some(exprs);
2258            }
2259            If { cond, then, els } => {
2260                first = Some(&mut **cond);
2261                second = Some(&mut **then);
2262                third = Some(&mut **els);
2263            }
2264        }
2265
2266        first
2267            .into_iter()
2268            .chain(second)
2269            .chain(third)
2270            .chain(variadic.into_iter().flatten())
2271    }
2272
2273    /// Visits all subexpressions in DFS preorder.
2274    pub fn visit_pre<F>(&self, mut f: F)
2275    where
2276        F: FnMut(&Self),
2277    {
2278        let mut worklist = vec![self];
2279        while let Some(e) = worklist.pop() {
2280            f(e);
2281            worklist.extend(e.children().rev());
2282        }
2283    }
2284
2285    /// Iterative pre-order visitor.
2286    pub fn visit_pre_mut<F: FnMut(&mut Self)>(&mut self, mut f: F) {
2287        let mut worklist = vec![self];
2288        while let Some(expr) = worklist.pop() {
2289            f(expr);
2290            worklist.extend(expr.children_mut().rev());
2291        }
2292    }
2293}
2294
2295/// Filter characteristics that are used for ordering join inputs.
2296/// This can be created for a `Vec<MirScalarExpr>`, which represents an AND of predicates.
2297///
2298/// The fields are ordered based on heuristic assumptions about their typical selectivity, so that
2299/// Ord gives the right ordering for join inputs. Bigger is better, i.e., will tend to come earlier
2300/// than other inputs.
2301#[derive(
2302    Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Serialize, Deserialize, Hash, MzReflect, Arbitrary,
2303)]
2304pub struct FilterCharacteristics {
2305    // `<expr> = <literal>` appears in the filter.
2306    // Excludes cases where NOT appears anywhere above the literal equality.
2307    literal_equality: bool,
2308    // (Assuming a random string of lower-case characters, `LIKE 'a%'` has a selectivity of 1/26.)
2309    like: bool,
2310    is_null: bool,
2311    // Number of Vec elements that involve inequality predicates. (A BETWEEN is represented as two
2312    // inequality predicates.)
2313    // Excludes cases where NOT appears around the literal inequality.
2314    // Note that for inequality predicates, some databases assume 1/3 selectivity in the absence of
2315    // concrete statistics.
2316    literal_inequality: usize,
2317    /// Any filter, except ones involving `IS NOT NULL`, because those are too common.
2318    /// Can be true by itself, or any other field being true can also make this true.
2319    /// `NOT LIKE` is only in this category.
2320    /// `!=` is only in this category.
2321    /// `NOT (a = b)` is turned into `!=` by `reduce` before us!
2322    any_filter: bool,
2323}
2324
2325impl BitOrAssign for FilterCharacteristics {
2326    fn bitor_assign(&mut self, rhs: Self) {
2327        self.literal_equality |= rhs.literal_equality;
2328        self.like |= rhs.like;
2329        self.is_null |= rhs.is_null;
2330        self.literal_inequality += rhs.literal_inequality;
2331        self.any_filter |= rhs.any_filter;
2332    }
2333}
2334
2335impl FilterCharacteristics {
2336    pub fn none() -> FilterCharacteristics {
2337        FilterCharacteristics {
2338            literal_equality: false,
2339            like: false,
2340            is_null: false,
2341            literal_inequality: 0,
2342            any_filter: false,
2343        }
2344    }
2345
2346    pub fn explain(&self) -> String {
2347        let mut e = "".to_owned();
2348        if self.literal_equality {
2349            e.push_str("e");
2350        }
2351        if self.like {
2352            e.push_str("l");
2353        }
2354        if self.is_null {
2355            e.push_str("n");
2356        }
2357        for _ in 0..self.literal_inequality {
2358            e.push_str("i");
2359        }
2360        if self.any_filter {
2361            e.push_str("f");
2362        }
2363        e
2364    }
2365
2366    pub fn filter_characteristics(
2367        filters: &Vec<MirScalarExpr>,
2368    ) -> Result<FilterCharacteristics, RecursionLimitError> {
2369        let mut literal_equality = false;
2370        let mut like = false;
2371        let mut is_null = false;
2372        let mut literal_inequality = 0;
2373        let mut any_filter = false;
2374        filters.iter().try_for_each(|f| {
2375            let mut literal_inequality_in_current_filter = false;
2376            let mut is_not_null_in_current_filter = false;
2377            f.visit_pre_with_context(
2378                false,
2379                &mut |not_in_parent_chain, expr| {
2380                    not_in_parent_chain
2381                        || matches!(
2382                            expr,
2383                            MirScalarExpr::CallUnary {
2384                                func: UnaryFunc::Not(func::Not),
2385                                ..
2386                            }
2387                        )
2388                },
2389                &mut |not_in_parent_chain, expr| {
2390                    if !not_in_parent_chain {
2391                        if expr.any_expr_eq_literal().is_some() {
2392                            literal_equality = true;
2393                        }
2394                        if expr.any_expr_ineq_literal() {
2395                            literal_inequality_in_current_filter = true;
2396                        }
2397                        if matches!(
2398                            expr,
2399                            MirScalarExpr::CallUnary {
2400                                func: UnaryFunc::IsLikeMatch(_),
2401                                ..
2402                            }
2403                        ) {
2404                            like = true;
2405                        }
2406                    };
2407                    if matches!(
2408                        expr,
2409                        MirScalarExpr::CallUnary {
2410                            func: UnaryFunc::IsNull(crate::func::IsNull),
2411                            ..
2412                        }
2413                    ) {
2414                        if *not_in_parent_chain {
2415                            is_not_null_in_current_filter = true;
2416                        } else {
2417                            is_null = true;
2418                        }
2419                    }
2420                },
2421            )?;
2422            if literal_inequality_in_current_filter {
2423                literal_inequality += 1;
2424            }
2425            if !is_not_null_in_current_filter {
2426                // We want to ignore `IS NOT NULL` for `any_filter`.
2427                any_filter = true;
2428            }
2429            Ok(())
2430        })?;
2431        Ok(FilterCharacteristics {
2432            literal_equality,
2433            like,
2434            is_null,
2435            literal_inequality,
2436            any_filter,
2437        })
2438    }
2439
2440    pub fn add_literal_equality(&mut self) {
2441        self.literal_equality = true;
2442    }
2443
2444    pub fn worst_case_scaling_factor(&self) -> f64 {
2445        let mut factor = 1.0;
2446
2447        if self.literal_equality {
2448            factor *= 0.1;
2449        }
2450
2451        if self.is_null {
2452            factor *= 0.1;
2453        }
2454
2455        if self.literal_inequality >= 2 {
2456            factor *= 0.25;
2457        } else if self.literal_inequality == 1 {
2458            factor *= 0.33;
2459        }
2460
2461        // catch various negated filters, treat them pessimistically
2462        if !(self.literal_equality || self.is_null || self.literal_inequality > 0)
2463            && self.any_filter
2464        {
2465            factor *= 0.9;
2466        }
2467
2468        factor
2469    }
2470}
2471
2472#[derive(
2473    Arbitrary,
2474    Ord,
2475    PartialOrd,
2476    Copy,
2477    Clone,
2478    Debug,
2479    Eq,
2480    PartialEq,
2481    Serialize,
2482    Deserialize,
2483    Hash,
2484    MzReflect,
2485)]
2486pub enum DomainLimit {
2487    None,
2488    Inclusive(i64),
2489    Exclusive(i64),
2490}
2491
2492impl RustType<ProtoDomainLimit> for DomainLimit {
2493    fn into_proto(&self) -> ProtoDomainLimit {
2494        use proto_domain_limit::Kind::*;
2495        let kind = match self {
2496            DomainLimit::None => None(()),
2497            DomainLimit::Inclusive(v) => Inclusive(*v),
2498            DomainLimit::Exclusive(v) => Exclusive(*v),
2499        };
2500        ProtoDomainLimit { kind: Some(kind) }
2501    }
2502
2503    fn from_proto(proto: ProtoDomainLimit) -> Result<Self, TryFromProtoError> {
2504        use proto_domain_limit::Kind::*;
2505        if let Some(kind) = proto.kind {
2506            match kind {
2507                None(()) => Ok(DomainLimit::None),
2508                Inclusive(v) => Ok(DomainLimit::Inclusive(v)),
2509                Exclusive(v) => Ok(DomainLimit::Exclusive(v)),
2510            }
2511        } else {
2512            Err(TryFromProtoError::missing_field("ProtoDomainLimit::kind"))
2513        }
2514    }
2515}
2516
2517#[derive(
2518    Arbitrary, Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect,
2519)]
2520pub enum EvalError {
2521    CharacterNotValidForEncoding(i32),
2522    CharacterTooLargeForEncoding(i32),
2523    DateBinOutOfRange(Box<str>),
2524    DivisionByZero,
2525    Unsupported {
2526        feature: Box<str>,
2527        discussion_no: Option<usize>,
2528    },
2529    FloatOverflow,
2530    FloatUnderflow,
2531    NumericFieldOverflow,
2532    Float32OutOfRange(Box<str>),
2533    Float64OutOfRange(Box<str>),
2534    Int16OutOfRange(Box<str>),
2535    Int32OutOfRange(Box<str>),
2536    Int64OutOfRange(Box<str>),
2537    UInt16OutOfRange(Box<str>),
2538    UInt32OutOfRange(Box<str>),
2539    UInt64OutOfRange(Box<str>),
2540    MzTimestampOutOfRange(Box<str>),
2541    MzTimestampStepOverflow,
2542    OidOutOfRange(Box<str>),
2543    IntervalOutOfRange(Box<str>),
2544    TimestampCannotBeNan,
2545    TimestampOutOfRange,
2546    DateOutOfRange,
2547    CharOutOfRange,
2548    IndexOutOfRange {
2549        provided: i32,
2550        // The last valid index position, i.e. `v.len() - 1`
2551        valid_end: i32,
2552    },
2553    InvalidBase64Equals,
2554    InvalidBase64Symbol(char),
2555    InvalidBase64EndSequence,
2556    InvalidTimezone(Box<str>),
2557    InvalidTimezoneInterval,
2558    InvalidTimezoneConversion,
2559    InvalidIanaTimezoneId(Box<str>),
2560    InvalidLayer {
2561        max_layer: usize,
2562        val: i64,
2563    },
2564    InvalidArray(InvalidArrayError),
2565    InvalidEncodingName(Box<str>),
2566    InvalidHashAlgorithm(Box<str>),
2567    InvalidByteSequence {
2568        byte_sequence: Box<str>,
2569        encoding_name: Box<str>,
2570    },
2571    InvalidJsonbCast {
2572        from: Box<str>,
2573        to: Box<str>,
2574    },
2575    InvalidRegex(Box<str>),
2576    InvalidRegexFlag(char),
2577    InvalidParameterValue(Box<str>),
2578    InvalidDatePart(Box<str>),
2579    KeyCannotBeNull,
2580    NegSqrt,
2581    NegLimit,
2582    NullCharacterNotPermitted,
2583    UnknownUnits(Box<str>),
2584    UnsupportedUnits(Box<str>, Box<str>),
2585    UnterminatedLikeEscapeSequence,
2586    Parse(ParseError),
2587    ParseHex(ParseHexError),
2588    Internal(Box<str>),
2589    InfinityOutOfDomain(Box<str>),
2590    NegativeOutOfDomain(Box<str>),
2591    ZeroOutOfDomain(Box<str>),
2592    OutOfDomain(DomainLimit, DomainLimit, Box<str>),
2593    ComplexOutOfRange(Box<str>),
2594    MultipleRowsFromSubquery,
2595    Undefined(Box<str>),
2596    LikePatternTooLong,
2597    LikeEscapeTooLong,
2598    StringValueTooLong {
2599        target_type: Box<str>,
2600        length: usize,
2601    },
2602    MultidimensionalArrayRemovalNotSupported,
2603    IncompatibleArrayDimensions {
2604        dims: Option<(usize, usize)>,
2605    },
2606    TypeFromOid(Box<str>),
2607    InvalidRange(InvalidRangeError),
2608    InvalidRoleId(Box<str>),
2609    InvalidPrivileges(Box<str>),
2610    LetRecLimitExceeded(Box<str>),
2611    MultiDimensionalArraySearch,
2612    MustNotBeNull(Box<str>),
2613    InvalidIdentifier {
2614        ident: Box<str>,
2615        detail: Option<Box<str>>,
2616    },
2617    ArrayFillWrongArraySubscripts,
2618    // TODO: propagate this check more widely throughout the expr crate
2619    MaxArraySizeExceeded(usize),
2620    DateDiffOverflow {
2621        unit: Box<str>,
2622        a: Box<str>,
2623        b: Box<str>,
2624    },
2625    // The error for ErrorIfNull; this should not be used in other contexts as a generic error
2626    // printer.
2627    IfNullError(Box<str>),
2628    LengthTooLarge,
2629    AclArrayNullElement,
2630    MzAclArrayNullElement,
2631    PrettyError(Box<str>),
2632}
2633
2634impl fmt::Display for EvalError {
2635    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2636        match self {
2637            EvalError::CharacterNotValidForEncoding(v) => {
2638                write!(f, "requested character not valid for encoding: {v}")
2639            }
2640            EvalError::CharacterTooLargeForEncoding(v) => {
2641                write!(f, "requested character too large for encoding: {v}")
2642            }
2643            EvalError::DateBinOutOfRange(message) => f.write_str(message),
2644            EvalError::DivisionByZero => f.write_str("division by zero"),
2645            EvalError::Unsupported {
2646                feature,
2647                discussion_no,
2648            } => {
2649                write!(f, "{} not yet supported", feature)?;
2650                if let Some(discussion_no) = discussion_no {
2651                    write!(
2652                        f,
2653                        ", see https://github.com/MaterializeInc/materialize/discussions/{} for more details",
2654                        discussion_no
2655                    )?;
2656                }
2657                Ok(())
2658            }
2659            EvalError::FloatOverflow => f.write_str("value out of range: overflow"),
2660            EvalError::FloatUnderflow => f.write_str("value out of range: underflow"),
2661            EvalError::NumericFieldOverflow => f.write_str("numeric field overflow"),
2662            EvalError::Float32OutOfRange(val) => write!(f, "{} real out of range", val.quoted()),
2663            EvalError::Float64OutOfRange(val) => {
2664                write!(f, "{} double precision out of range", val.quoted())
2665            }
2666            EvalError::Int16OutOfRange(val) => write!(f, "{} smallint out of range", val.quoted()),
2667            EvalError::Int32OutOfRange(val) => write!(f, "{} integer out of range", val.quoted()),
2668            EvalError::Int64OutOfRange(val) => write!(f, "{} bigint out of range", val.quoted()),
2669            EvalError::UInt16OutOfRange(val) => write!(f, "{} uint2 out of range", val.quoted()),
2670            EvalError::UInt32OutOfRange(val) => write!(f, "{} uint4 out of range", val.quoted()),
2671            EvalError::UInt64OutOfRange(val) => write!(f, "{} uint8 out of range", val.quoted()),
2672            EvalError::MzTimestampOutOfRange(val) => {
2673                write!(f, "{} mz_timestamp out of range", val.quoted())
2674            }
2675            EvalError::MzTimestampStepOverflow => f.write_str("step mz_timestamp overflow"),
2676            EvalError::OidOutOfRange(val) => write!(f, "{} OID out of range", val.quoted()),
2677            EvalError::IntervalOutOfRange(val) => {
2678                write!(f, "{} interval out of range", val.quoted())
2679            }
2680            EvalError::TimestampCannotBeNan => f.write_str("timestamp cannot be NaN"),
2681            EvalError::TimestampOutOfRange => f.write_str("timestamp out of range"),
2682            EvalError::DateOutOfRange => f.write_str("date out of range"),
2683            EvalError::CharOutOfRange => f.write_str("\"char\" out of range"),
2684            EvalError::IndexOutOfRange {
2685                provided,
2686                valid_end,
2687            } => write!(f, "index {provided} out of valid range, 0..{valid_end}",),
2688            EvalError::InvalidBase64Equals => {
2689                f.write_str("unexpected \"=\" while decoding base64 sequence")
2690            }
2691            EvalError::InvalidBase64Symbol(c) => write!(
2692                f,
2693                "invalid symbol \"{}\" found while decoding base64 sequence",
2694                c.escape_default()
2695            ),
2696            EvalError::InvalidBase64EndSequence => f.write_str("invalid base64 end sequence"),
2697            EvalError::InvalidJsonbCast { from, to } => {
2698                write!(f, "cannot cast jsonb {} to type {}", from, to)
2699            }
2700            EvalError::InvalidTimezone(tz) => write!(f, "invalid time zone '{}'", tz),
2701            EvalError::InvalidTimezoneInterval => {
2702                f.write_str("timezone interval must not contain months or years")
2703            }
2704            EvalError::InvalidTimezoneConversion => f.write_str("invalid timezone conversion"),
2705            EvalError::InvalidIanaTimezoneId(tz) => {
2706                write!(f, "invalid IANA Time Zone Database identifier: '{}'", tz)
2707            }
2708            EvalError::InvalidLayer { max_layer, val } => write!(
2709                f,
2710                "invalid layer: {}; must use value within [1, {}]",
2711                val, max_layer
2712            ),
2713            EvalError::InvalidArray(e) => e.fmt(f),
2714            EvalError::InvalidEncodingName(name) => write!(f, "invalid encoding name '{}'", name),
2715            EvalError::InvalidHashAlgorithm(alg) => write!(f, "invalid hash algorithm '{}'", alg),
2716            EvalError::InvalidByteSequence {
2717                byte_sequence,
2718                encoding_name,
2719            } => write!(
2720                f,
2721                "invalid byte sequence '{}' for encoding '{}'",
2722                byte_sequence, encoding_name
2723            ),
2724            EvalError::InvalidDatePart(part) => write!(f, "invalid datepart {}", part.quoted()),
2725            EvalError::KeyCannotBeNull => f.write_str("key cannot be null"),
2726            EvalError::NegSqrt => f.write_str("cannot take square root of a negative number"),
2727            EvalError::NegLimit => f.write_str("LIMIT must not be negative"),
2728            EvalError::NullCharacterNotPermitted => f.write_str("null character not permitted"),
2729            EvalError::InvalidRegex(e) => write!(f, "invalid regular expression: {}", e),
2730            EvalError::InvalidRegexFlag(c) => write!(f, "invalid regular expression flag: {}", c),
2731            EvalError::InvalidParameterValue(s) => f.write_str(s),
2732            EvalError::UnknownUnits(units) => write!(f, "unit '{}' not recognized", units),
2733            EvalError::UnsupportedUnits(units, typ) => {
2734                write!(f, "unit '{}' not supported for type {}", units, typ)
2735            }
2736            EvalError::UnterminatedLikeEscapeSequence => {
2737                f.write_str("unterminated escape sequence in LIKE")
2738            }
2739            EvalError::Parse(e) => e.fmt(f),
2740            EvalError::PrettyError(e) => e.fmt(f),
2741            EvalError::ParseHex(e) => e.fmt(f),
2742            EvalError::Internal(s) => write!(f, "internal error: {}", s),
2743            EvalError::InfinityOutOfDomain(s) => {
2744                write!(f, "function {} is only defined for finite arguments", s)
2745            }
2746            EvalError::NegativeOutOfDomain(s) => {
2747                write!(f, "function {} is not defined for negative numbers", s)
2748            }
2749            EvalError::ZeroOutOfDomain(s) => {
2750                write!(f, "function {} is not defined for zero", s)
2751            }
2752            EvalError::OutOfDomain(lower, upper, s) => {
2753                use DomainLimit::*;
2754                write!(f, "function {s} is defined for numbers ")?;
2755                match (lower, upper) {
2756                    (Inclusive(n), None) => write!(f, "greater than or equal to {n}"),
2757                    (Exclusive(n), None) => write!(f, "greater than {n}"),
2758                    (None, Inclusive(n)) => write!(f, "less than or equal to {n}"),
2759                    (None, Exclusive(n)) => write!(f, "less than {n}"),
2760                    (Inclusive(lo), Inclusive(hi)) => write!(f, "between {lo} and {hi} inclusive"),
2761                    (Exclusive(lo), Exclusive(hi)) => write!(f, "between {lo} and {hi} exclusive"),
2762                    (Inclusive(lo), Exclusive(hi)) => {
2763                        write!(f, "between {lo} inclusive and {hi} exclusive")
2764                    }
2765                    (Exclusive(lo), Inclusive(hi)) => {
2766                        write!(f, "between {lo} exclusive and {hi} inclusive")
2767                    }
2768                    (None, None) => panic!("invalid domain error"),
2769                }
2770            }
2771            EvalError::ComplexOutOfRange(s) => {
2772                write!(f, "function {} cannot return complex numbers", s)
2773            }
2774            EvalError::MultipleRowsFromSubquery => {
2775                write!(f, "more than one record produced in subquery")
2776            }
2777            EvalError::Undefined(s) => {
2778                write!(f, "{} is undefined", s)
2779            }
2780            EvalError::LikePatternTooLong => {
2781                write!(f, "LIKE pattern exceeds maximum length")
2782            }
2783            EvalError::LikeEscapeTooLong => {
2784                write!(f, "invalid escape string")
2785            }
2786            EvalError::StringValueTooLong {
2787                target_type,
2788                length,
2789            } => {
2790                write!(f, "value too long for type {}({})", target_type, length)
2791            }
2792            EvalError::MultidimensionalArrayRemovalNotSupported => {
2793                write!(
2794                    f,
2795                    "removing elements from multidimensional arrays is not supported"
2796                )
2797            }
2798            EvalError::IncompatibleArrayDimensions { dims: _ } => {
2799                write!(f, "cannot concatenate incompatible arrays")
2800            }
2801            EvalError::TypeFromOid(msg) => write!(f, "{msg}"),
2802            EvalError::InvalidRange(e) => e.fmt(f),
2803            EvalError::InvalidRoleId(msg) => write!(f, "{msg}"),
2804            EvalError::InvalidPrivileges(privilege) => {
2805                write!(f, "unrecognized privilege type: {privilege}")
2806            }
2807            EvalError::LetRecLimitExceeded(max_iters) => {
2808                write!(
2809                    f,
2810                    "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.)",
2811                    max_iters
2812                )
2813            }
2814            EvalError::MultiDimensionalArraySearch => write!(
2815                f,
2816                "searching for elements in multidimensional arrays is not supported"
2817            ),
2818            EvalError::MustNotBeNull(v) => write!(f, "{v} must not be null"),
2819            EvalError::InvalidIdentifier { ident, .. } => {
2820                write!(f, "string is not a valid identifier: {}", ident.quoted())
2821            }
2822            EvalError::ArrayFillWrongArraySubscripts => {
2823                f.write_str("wrong number of array subscripts")
2824            }
2825            EvalError::MaxArraySizeExceeded(max_size) => {
2826                write!(
2827                    f,
2828                    "array size exceeds the maximum allowed ({max_size} bytes)"
2829                )
2830            }
2831            EvalError::DateDiffOverflow { unit, a, b } => {
2832                write!(f, "datediff overflow, {unit} of {a}, {b}")
2833            }
2834            EvalError::IfNullError(s) => f.write_str(s),
2835            EvalError::LengthTooLarge => write!(f, "requested length too large"),
2836            EvalError::AclArrayNullElement => write!(f, "ACL arrays must not contain null values"),
2837            EvalError::MzAclArrayNullElement => {
2838                write!(f, "MZ_ACL arrays must not contain null values")
2839            }
2840        }
2841    }
2842}
2843
2844impl EvalError {
2845    pub fn detail(&self) -> Option<String> {
2846        match self {
2847            EvalError::IncompatibleArrayDimensions { dims: None } => Some(
2848                "Arrays with differing dimensions are not compatible for concatenation.".into(),
2849            ),
2850            EvalError::IncompatibleArrayDimensions {
2851                dims: Some((a_dims, b_dims)),
2852            } => Some(format!(
2853                "Arrays of {} and {} dimensions are not compatible for concatenation.",
2854                a_dims, b_dims
2855            )),
2856            EvalError::InvalidIdentifier { detail, .. } => detail.as_deref().map(Into::into),
2857            EvalError::ArrayFillWrongArraySubscripts => {
2858                Some("Low bound array has different size than dimensions array.".into())
2859            }
2860            _ => None,
2861        }
2862    }
2863
2864    pub fn hint(&self) -> Option<String> {
2865        match self {
2866            EvalError::InvalidBase64EndSequence => Some(
2867                "Input data is missing padding, is truncated, or is otherwise corrupted.".into(),
2868            ),
2869            EvalError::LikeEscapeTooLong => {
2870                Some("Escape string must be empty or one character.".into())
2871            }
2872            EvalError::MzTimestampOutOfRange(_) => Some(
2873                "Integer, numeric, and text casts to mz_timestamp must be in the form of whole \
2874                milliseconds since the Unix epoch. Values with fractional parts cannot be \
2875                converted to mz_timestamp."
2876                    .into(),
2877            ),
2878            _ => None,
2879        }
2880    }
2881}
2882
2883impl std::error::Error for EvalError {}
2884
2885impl From<ParseError> for EvalError {
2886    fn from(e: ParseError) -> EvalError {
2887        EvalError::Parse(e)
2888    }
2889}
2890
2891impl From<ParseHexError> for EvalError {
2892    fn from(e: ParseHexError) -> EvalError {
2893        EvalError::ParseHex(e)
2894    }
2895}
2896
2897impl From<InvalidArrayError> for EvalError {
2898    fn from(e: InvalidArrayError) -> EvalError {
2899        EvalError::InvalidArray(e)
2900    }
2901}
2902
2903impl From<regex::Error> for EvalError {
2904    fn from(e: regex::Error) -> EvalError {
2905        EvalError::InvalidRegex(e.to_string().into())
2906    }
2907}
2908
2909impl From<TypeFromOidError> for EvalError {
2910    fn from(e: TypeFromOidError) -> EvalError {
2911        EvalError::TypeFromOid(e.to_string().into())
2912    }
2913}
2914
2915impl From<DateError> for EvalError {
2916    fn from(e: DateError) -> EvalError {
2917        match e {
2918            DateError::OutOfRange => EvalError::DateOutOfRange,
2919        }
2920    }
2921}
2922
2923impl From<TimestampError> for EvalError {
2924    fn from(e: TimestampError) -> EvalError {
2925        match e {
2926            TimestampError::OutOfRange => EvalError::TimestampOutOfRange,
2927        }
2928    }
2929}
2930
2931impl From<InvalidRangeError> for EvalError {
2932    fn from(e: InvalidRangeError) -> EvalError {
2933        EvalError::InvalidRange(e)
2934    }
2935}
2936
2937impl RustType<ProtoEvalError> for EvalError {
2938    fn into_proto(&self) -> ProtoEvalError {
2939        use proto_eval_error::Kind::*;
2940        use proto_eval_error::*;
2941        let kind = match self {
2942            EvalError::CharacterNotValidForEncoding(v) => CharacterNotValidForEncoding(*v),
2943            EvalError::CharacterTooLargeForEncoding(v) => CharacterTooLargeForEncoding(*v),
2944            EvalError::DateBinOutOfRange(v) => DateBinOutOfRange(v.into_proto()),
2945            EvalError::DivisionByZero => DivisionByZero(()),
2946            EvalError::Unsupported {
2947                feature,
2948                discussion_no,
2949            } => Unsupported(ProtoUnsupported {
2950                feature: feature.into_proto(),
2951                discussion_no: discussion_no.into_proto(),
2952            }),
2953            EvalError::FloatOverflow => FloatOverflow(()),
2954            EvalError::FloatUnderflow => FloatUnderflow(()),
2955            EvalError::NumericFieldOverflow => NumericFieldOverflow(()),
2956            EvalError::Float32OutOfRange(val) => Float32OutOfRange(ProtoValueOutOfRange {
2957                value: val.to_string(),
2958            }),
2959            EvalError::Float64OutOfRange(val) => Float64OutOfRange(ProtoValueOutOfRange {
2960                value: val.to_string(),
2961            }),
2962            EvalError::Int16OutOfRange(val) => Int16OutOfRange(ProtoValueOutOfRange {
2963                value: val.to_string(),
2964            }),
2965            EvalError::Int32OutOfRange(val) => Int32OutOfRange(ProtoValueOutOfRange {
2966                value: val.to_string(),
2967            }),
2968            EvalError::Int64OutOfRange(val) => Int64OutOfRange(ProtoValueOutOfRange {
2969                value: val.to_string(),
2970            }),
2971            EvalError::UInt16OutOfRange(val) => Uint16OutOfRange(ProtoValueOutOfRange {
2972                value: val.to_string(),
2973            }),
2974            EvalError::UInt32OutOfRange(val) => Uint32OutOfRange(ProtoValueOutOfRange {
2975                value: val.to_string(),
2976            }),
2977            EvalError::UInt64OutOfRange(val) => Uint64OutOfRange(ProtoValueOutOfRange {
2978                value: val.to_string(),
2979            }),
2980            EvalError::MzTimestampOutOfRange(val) => MzTimestampOutOfRange(ProtoValueOutOfRange {
2981                value: val.to_string(),
2982            }),
2983            EvalError::MzTimestampStepOverflow => MzTimestampStepOverflow(()),
2984            EvalError::OidOutOfRange(val) => OidOutOfRange(ProtoValueOutOfRange {
2985                value: val.to_string(),
2986            }),
2987            EvalError::IntervalOutOfRange(val) => IntervalOutOfRange(ProtoValueOutOfRange {
2988                value: val.to_string(),
2989            }),
2990            EvalError::TimestampCannotBeNan => TimestampCannotBeNan(()),
2991            EvalError::TimestampOutOfRange => TimestampOutOfRange(()),
2992            EvalError::DateOutOfRange => DateOutOfRange(()),
2993            EvalError::CharOutOfRange => CharOutOfRange(()),
2994            EvalError::IndexOutOfRange {
2995                provided,
2996                valid_end,
2997            } => IndexOutOfRange(ProtoIndexOutOfRange {
2998                provided: *provided,
2999                valid_end: *valid_end,
3000            }),
3001            EvalError::InvalidBase64Equals => InvalidBase64Equals(()),
3002            EvalError::InvalidBase64Symbol(sym) => InvalidBase64Symbol(sym.into_proto()),
3003            EvalError::InvalidBase64EndSequence => InvalidBase64EndSequence(()),
3004            EvalError::InvalidTimezone(tz) => InvalidTimezone(tz.into_proto()),
3005            EvalError::InvalidTimezoneInterval => InvalidTimezoneInterval(()),
3006            EvalError::InvalidTimezoneConversion => InvalidTimezoneConversion(()),
3007            EvalError::InvalidLayer { max_layer, val } => InvalidLayer(ProtoInvalidLayer {
3008                max_layer: max_layer.into_proto(),
3009                val: *val,
3010            }),
3011            EvalError::InvalidArray(error) => InvalidArray(error.into_proto()),
3012            EvalError::InvalidEncodingName(v) => InvalidEncodingName(v.into_proto()),
3013            EvalError::InvalidHashAlgorithm(v) => InvalidHashAlgorithm(v.into_proto()),
3014            EvalError::InvalidByteSequence {
3015                byte_sequence,
3016                encoding_name,
3017            } => InvalidByteSequence(ProtoInvalidByteSequence {
3018                byte_sequence: byte_sequence.into_proto(),
3019                encoding_name: encoding_name.into_proto(),
3020            }),
3021            EvalError::InvalidJsonbCast { from, to } => InvalidJsonbCast(ProtoInvalidJsonbCast {
3022                from: from.into_proto(),
3023                to: to.into_proto(),
3024            }),
3025            EvalError::InvalidRegex(v) => InvalidRegex(v.into_proto()),
3026            EvalError::InvalidRegexFlag(v) => InvalidRegexFlag(v.into_proto()),
3027            EvalError::InvalidParameterValue(v) => InvalidParameterValue(v.into_proto()),
3028            EvalError::InvalidDatePart(part) => InvalidDatePart(part.into_proto()),
3029            EvalError::KeyCannotBeNull => KeyCannotBeNull(()),
3030            EvalError::NegSqrt => NegSqrt(()),
3031            EvalError::NegLimit => NegLimit(()),
3032            EvalError::NullCharacterNotPermitted => NullCharacterNotPermitted(()),
3033            EvalError::UnknownUnits(v) => UnknownUnits(v.into_proto()),
3034            EvalError::UnsupportedUnits(units, typ) => UnsupportedUnits(ProtoUnsupportedUnits {
3035                units: units.into_proto(),
3036                typ: typ.into_proto(),
3037            }),
3038            EvalError::UnterminatedLikeEscapeSequence => UnterminatedLikeEscapeSequence(()),
3039            EvalError::Parse(error) => Parse(error.into_proto()),
3040            EvalError::PrettyError(error) => PrettyError(error.into_proto()),
3041            EvalError::ParseHex(error) => ParseHex(error.into_proto()),
3042            EvalError::Internal(v) => Internal(v.into_proto()),
3043            EvalError::InfinityOutOfDomain(v) => InfinityOutOfDomain(v.into_proto()),
3044            EvalError::NegativeOutOfDomain(v) => NegativeOutOfDomain(v.into_proto()),
3045            EvalError::ZeroOutOfDomain(v) => ZeroOutOfDomain(v.into_proto()),
3046            EvalError::OutOfDomain(lower, upper, id) => OutOfDomain(ProtoOutOfDomain {
3047                lower: Some(lower.into_proto()),
3048                upper: Some(upper.into_proto()),
3049                id: id.into_proto(),
3050            }),
3051            EvalError::ComplexOutOfRange(v) => ComplexOutOfRange(v.into_proto()),
3052            EvalError::MultipleRowsFromSubquery => MultipleRowsFromSubquery(()),
3053            EvalError::Undefined(v) => Undefined(v.into_proto()),
3054            EvalError::LikePatternTooLong => LikePatternTooLong(()),
3055            EvalError::LikeEscapeTooLong => LikeEscapeTooLong(()),
3056            EvalError::StringValueTooLong {
3057                target_type,
3058                length,
3059            } => StringValueTooLong(ProtoStringValueTooLong {
3060                target_type: target_type.into_proto(),
3061                length: length.into_proto(),
3062            }),
3063            EvalError::MultidimensionalArrayRemovalNotSupported => {
3064                MultidimensionalArrayRemovalNotSupported(())
3065            }
3066            EvalError::IncompatibleArrayDimensions { dims } => {
3067                IncompatibleArrayDimensions(ProtoIncompatibleArrayDimensions {
3068                    dims: dims.into_proto(),
3069                })
3070            }
3071            EvalError::TypeFromOid(v) => TypeFromOid(v.into_proto()),
3072            EvalError::InvalidRange(error) => InvalidRange(error.into_proto()),
3073            EvalError::InvalidRoleId(v) => InvalidRoleId(v.into_proto()),
3074            EvalError::InvalidPrivileges(v) => InvalidPrivileges(v.into_proto()),
3075            EvalError::LetRecLimitExceeded(v) => WmrRecursionLimitExceeded(v.into_proto()),
3076            EvalError::MultiDimensionalArraySearch => MultiDimensionalArraySearch(()),
3077            EvalError::MustNotBeNull(v) => MustNotBeNull(v.into_proto()),
3078            EvalError::InvalidIdentifier { ident, detail } => {
3079                InvalidIdentifier(ProtoInvalidIdentifier {
3080                    ident: ident.into_proto(),
3081                    detail: detail.into_proto(),
3082                })
3083            }
3084            EvalError::ArrayFillWrongArraySubscripts => ArrayFillWrongArraySubscripts(()),
3085            EvalError::MaxArraySizeExceeded(max_size) => {
3086                MaxArraySizeExceeded(u64::cast_from(*max_size))
3087            }
3088            EvalError::DateDiffOverflow { unit, a, b } => DateDiffOverflow(ProtoDateDiffOverflow {
3089                unit: unit.into_proto(),
3090                a: a.into_proto(),
3091                b: b.into_proto(),
3092            }),
3093            EvalError::IfNullError(s) => IfNullError(s.into_proto()),
3094            EvalError::LengthTooLarge => LengthTooLarge(()),
3095            EvalError::AclArrayNullElement => AclArrayNullElement(()),
3096            EvalError::MzAclArrayNullElement => MzAclArrayNullElement(()),
3097            EvalError::InvalidIanaTimezoneId(s) => InvalidIanaTimezoneId(s.into_proto()),
3098        };
3099        ProtoEvalError { kind: Some(kind) }
3100    }
3101
3102    fn from_proto(proto: ProtoEvalError) -> Result<Self, TryFromProtoError> {
3103        use proto_eval_error::Kind::*;
3104        match proto.kind {
3105            Some(kind) => match kind {
3106                CharacterNotValidForEncoding(v) => Ok(EvalError::CharacterNotValidForEncoding(v)),
3107                CharacterTooLargeForEncoding(v) => Ok(EvalError::CharacterTooLargeForEncoding(v)),
3108                DateBinOutOfRange(v) => Ok(EvalError::DateBinOutOfRange(v.into())),
3109                DivisionByZero(()) => Ok(EvalError::DivisionByZero),
3110                Unsupported(v) => Ok(EvalError::Unsupported {
3111                    feature: v.feature.into(),
3112                    discussion_no: v.discussion_no.into_rust()?,
3113                }),
3114                FloatOverflow(()) => Ok(EvalError::FloatOverflow),
3115                FloatUnderflow(()) => Ok(EvalError::FloatUnderflow),
3116                NumericFieldOverflow(()) => Ok(EvalError::NumericFieldOverflow),
3117                Float32OutOfRange(val) => Ok(EvalError::Float32OutOfRange(val.value.into())),
3118                Float64OutOfRange(val) => Ok(EvalError::Float64OutOfRange(val.value.into())),
3119                Int16OutOfRange(val) => Ok(EvalError::Int16OutOfRange(val.value.into())),
3120                Int32OutOfRange(val) => Ok(EvalError::Int32OutOfRange(val.value.into())),
3121                Int64OutOfRange(val) => Ok(EvalError::Int64OutOfRange(val.value.into())),
3122                Uint16OutOfRange(val) => Ok(EvalError::UInt16OutOfRange(val.value.into())),
3123                Uint32OutOfRange(val) => Ok(EvalError::UInt32OutOfRange(val.value.into())),
3124                Uint64OutOfRange(val) => Ok(EvalError::UInt64OutOfRange(val.value.into())),
3125                MzTimestampOutOfRange(val) => {
3126                    Ok(EvalError::MzTimestampOutOfRange(val.value.into()))
3127                }
3128                MzTimestampStepOverflow(()) => Ok(EvalError::MzTimestampStepOverflow),
3129                OidOutOfRange(val) => Ok(EvalError::OidOutOfRange(val.value.into())),
3130                IntervalOutOfRange(val) => Ok(EvalError::IntervalOutOfRange(val.value.into())),
3131                TimestampCannotBeNan(()) => Ok(EvalError::TimestampCannotBeNan),
3132                TimestampOutOfRange(()) => Ok(EvalError::TimestampOutOfRange),
3133                DateOutOfRange(()) => Ok(EvalError::DateOutOfRange),
3134                CharOutOfRange(()) => Ok(EvalError::CharOutOfRange),
3135                IndexOutOfRange(v) => Ok(EvalError::IndexOutOfRange {
3136                    provided: v.provided,
3137                    valid_end: v.valid_end,
3138                }),
3139                InvalidBase64Equals(()) => Ok(EvalError::InvalidBase64Equals),
3140                InvalidBase64Symbol(v) => char::from_proto(v).map(EvalError::InvalidBase64Symbol),
3141                InvalidBase64EndSequence(()) => Ok(EvalError::InvalidBase64EndSequence),
3142                InvalidTimezone(v) => Ok(EvalError::InvalidTimezone(v.into())),
3143                InvalidTimezoneInterval(()) => Ok(EvalError::InvalidTimezoneInterval),
3144                InvalidTimezoneConversion(()) => Ok(EvalError::InvalidTimezoneConversion),
3145                InvalidLayer(v) => Ok(EvalError::InvalidLayer {
3146                    max_layer: usize::from_proto(v.max_layer)?,
3147                    val: v.val,
3148                }),
3149                InvalidArray(error) => Ok(EvalError::InvalidArray(error.into_rust()?)),
3150                InvalidEncodingName(v) => Ok(EvalError::InvalidEncodingName(v.into())),
3151                InvalidHashAlgorithm(v) => Ok(EvalError::InvalidHashAlgorithm(v.into())),
3152                InvalidByteSequence(v) => Ok(EvalError::InvalidByteSequence {
3153                    byte_sequence: v.byte_sequence.into(),
3154                    encoding_name: v.encoding_name.into(),
3155                }),
3156                InvalidJsonbCast(v) => Ok(EvalError::InvalidJsonbCast {
3157                    from: v.from.into(),
3158                    to: v.to.into(),
3159                }),
3160                InvalidRegex(v) => Ok(EvalError::InvalidRegex(v.into())),
3161                InvalidRegexFlag(v) => Ok(EvalError::InvalidRegexFlag(char::from_proto(v)?)),
3162                InvalidParameterValue(v) => Ok(EvalError::InvalidParameterValue(v.into())),
3163                InvalidDatePart(part) => Ok(EvalError::InvalidDatePart(part.into())),
3164                KeyCannotBeNull(()) => Ok(EvalError::KeyCannotBeNull),
3165                NegSqrt(()) => Ok(EvalError::NegSqrt),
3166                NegLimit(()) => Ok(EvalError::NegLimit),
3167                NullCharacterNotPermitted(()) => Ok(EvalError::NullCharacterNotPermitted),
3168                UnknownUnits(v) => Ok(EvalError::UnknownUnits(v.into())),
3169                UnsupportedUnits(v) => {
3170                    Ok(EvalError::UnsupportedUnits(v.units.into(), v.typ.into()))
3171                }
3172                UnterminatedLikeEscapeSequence(()) => Ok(EvalError::UnterminatedLikeEscapeSequence),
3173                Parse(error) => Ok(EvalError::Parse(error.into_rust()?)),
3174                ParseHex(error) => Ok(EvalError::ParseHex(error.into_rust()?)),
3175                Internal(v) => Ok(EvalError::Internal(v.into())),
3176                InfinityOutOfDomain(v) => Ok(EvalError::InfinityOutOfDomain(v.into())),
3177                NegativeOutOfDomain(v) => Ok(EvalError::NegativeOutOfDomain(v.into())),
3178                ZeroOutOfDomain(v) => Ok(EvalError::ZeroOutOfDomain(v.into())),
3179                OutOfDomain(v) => Ok(EvalError::OutOfDomain(
3180                    v.lower.into_rust_if_some("ProtoDomainLimit::lower")?,
3181                    v.upper.into_rust_if_some("ProtoDomainLimit::upper")?,
3182                    v.id.into(),
3183                )),
3184                ComplexOutOfRange(v) => Ok(EvalError::ComplexOutOfRange(v.into())),
3185                MultipleRowsFromSubquery(()) => Ok(EvalError::MultipleRowsFromSubquery),
3186                Undefined(v) => Ok(EvalError::Undefined(v.into())),
3187                LikePatternTooLong(()) => Ok(EvalError::LikePatternTooLong),
3188                LikeEscapeTooLong(()) => Ok(EvalError::LikeEscapeTooLong),
3189                StringValueTooLong(v) => Ok(EvalError::StringValueTooLong {
3190                    target_type: v.target_type.into(),
3191                    length: usize::from_proto(v.length)?,
3192                }),
3193                MultidimensionalArrayRemovalNotSupported(()) => {
3194                    Ok(EvalError::MultidimensionalArrayRemovalNotSupported)
3195                }
3196                IncompatibleArrayDimensions(v) => Ok(EvalError::IncompatibleArrayDimensions {
3197                    dims: v.dims.into_rust()?,
3198                }),
3199                TypeFromOid(v) => Ok(EvalError::TypeFromOid(v.into())),
3200                InvalidRange(e) => Ok(EvalError::InvalidRange(e.into_rust()?)),
3201                InvalidRoleId(v) => Ok(EvalError::InvalidRoleId(v.into())),
3202                InvalidPrivileges(v) => Ok(EvalError::InvalidPrivileges(v.into())),
3203                WmrRecursionLimitExceeded(v) => Ok(EvalError::LetRecLimitExceeded(v.into())),
3204                MultiDimensionalArraySearch(()) => Ok(EvalError::MultiDimensionalArraySearch),
3205                MustNotBeNull(v) => Ok(EvalError::MustNotBeNull(v.into())),
3206                InvalidIdentifier(v) => Ok(EvalError::InvalidIdentifier {
3207                    ident: v.ident.into(),
3208                    detail: v.detail.into_rust()?,
3209                }),
3210                ArrayFillWrongArraySubscripts(()) => Ok(EvalError::ArrayFillWrongArraySubscripts),
3211                MaxArraySizeExceeded(max_size) => {
3212                    Ok(EvalError::MaxArraySizeExceeded(usize::cast_from(max_size)))
3213                }
3214                DateDiffOverflow(v) => Ok(EvalError::DateDiffOverflow {
3215                    unit: v.unit.into(),
3216                    a: v.a.into(),
3217                    b: v.b.into(),
3218                }),
3219                IfNullError(v) => Ok(EvalError::IfNullError(v.into())),
3220                LengthTooLarge(()) => Ok(EvalError::LengthTooLarge),
3221                AclArrayNullElement(()) => Ok(EvalError::AclArrayNullElement),
3222                MzAclArrayNullElement(()) => Ok(EvalError::MzAclArrayNullElement),
3223                InvalidIanaTimezoneId(s) => Ok(EvalError::InvalidIanaTimezoneId(s.into())),
3224                PrettyError(s) => Ok(EvalError::PrettyError(s.into())),
3225            },
3226            None => Err(TryFromProtoError::missing_field("ProtoEvalError::kind")),
3227        }
3228    }
3229}
3230
3231impl RustType<ProtoDims> for (usize, usize) {
3232    fn into_proto(&self) -> ProtoDims {
3233        ProtoDims {
3234            f0: self.0.into_proto(),
3235            f1: self.1.into_proto(),
3236        }
3237    }
3238
3239    fn from_proto(proto: ProtoDims) -> Result<Self, TryFromProtoError> {
3240        Ok((proto.f0.into_rust()?, proto.f1.into_rust()?))
3241    }
3242}
3243
3244#[cfg(test)]
3245mod tests {
3246    use mz_ore::assert_ok;
3247    use mz_proto::protobuf_roundtrip;
3248
3249    use super::*;
3250
3251    #[mz_ore::test]
3252    #[cfg_attr(miri, ignore)] // error: unsupported operation: can't call foreign function `rust_psm_stack_pointer` on OS `linux`
3253    fn test_reduce() {
3254        let relation_type = vec![
3255            ScalarType::Int64.nullable(true),
3256            ScalarType::Int64.nullable(true),
3257            ScalarType::Int64.nullable(false),
3258        ];
3259        let col = MirScalarExpr::Column;
3260        let err = |e| MirScalarExpr::literal(Err(e), ScalarType::Int64);
3261        let lit = |i| MirScalarExpr::literal_ok(Datum::Int64(i), ScalarType::Int64);
3262        let null = || MirScalarExpr::literal_null(ScalarType::Int64);
3263
3264        struct TestCase {
3265            input: MirScalarExpr,
3266            output: MirScalarExpr,
3267        }
3268
3269        let test_cases = vec![
3270            TestCase {
3271                input: MirScalarExpr::CallVariadic {
3272                    func: VariadicFunc::Coalesce,
3273                    exprs: vec![lit(1)],
3274                },
3275                output: lit(1),
3276            },
3277            TestCase {
3278                input: MirScalarExpr::CallVariadic {
3279                    func: VariadicFunc::Coalesce,
3280                    exprs: vec![lit(1), lit(2)],
3281                },
3282                output: lit(1),
3283            },
3284            TestCase {
3285                input: MirScalarExpr::CallVariadic {
3286                    func: VariadicFunc::Coalesce,
3287                    exprs: vec![null(), lit(2), null()],
3288                },
3289                output: lit(2),
3290            },
3291            TestCase {
3292                input: MirScalarExpr::CallVariadic {
3293                    func: VariadicFunc::Coalesce,
3294                    exprs: vec![null(), col(0), null(), col(1), lit(2), lit(3)],
3295                },
3296                output: MirScalarExpr::CallVariadic {
3297                    func: VariadicFunc::Coalesce,
3298                    exprs: vec![col(0), col(1), lit(2)],
3299                },
3300            },
3301            TestCase {
3302                input: MirScalarExpr::CallVariadic {
3303                    func: VariadicFunc::Coalesce,
3304                    exprs: vec![col(0), col(2), col(1)],
3305                },
3306                output: MirScalarExpr::CallVariadic {
3307                    func: VariadicFunc::Coalesce,
3308                    exprs: vec![col(0), col(2)],
3309                },
3310            },
3311            TestCase {
3312                input: MirScalarExpr::CallVariadic {
3313                    func: VariadicFunc::Coalesce,
3314                    exprs: vec![lit(1), err(EvalError::DivisionByZero)],
3315                },
3316                output: lit(1),
3317            },
3318            TestCase {
3319                input: MirScalarExpr::CallVariadic {
3320                    func: VariadicFunc::Coalesce,
3321                    exprs: vec![col(0), err(EvalError::DivisionByZero)],
3322                },
3323                output: err(EvalError::DivisionByZero),
3324            },
3325            TestCase {
3326                input: MirScalarExpr::CallVariadic {
3327                    func: VariadicFunc::Coalesce,
3328                    exprs: vec![
3329                        null(),
3330                        err(EvalError::DivisionByZero),
3331                        err(EvalError::NumericFieldOverflow),
3332                    ],
3333                },
3334                output: err(EvalError::DivisionByZero),
3335            },
3336            TestCase {
3337                input: MirScalarExpr::CallVariadic {
3338                    func: VariadicFunc::Coalesce,
3339                    exprs: vec![col(0), err(EvalError::DivisionByZero)],
3340                },
3341                output: err(EvalError::DivisionByZero),
3342            },
3343        ];
3344
3345        for tc in test_cases {
3346            let mut actual = tc.input.clone();
3347            actual.reduce(&relation_type);
3348            assert!(
3349                actual == tc.output,
3350                "input: {}\nactual: {}\nexpected: {}",
3351                tc.input,
3352                actual,
3353                tc.output
3354            );
3355        }
3356    }
3357
3358    proptest! {
3359        #[mz_ore::test]
3360        #[cfg_attr(miri, ignore)] // error: unsupported operation: can't call foreign function `decContextDefault` on OS `linux`
3361        fn mir_scalar_expr_protobuf_roundtrip(expect in any::<MirScalarExpr>()) {
3362            let actual = protobuf_roundtrip::<_, ProtoMirScalarExpr>(&expect);
3363            assert_ok!(actual);
3364            assert_eq!(actual.unwrap(), expect);
3365        }
3366    }
3367
3368    proptest! {
3369        #[mz_ore::test]
3370        fn domain_limit_protobuf_roundtrip(expect in any::<DomainLimit>()) {
3371            let actual = protobuf_roundtrip::<_, ProtoDomainLimit>(&expect);
3372            assert_ok!(actual);
3373            assert_eq!(actual.unwrap(), expect);
3374        }
3375    }
3376
3377    proptest! {
3378        #[mz_ore::test]
3379        #[cfg_attr(miri, ignore)] // too slow
3380        fn eval_error_protobuf_roundtrip(expect in any::<EvalError>()) {
3381            let actual = protobuf_roundtrip::<_, ProtoEvalError>(&expect);
3382            assert_ok!(actual);
3383            assert_eq!(actual.unwrap(), expect);
3384        }
3385    }
3386}