mz_sql/
func.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
10//! TBD: Currently, `sql::func` handles matching arguments to their respective
11//! built-in functions (for most built-in functions, at least).
12
13use std::cell::RefCell;
14use std::collections::BTreeMap;
15use std::fmt;
16use std::sync::LazyLock;
17
18use itertools::Itertools;
19use mz_expr::func;
20use mz_ore::collections::CollectionExt;
21use mz_ore::str::StrExt;
22use mz_pgrepr::oid;
23use mz_repr::role_id::RoleId;
24use mz_repr::{ColumnName, Datum, RelationType, ScalarBaseType, ScalarType};
25
26use crate::ast::{SelectStatement, Statement};
27use crate::catalog::{CatalogType, TypeCategory, TypeReference};
28use crate::names::{self, ResolvedItemName};
29use crate::plan::error::PlanError;
30use crate::plan::hir::{
31    AggregateFunc, BinaryFunc, CoercibleScalarExpr, CoercibleScalarType, ColumnOrder,
32    HirRelationExpr, HirScalarExpr, ScalarWindowFunc, TableFunc, UnaryFunc, UnmaterializableFunc,
33    ValueWindowFunc, VariadicFunc,
34};
35use crate::plan::query::{self, ExprContext, QueryContext};
36use crate::plan::scope::Scope;
37use crate::plan::side_effecting_func::PG_CATALOG_SEF_BUILTINS;
38use crate::plan::transform_ast;
39use crate::plan::typeconv::{self, CastContext};
40use crate::session::vars::{self, ENABLE_TIME_AT_TIME_ZONE};
41
42/// A specifier for a function or an operator.
43#[derive(Clone, Copy, Debug)]
44pub enum FuncSpec<'a> {
45    /// A function name.
46    Func(&'a ResolvedItemName),
47    /// An operator name.
48    Op(&'a str),
49}
50
51impl<'a> fmt::Display for FuncSpec<'a> {
52    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53        match self {
54            FuncSpec::Func(n) => n.fmt(f),
55            FuncSpec::Op(o) => o.fmt(f),
56        }
57    }
58}
59
60impl TypeCategory {
61    /// Extracted from PostgreSQL 9.6.
62    /// ```sql,ignore
63    /// SELECT array_agg(typname), typcategory
64    /// FROM pg_catalog.pg_type
65    /// WHERE typname IN (
66    ///  'bool', 'bytea', 'date', 'float4', 'float8', 'int4', 'int8', 'interval', 'jsonb',
67    ///  'numeric', 'text', 'time', 'timestamp', 'timestamptz'
68    /// )
69    /// GROUP BY typcategory
70    /// ORDER BY typcategory;
71    /// ```
72    pub fn from_type(typ: &ScalarType) -> Self {
73        // Keep this in sync with `from_catalog_type`.
74        match typ {
75            ScalarType::Array(..) | ScalarType::Int2Vector => Self::Array,
76            ScalarType::Bool => Self::Boolean,
77            ScalarType::AclItem
78            | ScalarType::Bytes
79            | ScalarType::Jsonb
80            | ScalarType::Uuid
81            | ScalarType::MzAclItem => Self::UserDefined,
82            ScalarType::Date
83            | ScalarType::Time
84            | ScalarType::Timestamp { .. }
85            | ScalarType::TimestampTz { .. } => Self::DateTime,
86            ScalarType::Float32
87            | ScalarType::Float64
88            | ScalarType::Int16
89            | ScalarType::Int32
90            | ScalarType::Int64
91            | ScalarType::UInt16
92            | ScalarType::UInt32
93            | ScalarType::UInt64
94            | ScalarType::Oid
95            | ScalarType::RegClass
96            | ScalarType::RegProc
97            | ScalarType::RegType
98            | ScalarType::Numeric { .. } => Self::Numeric,
99            ScalarType::Interval => Self::Timespan,
100            ScalarType::List { .. } => Self::List,
101            ScalarType::PgLegacyChar
102            | ScalarType::PgLegacyName
103            | ScalarType::String
104            | ScalarType::Char { .. }
105            | ScalarType::VarChar { .. } => Self::String,
106            ScalarType::Record { custom_id, .. } => {
107                if custom_id.is_some() {
108                    Self::Composite
109                } else {
110                    Self::Pseudo
111                }
112            }
113            ScalarType::Map { .. } => Self::Pseudo,
114            ScalarType::MzTimestamp => Self::Numeric,
115            ScalarType::Range { .. } => Self::Range,
116        }
117    }
118
119    pub fn from_param(param: &ParamType) -> Self {
120        match param {
121            ParamType::Any
122            | ParamType::AnyElement
123            | ParamType::ArrayAny
124            | ParamType::ArrayAnyCompatible
125            | ParamType::AnyCompatible
126            | ParamType::ListAny
127            | ParamType::ListAnyCompatible
128            | ParamType::ListElementAnyCompatible
129            | ParamType::Internal
130            | ParamType::NonVecAny
131            | ParamType::NonVecAnyCompatible
132            | ParamType::MapAny
133            | ParamType::MapAnyCompatible
134            | ParamType::RecordAny => Self::Pseudo,
135            ParamType::RangeAnyCompatible | ParamType::RangeAny => Self::Range,
136            ParamType::Plain(t) => Self::from_type(t),
137        }
138    }
139
140    /// Like [`TypeCategory::from_type`], but for catalog types.
141    // TODO(benesch): would be nice to figure out how to share code with
142    // `from_type`, but the refactor to enable that would be substantial.
143    pub fn from_catalog_type<T>(catalog_type: &CatalogType<T>) -> Self
144    where
145        T: TypeReference,
146    {
147        // Keep this in sync with `from_type`.
148        match catalog_type {
149            CatalogType::Array { .. } | CatalogType::Int2Vector => Self::Array,
150            CatalogType::Bool => Self::Boolean,
151            CatalogType::AclItem
152            | CatalogType::Bytes
153            | CatalogType::Jsonb
154            | CatalogType::Uuid
155            | CatalogType::MzAclItem => Self::UserDefined,
156            CatalogType::Date
157            | CatalogType::Time
158            | CatalogType::Timestamp
159            | CatalogType::TimestampTz => Self::DateTime,
160            CatalogType::Float32
161            | CatalogType::Float64
162            | CatalogType::Int16
163            | CatalogType::Int32
164            | CatalogType::Int64
165            | CatalogType::UInt16
166            | CatalogType::UInt32
167            | CatalogType::UInt64
168            | CatalogType::Oid
169            | CatalogType::RegClass
170            | CatalogType::RegProc
171            | CatalogType::RegType
172            | CatalogType::Numeric { .. } => Self::Numeric,
173            CatalogType::Interval => Self::Timespan,
174            CatalogType::List { .. } => Self::List,
175            CatalogType::PgLegacyChar
176            | CatalogType::PgLegacyName
177            | CatalogType::String
178            | CatalogType::Char { .. }
179            | CatalogType::VarChar { .. } => Self::String,
180            CatalogType::Record { .. } => TypeCategory::Composite,
181            CatalogType::Map { .. } | CatalogType::Pseudo => Self::Pseudo,
182            CatalogType::MzTimestamp => Self::String,
183            CatalogType::Range { .. } => Self::Range,
184        }
185    }
186
187    /// Extracted from PostgreSQL 9.6.
188    /// ```ignore
189    /// SELECT typcategory, typname, typispreferred
190    /// FROM pg_catalog.pg_type
191    /// WHERE typispreferred = true
192    /// ORDER BY typcategory;
193    /// ```
194    pub fn preferred_type(&self) -> Option<ScalarType> {
195        match self {
196            Self::Array
197            | Self::BitString
198            | Self::Composite
199            | Self::Enum
200            | Self::Geometric
201            | Self::List
202            | Self::NetworkAddress
203            | Self::Pseudo
204            | Self::Range
205            | Self::Unknown
206            | Self::UserDefined => None,
207            Self::Boolean => Some(ScalarType::Bool),
208            Self::DateTime => Some(ScalarType::TimestampTz { precision: None }),
209            Self::Numeric => Some(ScalarType::Float64),
210            Self::String => Some(ScalarType::String),
211            Self::Timespan => Some(ScalarType::Interval),
212        }
213    }
214}
215
216/// Builds an expression that evaluates a scalar function on the provided
217/// input expressions.
218pub struct Operation<R>(
219    pub  Box<
220        dyn Fn(
221                &ExprContext,
222                Vec<CoercibleScalarExpr>,
223                &ParamList,
224                Vec<ColumnOrder>,
225            ) -> Result<R, PlanError>
226            + Send
227            + Sync,
228    >,
229);
230
231impl<R> fmt::Debug for Operation<R> {
232    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233        f.debug_struct("Operation").finish()
234    }
235}
236
237impl Operation<HirScalarExpr> {
238    /// Builds a unary operation that simply returns its input.
239    fn identity() -> Operation<HirScalarExpr> {
240        Operation::unary(|_ecx, e| Ok(e))
241    }
242}
243
244impl<R> Operation<R> {
245    fn new<F>(f: F) -> Operation<R>
246    where
247        F: Fn(
248                &ExprContext,
249                Vec<CoercibleScalarExpr>,
250                &ParamList,
251                Vec<ColumnOrder>,
252            ) -> Result<R, PlanError>
253            + Send
254            + Sync
255            + 'static,
256    {
257        Operation(Box::new(f))
258    }
259
260    /// Builds an operation that takes no arguments.
261    fn nullary<F>(f: F) -> Operation<R>
262    where
263        F: Fn(&ExprContext) -> Result<R, PlanError> + Send + Sync + 'static,
264    {
265        Self::variadic(move |ecx, exprs| {
266            assert!(exprs.is_empty());
267            f(ecx)
268        })
269    }
270
271    /// Builds an operation that takes one argument.
272    fn unary<F>(f: F) -> Operation<R>
273    where
274        F: Fn(&ExprContext, HirScalarExpr) -> Result<R, PlanError> + Send + Sync + 'static,
275    {
276        Self::variadic(move |ecx, exprs| f(ecx, exprs.into_element()))
277    }
278
279    /// Builds an operation that takes one argument and an order_by.
280    fn unary_ordered<F>(f: F) -> Operation<R>
281    where
282        F: Fn(&ExprContext, HirScalarExpr, Vec<ColumnOrder>) -> Result<R, PlanError>
283            + Send
284            + Sync
285            + 'static,
286    {
287        Self::new(move |ecx, cexprs, params, order_by| {
288            let exprs = coerce_args_to_types(ecx, cexprs, params)?;
289            f(ecx, exprs.into_element(), order_by)
290        })
291    }
292
293    /// Builds an operation that takes two arguments.
294    fn binary<F>(f: F) -> Operation<R>
295    where
296        F: Fn(&ExprContext, HirScalarExpr, HirScalarExpr) -> Result<R, PlanError>
297            + Send
298            + Sync
299            + 'static,
300    {
301        Self::variadic(move |ecx, exprs| {
302            assert_eq!(exprs.len(), 2);
303            let mut exprs = exprs.into_iter();
304            let left = exprs.next().unwrap();
305            let right = exprs.next().unwrap();
306            f(ecx, left, right)
307        })
308    }
309
310    /// Builds an operation that takes two arguments and an order_by.
311    ///
312    /// If returning an aggregate function, it should return `true` for
313    /// [`AggregateFunc::is_order_sensitive`].
314    fn binary_ordered<F>(f: F) -> Operation<R>
315    where
316        F: Fn(&ExprContext, HirScalarExpr, HirScalarExpr, Vec<ColumnOrder>) -> Result<R, PlanError>
317            + Send
318            + Sync
319            + 'static,
320    {
321        Self::new(move |ecx, cexprs, params, order_by| {
322            let exprs = coerce_args_to_types(ecx, cexprs, params)?;
323            assert_eq!(exprs.len(), 2);
324            let mut exprs = exprs.into_iter();
325            let left = exprs.next().unwrap();
326            let right = exprs.next().unwrap();
327            f(ecx, left, right, order_by)
328        })
329    }
330
331    /// Builds an operation that takes any number of arguments.
332    fn variadic<F>(f: F) -> Operation<R>
333    where
334        F: Fn(&ExprContext, Vec<HirScalarExpr>) -> Result<R, PlanError> + Send + Sync + 'static,
335    {
336        Self::new(move |ecx, cexprs, params, _order_by| {
337            let exprs = coerce_args_to_types(ecx, cexprs, params)?;
338            f(ecx, exprs)
339        })
340    }
341}
342
343/// Backing implementation for sql_impl_func and sql_impl_cast. See those
344/// functions for details.
345pub fn sql_impl(
346    expr: &str,
347) -> impl Fn(&QueryContext, Vec<ScalarType>) -> Result<HirScalarExpr, PlanError> + use<> {
348    let expr = mz_sql_parser::parser::parse_expr(expr).unwrap_or_else(|e| {
349        panic!(
350            "static function definition failed to parse {}: {}",
351            expr.quoted(),
352            e,
353        )
354    });
355    move |qcx, types| {
356        // Reconstruct an expression context where the parameter types are
357        // bound to the types of the expressions in `args`.
358        let mut scx = qcx.scx.clone();
359        scx.param_types = RefCell::new(
360            types
361                .into_iter()
362                .enumerate()
363                .map(|(i, ty)| (i + 1, ty))
364                .collect(),
365        );
366        let qcx = QueryContext::root(&scx, qcx.lifetime);
367
368        let (mut expr, _) = names::resolve(qcx.scx.catalog, expr.clone())?;
369        // Desugar the expression
370        transform_ast::transform(&scx, &mut expr)?;
371
372        let ecx = ExprContext {
373            qcx: &qcx,
374            name: "static function definition",
375            scope: &Scope::empty(),
376            relation_type: &RelationType::empty(),
377            allow_aggregates: false,
378            allow_subqueries: true,
379            allow_parameters: true,
380            allow_windows: false,
381        };
382
383        // Plan the expression.
384        query::plan_expr(&ecx, &expr)?.type_as_any(&ecx)
385    }
386}
387
388// Constructs a definition for a built-in function out of a static SQL
389// expression.
390//
391// The SQL expression should use the standard parameter syntax (`$1`, `$2`, ...)
392// to refer to the inputs to the function. For example, a built-in function that
393// takes two arguments and concatenates them with an arrow in between could be
394// defined like so:
395//
396//     sql_impl_func("$1 || '<->' || $2")
397//
398// The number of parameters in the SQL expression must exactly match the number
399// of parameters in the built-in's declaration. There is no support for variadic
400// functions.
401fn sql_impl_func(expr: &str) -> Operation<HirScalarExpr> {
402    let invoke = sql_impl(expr);
403    Operation::variadic(move |ecx, args| {
404        let types = args.iter().map(|arg| ecx.scalar_type(arg)).collect();
405        let mut out = invoke(ecx.qcx, types)?;
406        out.splice_parameters(&args, 0);
407        Ok(out)
408    })
409}
410
411// Defines a built-in table function from a static SQL SELECT statement.
412//
413// The SQL statement should use the standard parameter syntax (`$1`, `$2`, ...)
414// to refer to the inputs to the function; see sql_impl_func for an example.
415//
416// The number of parameters in the SQL expression must exactly match the number
417// of parameters in the built-in's declaration. There is no support for variadic
418// functions.
419//
420// As this is a full SQL statement, it returns a set of rows, similar to a
421// table function. The SELECT's projection's names are used and should be
422// aliased if needed.
423fn sql_impl_table_func_inner(
424    sql: &'static str,
425    feature_flag: Option<&'static vars::FeatureFlag>,
426) -> Operation<TableFuncPlan> {
427    let query = match mz_sql_parser::parser::parse_statements(sql)
428        .expect("static function definition failed to parse")
429        .expect_element(|| "static function definition must have exactly one statement")
430        .ast
431    {
432        Statement::Select(SelectStatement { query, as_of: None }) => query,
433        _ => panic!("static function definition expected SELECT statement"),
434    };
435    let invoke = move |qcx: &QueryContext, types: Vec<ScalarType>| {
436        // Reconstruct an expression context where the parameter types are
437        // bound to the types of the expressions in `args`.
438        let mut scx = qcx.scx.clone();
439        scx.param_types = RefCell::new(
440            types
441                .into_iter()
442                .enumerate()
443                .map(|(i, ty)| (i + 1, ty))
444                .collect(),
445        );
446        let mut qcx = QueryContext::root(&scx, qcx.lifetime);
447
448        let query = query.clone();
449        let (mut query, _) = names::resolve(qcx.scx.catalog, query)?;
450        transform_ast::transform(&scx, &mut query)?;
451
452        query::plan_nested_query(&mut qcx, &query)
453    };
454
455    Operation::variadic(move |ecx, args| {
456        if let Some(feature_flag) = feature_flag {
457            ecx.require_feature_flag(feature_flag)?;
458        }
459        let types = args.iter().map(|arg| ecx.scalar_type(arg)).collect();
460        let (mut expr, scope) = invoke(ecx.qcx, types)?;
461        expr.splice_parameters(&args, 0);
462        Ok(TableFuncPlan {
463            expr,
464            column_names: scope.column_names().cloned().collect(),
465        })
466    })
467}
468
469fn sql_impl_table_func(sql: &'static str) -> Operation<TableFuncPlan> {
470    sql_impl_table_func_inner(sql, None)
471}
472
473fn experimental_sql_impl_table_func(
474    feature: &'static vars::FeatureFlag,
475    sql: &'static str,
476) -> Operation<TableFuncPlan> {
477    sql_impl_table_func_inner(sql, Some(feature))
478}
479
480/// Describes a single function's implementation.
481pub struct FuncImpl<R> {
482    pub oid: u32,
483    pub params: ParamList,
484    pub return_type: ReturnType,
485    pub op: Operation<R>,
486}
487
488/// Describes how each implementation should be represented in the catalog.
489#[derive(Debug)]
490pub struct FuncImplCatalogDetails {
491    pub oid: u32,
492    pub arg_typs: Vec<&'static str>,
493    pub variadic_typ: Option<&'static str>,
494    pub return_typ: Option<&'static str>,
495    pub return_is_set: bool,
496}
497
498impl<R> FuncImpl<R> {
499    pub fn details(&self) -> FuncImplCatalogDetails {
500        FuncImplCatalogDetails {
501            oid: self.oid,
502            arg_typs: self.params.arg_names(),
503            variadic_typ: self.params.variadic_name(),
504            return_typ: self.return_type.typ.as_ref().map(|t| t.name()),
505            return_is_set: self.return_type.is_set_of,
506        }
507    }
508}
509
510impl<R> fmt::Debug for FuncImpl<R> {
511    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
512        f.debug_struct("FuncImpl")
513            .field("oid", &self.oid)
514            .field("params", &self.params)
515            .field("ret", &self.return_type)
516            .field("op", &"<omitted>")
517            .finish()
518    }
519}
520
521impl From<UnmaterializableFunc> for Operation<HirScalarExpr> {
522    fn from(n: UnmaterializableFunc) -> Operation<HirScalarExpr> {
523        Operation::nullary(move |_ecx| Ok(HirScalarExpr::call_unmaterializable(n.clone())))
524    }
525}
526
527impl From<UnaryFunc> for Operation<HirScalarExpr> {
528    fn from(u: UnaryFunc) -> Operation<HirScalarExpr> {
529        Operation::unary(move |_ecx, e| Ok(e.call_unary(u.clone())))
530    }
531}
532
533impl From<BinaryFunc> for Operation<HirScalarExpr> {
534    fn from(b: BinaryFunc) -> Operation<HirScalarExpr> {
535        Operation::binary(move |_ecx, left, right| Ok(left.call_binary(right, b.clone())))
536    }
537}
538
539impl From<VariadicFunc> for Operation<HirScalarExpr> {
540    fn from(v: VariadicFunc) -> Operation<HirScalarExpr> {
541        Operation::variadic(move |_ecx, exprs| Ok(HirScalarExpr::call_variadic(v.clone(), exprs)))
542    }
543}
544
545impl From<AggregateFunc> for Operation<(HirScalarExpr, AggregateFunc)> {
546    fn from(a: AggregateFunc) -> Operation<(HirScalarExpr, AggregateFunc)> {
547        Operation::unary(move |_ecx, e| Ok((e, a.clone())))
548    }
549}
550
551impl From<ScalarWindowFunc> for Operation<ScalarWindowFunc> {
552    fn from(a: ScalarWindowFunc) -> Operation<ScalarWindowFunc> {
553        Operation::nullary(move |_ecx| Ok(a.clone()))
554    }
555}
556
557impl From<ValueWindowFunc> for Operation<(HirScalarExpr, ValueWindowFunc)> {
558    fn from(a: ValueWindowFunc) -> Operation<(HirScalarExpr, ValueWindowFunc)> {
559        Operation::unary(move |_ecx, e| Ok((e, a.clone())))
560    }
561}
562
563#[derive(Debug, Clone, Eq, PartialEq, Hash)]
564/// Describes possible types of function parameters.
565///
566/// Note that this is not exhaustive and will likely require additions.
567pub enum ParamList {
568    Exact(Vec<ParamType>),
569    Variadic {
570        leading: Vec<ParamType>,
571        trailing: ParamType,
572    },
573}
574
575impl ParamList {
576    /// Determines whether `typs` are compatible with `self`.
577    fn matches_argtypes(&self, ecx: &ExprContext, typs: &[CoercibleScalarType]) -> bool {
578        if !self.validate_arg_len(typs.len()) {
579            return false;
580        }
581
582        for (i, typ) in typs.iter().enumerate() {
583            let param = &self[i];
584            if let CoercibleScalarType::Coerced(typ) = typ {
585                // Ensures either `typ` can at least be implicitly cast to a
586                // type `param` accepts. Implicit in this check is that unknown
587                // type arguments can be cast to any type.
588                //
589                // N.B. this will require more fallthrough checks once we
590                // support RECORD types in functions.
591                if !param.accepts_type(ecx, typ) {
592                    return false;
593                }
594            }
595        }
596
597        // Ensure a polymorphic solution exists (non-polymorphic functions have
598        // trivial polymorphic solutions that evaluate to `None`).
599        PolymorphicSolution::new(ecx, typs, self).is_some()
600    }
601
602    /// Validates that the number of input elements are viable for `self`.
603    fn validate_arg_len(&self, input_len: usize) -> bool {
604        match self {
605            Self::Exact(p) => p.len() == input_len,
606            Self::Variadic { leading, .. } => input_len > leading.len(),
607        }
608    }
609
610    /// Matches a `&[ScalarType]` derived from the user's function argument
611    /// against this `ParamList`'s permitted arguments.
612    fn exact_match(&self, types: &[&ScalarType]) -> bool {
613        types.iter().enumerate().all(|(i, t)| self[i] == **t)
614    }
615
616    /// Generates values underlying data for for `mz_catalog.mz_functions.arg_ids`.
617    fn arg_names(&self) -> Vec<&'static str> {
618        match self {
619            ParamList::Exact(p) => p.iter().map(|p| p.name()).collect::<Vec<_>>(),
620            ParamList::Variadic { leading, trailing } => leading
621                .iter()
622                .chain([trailing])
623                .map(|p| p.name())
624                .collect::<Vec<_>>(),
625        }
626    }
627
628    /// Generates values for `mz_catalog.mz_functions.variadic_id`.
629    fn variadic_name(&self) -> Option<&'static str> {
630        match self {
631            ParamList::Exact(_) => None,
632            ParamList::Variadic { trailing, .. } => Some(trailing.name()),
633        }
634    }
635}
636
637impl std::ops::Index<usize> for ParamList {
638    type Output = ParamType;
639
640    fn index(&self, i: usize) -> &Self::Output {
641        match self {
642            Self::Exact(p) => &p[i],
643            Self::Variadic { leading, trailing } => leading.get(i).unwrap_or(trailing),
644        }
645    }
646}
647
648/// Provides a shorthand function for writing `ParamList::Exact`.
649impl From<Vec<ParamType>> for ParamList {
650    fn from(p: Vec<ParamType>) -> ParamList {
651        ParamList::Exact(p)
652    }
653}
654
655#[derive(Debug, Clone, Eq, PartialEq, Hash)]
656/// Describes parameter types.
657///
658/// Parameters with "Compatible" in their name are used in conjunction with
659/// other "Compatible"-type parameters to determine the best common type to cast
660/// arguments to.
661///
662/// "Compatible" parameters contrast with parameters that contain "Any" in their
663/// name, but not "Compatible." These parameters require all other "Any"-type
664/// parameters be of the same type from the perspective of
665/// [`ScalarType::base_eq`].
666///
667/// For more details on polymorphic parameter resolution, see `PolymorphicSolution`.
668pub enum ParamType {
669    /// A pseudotype permitting any type. Note that this parameter does not
670    /// enforce the same "Any" constraint as the other "Any"-type parameters.
671    Any,
672    /// A pseudotype permitting any type, permitting other "Compatibility"-type
673    /// parameters to find the best common type.
674    AnyCompatible,
675    /// An pseudotype permitting any type, requiring other "Any"-type parameters
676    /// to be of the same type.
677    AnyElement,
678    /// An pseudotype permitting any array type, requiring other "Any"-type
679    /// parameters to be of the same type.
680    ArrayAny,
681    /// A pseudotype permitting any array type, permitting other "Compatibility"-type
682    /// parameters to find the best common type.
683    ArrayAnyCompatible,
684    /// An pseudotype permitting any list type, requiring other "Any"-type
685    /// parameters to be of the same type.
686    ListAny,
687    /// A pseudotype permitting any list type, permitting other
688    /// "Compatibility"-type parameters to find the best common type.
689    ListAnyCompatible,
690    /// A pseudotype permitting any type, permitting other "Compatibility"-type
691    /// parameters to find the best common type. Additionally, enforces a
692    /// constraint that when used with `ListAnyCompatible`, resolves to that
693    /// argument's element type.
694    ListElementAnyCompatible,
695    /// An pseudotype permitting any map type, requiring other "Any"-type
696    /// parameters to be of the same type.
697    MapAny,
698    /// A pseudotype permitting any map type, permitting other "Compatibility"-type
699    /// parameters to find the best common type.
700    MapAnyCompatible,
701    /// A pseudotype permitting any type except `ScalarType::List` and
702    /// `ScalarType::Array`, requiring other "Any"-type
703    /// parameters to be of the same type.
704    NonVecAny,
705    /// A pseudotype permitting any type except `ScalarType::List` and
706    /// `ScalarType::Array`, requiring other "Compatibility"-type
707    /// parameters to be of the same type.
708    NonVecAnyCompatible,
709    /// A standard parameter that accepts arguments that match its embedded
710    /// `ScalarType`.
711    Plain(ScalarType),
712    /// A polymorphic pseudotype permitting a `ScalarType::Record` of any type,
713    /// but all records must be structurally equal.
714    RecordAny,
715    /// An pseudotype permitting any range type, requiring other "Any"-type
716    /// parameters to be of the same type.
717    RangeAny,
718    /// A pseudotype permitting any range type, permitting other
719    /// "Compatibility"-type parameters to find the best common type.
720    ///
721    /// Prefer using [`ParamType::RangeAny`] over this type; it is easy to fool
722    /// this type into generating non-existent range types (e.g. ranges of
723    /// floats) that will panic.
724    RangeAnyCompatible,
725    /// A psuedotype indicating that the function is only meant to be called
726    /// internally by the database system.
727    Internal,
728}
729
730impl ParamType {
731    /// Does `self` accept arguments of type `t`?
732    fn accepts_type(&self, ecx: &ExprContext, t: &ScalarType) -> bool {
733        use ParamType::*;
734        use ScalarType::*;
735
736        match self {
737            Any | AnyElement | AnyCompatible | ListElementAnyCompatible => true,
738            ArrayAny | ArrayAnyCompatible => matches!(t, Array(..) | Int2Vector),
739            ListAny | ListAnyCompatible => matches!(t, List { .. }),
740            MapAny | MapAnyCompatible => matches!(t, Map { .. }),
741            RangeAny | RangeAnyCompatible => matches!(t, Range { .. }),
742            NonVecAny | NonVecAnyCompatible => !t.is_vec(),
743            Internal => false,
744            Plain(to) => typeconv::can_cast(ecx, CastContext::Implicit, t, to),
745            RecordAny => matches!(t, Record { .. }),
746        }
747    }
748
749    /// Does `t`'s [`TypeCategory`] prefer `self`? This question can make
750    /// more sense with the understanding that pseudotypes are never preferred.
751    fn is_preferred_by(&self, t: &ScalarType) -> bool {
752        if let Some(pt) = TypeCategory::from_type(t).preferred_type() {
753            *self == pt
754        } else {
755            false
756        }
757    }
758
759    /// Is `self` the [`ParamType`] corresponding to `t`'s [near match] value?
760    ///
761    /// [near match]: ScalarType::near_match
762    fn is_near_match(&self, t: &ScalarType) -> bool {
763        match (self, t.near_match()) {
764            (ParamType::Plain(t), Some(near_match)) => t.structural_eq(near_match),
765            _ => false,
766        }
767    }
768
769    /// Is `self` the preferred parameter type for its `TypeCategory`?
770    fn prefers_self(&self) -> bool {
771        if let Some(pt) = TypeCategory::from_param(self).preferred_type() {
772            *self == pt
773        } else {
774            false
775        }
776    }
777
778    fn is_polymorphic(&self) -> bool {
779        use ParamType::*;
780        match self {
781            AnyElement
782            | ArrayAny
783            | ArrayAnyCompatible
784            | AnyCompatible
785            | ListAny
786            | ListAnyCompatible
787            | ListElementAnyCompatible
788            | MapAny
789            | MapAnyCompatible
790            | NonVecAny
791            | NonVecAnyCompatible
792            // In PG, RecordAny isn't polymorphic even though it offers
793            // polymorphic behavior. For more detail, see
794            // `PolymorphicCompatClass::StructuralEq`.
795            | RecordAny
796            | RangeAny
797            | RangeAnyCompatible => true,
798            Any | Internal | Plain(_)  => false,
799        }
800    }
801
802    fn name(&self) -> &'static str {
803        match self {
804            ParamType::Plain(t) => {
805                assert!(
806                    !t.is_custom_type(),
807                    "custom types cannot currently be used as parameters; use a polymorphic parameter that accepts the custom type instead"
808                );
809                let t: mz_pgrepr::Type = t.into();
810                t.catalog_name()
811            }
812            ParamType::Any => "any",
813            ParamType::AnyCompatible => "anycompatible",
814            ParamType::AnyElement => "anyelement",
815            ParamType::ArrayAny => "anyarray",
816            ParamType::ArrayAnyCompatible => "anycompatiblearray",
817            ParamType::Internal => "internal",
818            ParamType::ListAny => "list",
819            ParamType::ListAnyCompatible => "anycompatiblelist",
820            // ListElementAnyCompatible is not identical to AnyCompatible, but reusing its ID appears harmless
821            ParamType::ListElementAnyCompatible => "anycompatible",
822            ParamType::MapAny => "map",
823            ParamType::MapAnyCompatible => "anycompatiblemap",
824            ParamType::NonVecAny => "anynonarray",
825            ParamType::NonVecAnyCompatible => "anycompatiblenonarray",
826            ParamType::RecordAny => "record",
827            ParamType::RangeAny => "anyrange",
828            ParamType::RangeAnyCompatible => "anycompatiblerange",
829        }
830    }
831}
832
833impl PartialEq<ScalarType> for ParamType {
834    fn eq(&self, other: &ScalarType) -> bool {
835        match self {
836            ParamType::Plain(s) => s.base_eq(other),
837            // Pseudotypes never equal concrete types
838            _ => false,
839        }
840    }
841}
842
843impl PartialEq<ParamType> for ScalarType {
844    fn eq(&self, other: &ParamType) -> bool {
845        other == self
846    }
847}
848
849impl From<ScalarType> for ParamType {
850    fn from(s: ScalarType) -> ParamType {
851        ParamType::Plain(s)
852    }
853}
854
855impl From<ScalarBaseType> for ParamType {
856    fn from(s: ScalarBaseType) -> ParamType {
857        use ScalarBaseType::*;
858        let s = match s {
859            Array | List | Map | Record | Range => {
860                panic!("use polymorphic parameters rather than {:?}", s);
861            }
862            AclItem => ScalarType::AclItem,
863            Bool => ScalarType::Bool,
864            Int16 => ScalarType::Int16,
865            Int32 => ScalarType::Int32,
866            Int64 => ScalarType::Int64,
867            UInt16 => ScalarType::UInt16,
868            UInt32 => ScalarType::UInt32,
869            UInt64 => ScalarType::UInt64,
870            Float32 => ScalarType::Float32,
871            Float64 => ScalarType::Float64,
872            Numeric => ScalarType::Numeric { max_scale: None },
873            Date => ScalarType::Date,
874            Time => ScalarType::Time,
875            Timestamp => ScalarType::Timestamp { precision: None },
876            TimestampTz => ScalarType::TimestampTz { precision: None },
877            Interval => ScalarType::Interval,
878            Bytes => ScalarType::Bytes,
879            String => ScalarType::String,
880            Char => ScalarType::Char { length: None },
881            VarChar => ScalarType::VarChar { max_length: None },
882            PgLegacyChar => ScalarType::PgLegacyChar,
883            PgLegacyName => ScalarType::PgLegacyName,
884            Jsonb => ScalarType::Jsonb,
885            Uuid => ScalarType::Uuid,
886            Oid => ScalarType::Oid,
887            RegClass => ScalarType::RegClass,
888            RegProc => ScalarType::RegProc,
889            RegType => ScalarType::RegType,
890            Int2Vector => ScalarType::Int2Vector,
891            MzTimestamp => ScalarType::MzTimestamp,
892            MzAclItem => ScalarType::MzAclItem,
893        };
894        ParamType::Plain(s)
895    }
896}
897
898#[derive(Debug, Clone, Eq, PartialEq, Hash)]
899pub struct ReturnType {
900    pub typ: Option<ParamType>,
901    pub is_set_of: bool,
902}
903
904impl ReturnType {
905    /// Expresses that a function's return type is a scalar value.
906    fn scalar(typ: ParamType) -> ReturnType {
907        ReturnType {
908            typ: Some(typ),
909            is_set_of: false,
910        }
911    }
912
913    /// Expresses that a function's return type is a set of values, e.g. a table
914    /// function.
915    fn set_of(typ: ParamType) -> ReturnType {
916        ReturnType {
917            typ: Some(typ),
918            is_set_of: true,
919        }
920    }
921
922    /// Expresses that a function's return type is None.
923    fn none(is_set_of: bool) -> ReturnType {
924        ReturnType {
925            typ: None,
926            is_set_of,
927        }
928    }
929}
930
931impl From<ParamType> for ReturnType {
932    fn from(typ: ParamType) -> ReturnType {
933        ReturnType::scalar(typ)
934    }
935}
936
937impl From<ScalarBaseType> for ReturnType {
938    fn from(s: ScalarBaseType) -> ReturnType {
939        ParamType::from(s).into()
940    }
941}
942
943impl From<ScalarType> for ReturnType {
944    fn from(s: ScalarType) -> ReturnType {
945        ParamType::Plain(s).into()
946    }
947}
948
949#[derive(Clone, Debug)]
950/// Tracks candidate implementations.
951pub struct Candidate<'a, R> {
952    /// The implementation under consideration.
953    fimpl: &'a FuncImpl<R>,
954    exact_matches: usize,
955    preferred_types: usize,
956    near_matches: usize,
957}
958
959/// Selects the best implementation given the provided `args` using a
960/// process similar to [PostgreSQL's parser][pgparser], and returns the
961/// `ScalarExpr` to invoke that function.
962///
963/// Inline comments prefixed with number are taken from the "Function Type
964/// Resolution" section of the aforelinked page.
965///
966/// # Errors
967/// - When the provided arguments are not valid for any implementation, e.g.
968///   cannot be converted to the appropriate types.
969/// - When all implementations are equally valid.
970///
971/// [pgparser]: https://www.postgresql.org/docs/current/typeconv-oper.html
972pub fn select_impl<R>(
973    ecx: &ExprContext,
974    spec: FuncSpec,
975    impls: &[FuncImpl<R>],
976    args: Vec<CoercibleScalarExpr>,
977    order_by: Vec<ColumnOrder>,
978) -> Result<R, PlanError>
979where
980    R: fmt::Debug,
981{
982    let name = spec.to_string();
983    let ecx = &ecx.with_name(&name);
984    let mut types: Vec<_> = args.iter().map(|e| ecx.scalar_type(e)).collect();
985
986    // PostgreSQL force coerces all record types before function selection. We
987    // may want to do something smarter in the future (e.g., a function that
988    // accepts multiple `RecordAny` parameters should perhaps coerce to the
989    // result of calling `guess_best_common_type` on all those parameters), but
990    // for now we just directly match PostgreSQL's behavior.
991    for ty in &mut types {
992        ty.force_coerced_if_record();
993    }
994
995    // 4.a. Discard candidate functions for which the input types do not
996    // match and cannot be converted (using an implicit conversion) to
997    // match. unknown literals are assumed to be convertible to anything for
998    // this purpose.
999    let impls: Vec<_> = impls
1000        .iter()
1001        .filter(|i| i.params.matches_argtypes(ecx, &types))
1002        .collect();
1003
1004    let f = find_match(ecx, &types, impls).map_err(|candidates| {
1005        let arg_types: Vec<_> = types
1006            .into_iter()
1007            .map(|ty| match ty {
1008                // This will be used in error msgs, therefore we call with `postgres_compat` false.
1009                CoercibleScalarType::Coerced(ty) => ecx.humanize_scalar_type(&ty, false),
1010                CoercibleScalarType::Record(_) => "record".to_string(),
1011                CoercibleScalarType::Uncoerced => "unknown".to_string(),
1012            })
1013            .collect();
1014
1015        if candidates == 0 {
1016            match spec {
1017                FuncSpec::Func(name) => PlanError::UnknownFunction {
1018                    name: ecx
1019                        .qcx
1020                        .scx
1021                        .humanize_resolved_name(name)
1022                        .expect("resolved to object")
1023                        .to_string(),
1024                    arg_types,
1025                },
1026                FuncSpec::Op(name) => PlanError::UnknownOperator {
1027                    name: name.to_string(),
1028                    arg_types,
1029                },
1030            }
1031        } else {
1032            match spec {
1033                FuncSpec::Func(name) => PlanError::IndistinctFunction {
1034                    name: ecx
1035                        .qcx
1036                        .scx
1037                        .humanize_resolved_name(name)
1038                        .expect("resolved to object")
1039                        .to_string(),
1040                    arg_types,
1041                },
1042                FuncSpec::Op(name) => PlanError::IndistinctOperator {
1043                    name: name.to_string(),
1044                    arg_types,
1045                },
1046            }
1047        }
1048    })?;
1049
1050    (f.op.0)(ecx, args, &f.params, order_by)
1051}
1052
1053/// Finds an exact match based on the arguments, or, if no exact match, finds
1054/// the best match available. Patterned after [PostgreSQL's type conversion
1055/// matching algorithm][pgparser].
1056///
1057/// [pgparser]: https://www.postgresql.org/docs/current/typeconv-func.html
1058fn find_match<'a, R: std::fmt::Debug>(
1059    ecx: &ExprContext,
1060    types: &[CoercibleScalarType],
1061    impls: Vec<&'a FuncImpl<R>>,
1062) -> Result<&'a FuncImpl<R>, usize> {
1063    let all_types_known = types.iter().all(|t| t.is_coerced());
1064
1065    // Check for exact match.
1066    if all_types_known {
1067        let known_types: Vec<_> = types.iter().filter_map(|t| t.as_coerced()).collect();
1068        let matching_impls: Vec<&FuncImpl<_>> = impls
1069            .iter()
1070            .filter(|i| i.params.exact_match(&known_types))
1071            .cloned()
1072            .collect();
1073
1074        if matching_impls.len() == 1 {
1075            return Ok(matching_impls[0]);
1076        }
1077    }
1078
1079    // No exact match. Apply PostgreSQL's best match algorithm. Generate
1080    // candidates by assessing their compatibility with each implementation's
1081    // parameters.
1082    let mut candidates: Vec<Candidate<_>> = Vec::new();
1083    macro_rules! maybe_get_last_candidate {
1084        () => {
1085            if candidates.len() == 1 {
1086                return Ok(&candidates[0].fimpl);
1087            }
1088        };
1089    }
1090    let mut max_exact_matches = 0;
1091
1092    for fimpl in impls {
1093        let mut exact_matches = 0;
1094        let mut preferred_types = 0;
1095        let mut near_matches = 0;
1096
1097        for (i, arg_type) in types.iter().enumerate() {
1098            let param_type = &fimpl.params[i];
1099
1100            match arg_type {
1101                CoercibleScalarType::Coerced(arg_type) => {
1102                    if param_type == arg_type {
1103                        exact_matches += 1;
1104                    }
1105                    if param_type.is_preferred_by(arg_type) {
1106                        preferred_types += 1;
1107                    }
1108                    if param_type.is_near_match(arg_type) {
1109                        near_matches += 1;
1110                    }
1111                }
1112                CoercibleScalarType::Record(_) | CoercibleScalarType::Uncoerced => {
1113                    if param_type.prefers_self() {
1114                        preferred_types += 1;
1115                    }
1116                }
1117            }
1118        }
1119
1120        // 4.a. Discard candidate functions for which the input types do not
1121        // match and cannot be converted (using an implicit conversion) to
1122        // match. unknown literals are assumed to be convertible to anything for
1123        // this purpose.
1124        max_exact_matches = std::cmp::max(max_exact_matches, exact_matches);
1125        candidates.push(Candidate {
1126            fimpl,
1127            exact_matches,
1128            preferred_types,
1129            near_matches,
1130        });
1131    }
1132
1133    if candidates.is_empty() {
1134        return Err(0);
1135    }
1136
1137    maybe_get_last_candidate!();
1138
1139    // 4.c. Run through all candidates and keep those with the most exact
1140    // matches on input types. Keep all candidates if none have exact matches.
1141    candidates.retain(|c| c.exact_matches >= max_exact_matches);
1142
1143    maybe_get_last_candidate!();
1144
1145    // 4.c.i. (MZ extension) Run through all candidates and keep those with the
1146    // most 'near' matches on input types. Keep all candidates if none have near
1147    // matches. If only one candidate remains, use it; else continue to the next
1148    // step.
1149    let mut max_near_matches = 0;
1150    for c in &candidates {
1151        max_near_matches = std::cmp::max(max_near_matches, c.near_matches);
1152    }
1153    candidates.retain(|c| c.near_matches >= max_near_matches);
1154
1155    // 4.d. Run through all candidates and keep those that accept preferred
1156    // types (of the input data type's type category) at the most positions
1157    // where type conversion will be required.
1158    let mut max_preferred_types = 0;
1159    for c in &candidates {
1160        max_preferred_types = std::cmp::max(max_preferred_types, c.preferred_types);
1161    }
1162    candidates.retain(|c| c.preferred_types >= max_preferred_types);
1163
1164    maybe_get_last_candidate!();
1165
1166    if all_types_known {
1167        return Err(candidates.len());
1168    }
1169
1170    let mut found_known = false;
1171    let mut types_match = true;
1172    let mut common_type: Option<ScalarType> = None;
1173
1174    for (i, arg_type) in types.iter().enumerate() {
1175        let mut selected_category: Option<TypeCategory> = None;
1176        let mut categories_match = true;
1177
1178        match arg_type {
1179            // 4.e. If any input arguments are unknown, check the type
1180            // categories accepted at those argument positions by the remaining
1181            // candidates.
1182            CoercibleScalarType::Uncoerced | CoercibleScalarType::Record(_) => {
1183                for c in candidates.iter() {
1184                    let this_category = TypeCategory::from_param(&c.fimpl.params[i]);
1185                    // 4.e. cont: Select the string category if any candidate
1186                    // accepts that category. (This bias towards string is
1187                    // appropriate since an unknown-type literal looks like a
1188                    // string.)
1189                    if this_category == TypeCategory::String {
1190                        selected_category = Some(TypeCategory::String);
1191                        break;
1192                    }
1193                    match selected_category {
1194                        Some(ref mut selected_category) => {
1195                            // 4.e. cont: [...otherwise,] if all the remaining candidates
1196                            // accept the same type category, select that category.
1197                            categories_match =
1198                                selected_category == &this_category && categories_match;
1199                        }
1200                        None => selected_category = Some(this_category.clone()),
1201                    }
1202                }
1203
1204                // 4.e. cont: Otherwise fail because the correct choice cannot
1205                // be deduced without more clues.
1206                // (ed: this doesn't mean fail entirely, simply moving onto 4.f)
1207                if selected_category != Some(TypeCategory::String) && !categories_match {
1208                    break;
1209                }
1210
1211                // 4.e. cont: Now discard candidates that do not accept the
1212                // selected type category. Furthermore, if any candidate accepts
1213                // a preferred type in that category, discard candidates that
1214                // accept non-preferred types for that argument.
1215                let selected_category = selected_category.unwrap();
1216
1217                let preferred_type = selected_category.preferred_type();
1218                let mut found_preferred_type_candidate = false;
1219                candidates.retain(|c| {
1220                    if let Some(typ) = &preferred_type {
1221                        found_preferred_type_candidate = c.fimpl.params[i].accepts_type(ecx, typ)
1222                            || found_preferred_type_candidate;
1223                    }
1224                    selected_category == TypeCategory::from_param(&c.fimpl.params[i])
1225                });
1226
1227                if found_preferred_type_candidate {
1228                    let preferred_type = preferred_type.unwrap();
1229                    candidates.retain(|c| c.fimpl.params[i].accepts_type(ecx, &preferred_type));
1230                }
1231            }
1232            CoercibleScalarType::Coerced(typ) => {
1233                found_known = true;
1234                // Track if all known types are of the same type; use this info
1235                // in 4.f.
1236                match common_type {
1237                    Some(ref common_type) => types_match = common_type == typ && types_match,
1238                    None => common_type = Some(typ.clone()),
1239                }
1240            }
1241        }
1242    }
1243
1244    maybe_get_last_candidate!();
1245
1246    // 4.f. If there are both unknown and known-type arguments, and all the
1247    // known-type arguments have the same type, assume that the unknown
1248    // arguments are also of that type, and check which candidates can accept
1249    // that type at the unknown-argument positions.
1250    // (ed: We know unknown argument exists if we're in this part of the code.)
1251    if found_known && types_match {
1252        let common_type = common_type.unwrap();
1253        let common_typed: Vec<_> = types
1254            .iter()
1255            .map(|t| match t {
1256                CoercibleScalarType::Coerced(t) => CoercibleScalarType::Coerced(t.clone()),
1257                CoercibleScalarType::Uncoerced | CoercibleScalarType::Record(_) => {
1258                    CoercibleScalarType::Coerced(common_type.clone())
1259                }
1260            })
1261            .collect();
1262
1263        candidates.retain(|c| c.fimpl.params.matches_argtypes(ecx, &common_typed));
1264
1265        maybe_get_last_candidate!();
1266    }
1267
1268    Err(candidates.len())
1269}
1270
1271#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
1272enum PolymorphicCompatClass {
1273    /// Represents the older "Any"-style matching of PG polymorphic types, which
1274    /// constrains all types to be of the same type, i.e. does not attempt to
1275    /// promote parameters to a best common type.
1276    Any,
1277    /// Represent's Postgres' "anycompatible"-type polymorphic resolution.
1278    ///
1279    /// > Selection of the common type considers the actual types of
1280    /// > anycompatible and anycompatiblenonarray inputs, the array element
1281    /// > types of anycompatiblearray inputs, the range subtypes of
1282    /// > anycompatiblerange inputs, and the multirange subtypes of
1283    /// > anycompatiblemultirange inputs. If anycompatiblenonarray is present
1284    /// > then the common type is required to be a non-array type. Once a common
1285    /// > type is identified, arguments in anycompatible and
1286    /// > anycompatiblenonarray positions are automatically cast to that type,
1287    /// > and arguments in anycompatiblearray positions are automatically cast
1288    /// > to the array type for that type.
1289    ///
1290    /// For details, see
1291    /// <https://www.postgresql.org/docs/current/extend-type-system.html#EXTEND-TYPES-POLYMORPHIC>
1292    BestCommonAny,
1293    /// Represents polymorphic compatibility operations for Materialize LIST
1294    /// types. This differs from PG's "anycompatible" type resolution, which
1295    /// focuses on determining a common type, and e.g. using that as
1296    /// `AnyCompatibleArray`'s element type. Instead, our list compatibility
1297    /// focuses on finding a common list type, and then casting
1298    /// `ListElementAnyCompatible` parameters to that list type's elements. This
1299    /// approach is necessary to let us polymorphically resolve custom list
1300    /// types without losing their OIDs.
1301    BestCommonList,
1302    /// Represents an operation similar to LIST compatibility, but for MAP. This
1303    /// is distinct from `BestCommonList` in as much as the parameter types that
1304    /// work with `BestCommonList` are incommensurate with the parameter types
1305    /// used with `BestCommonMap`.
1306    BestCommonMap,
1307    /// Represents type resolution for `ScalarType::Record` types, which e.g.
1308    /// ignores custom types and type modifications.
1309    ///
1310    /// In [PG], this is handled by invocation of the function calls that take
1311    /// `RecordAny` params, which we want to avoid if at all possible.
1312    ///
1313    /// [PG]:
1314    ///     https://github.com/postgres/postgres/blob/33a377608fc29cdd1f6b63be561eab0aee5c81f0/src/backend/utils/adt/rowtypes.c#L1041
1315    StructuralEq,
1316}
1317
1318impl TryFrom<&ParamType> for PolymorphicCompatClass {
1319    type Error = ();
1320    fn try_from(param: &ParamType) -> Result<PolymorphicCompatClass, Self::Error> {
1321        use ParamType::*;
1322
1323        Ok(match param {
1324            AnyElement | ArrayAny | ListAny | MapAny | NonVecAny | RangeAny => {
1325                PolymorphicCompatClass::Any
1326            }
1327            ArrayAnyCompatible | AnyCompatible | RangeAnyCompatible | NonVecAnyCompatible => {
1328                PolymorphicCompatClass::BestCommonAny
1329            }
1330            ListAnyCompatible | ListElementAnyCompatible => PolymorphicCompatClass::BestCommonList,
1331            MapAnyCompatible => PolymorphicCompatClass::BestCommonMap,
1332            RecordAny => PolymorphicCompatClass::StructuralEq,
1333            _ => return Err(()),
1334        })
1335    }
1336}
1337
1338impl PolymorphicCompatClass {
1339    fn compatible(&self, ecx: &ExprContext, from: &ScalarType, to: &ScalarType) -> bool {
1340        use PolymorphicCompatClass::*;
1341        match self {
1342            StructuralEq => from.structural_eq(to),
1343            Any => from.base_eq(to),
1344            _ => typeconv::can_cast(ecx, CastContext::Implicit, from, to),
1345        }
1346    }
1347}
1348
1349/// Represents a solution to a set of polymorphic constraints, expressed as the
1350/// `params` of a function and the user-supplied `args`.
1351#[derive(Debug)]
1352pub(crate) struct PolymorphicSolution {
1353    /// Constrains this solution to a particular form of polymorphic
1354    /// compatibility.
1355    compat: Option<PolymorphicCompatClass>,
1356    seen: Vec<CoercibleScalarType>,
1357    /// An internal representation of the discovered polymorphic type.
1358    key: Option<ScalarType>,
1359}
1360
1361impl PolymorphicSolution {
1362    /// Provides a solution to the polymorphic type constraints expressed in
1363    /// `params` based on the users' input in `args`. Returns `None` if a
1364    /// solution cannot be found.
1365    ///
1366    /// After constructing the `PolymorphicSolution`, access its solution using
1367    /// [`PolymorphicSolution::target_for_param_type`].
1368    fn new(
1369        ecx: &ExprContext,
1370        args: &[CoercibleScalarType],
1371        params: &ParamList,
1372    ) -> Option<PolymorphicSolution> {
1373        let mut r = PolymorphicSolution {
1374            compat: None,
1375            seen: vec![],
1376            key: None,
1377        };
1378
1379        for (i, scalar_type) in args.iter().cloned().enumerate() {
1380            r.track_seen(&params[i], scalar_type);
1381        }
1382
1383        if !r.determine_key(ecx) { None } else { Some(r) }
1384    }
1385
1386    /// Determines the desired type of polymorphic compatibility, as well as the
1387    /// values to determine a polymorphic solution.
1388    fn track_seen(&mut self, param: &ParamType, seen: CoercibleScalarType) {
1389        use ParamType::*;
1390
1391        self.seen.push(match param {
1392            // These represent the keys of their respective compatibility classes.
1393            AnyElement | AnyCompatible | ListAnyCompatible |  MapAnyCompatible | NonVecAny | RecordAny => seen,
1394            MapAny => seen.map_coerced(|array| array.unwrap_map_value_type().clone()),
1395            ListAny => seen.map_coerced(|array| array.unwrap_list_element_type().clone()),
1396            ArrayAny | ArrayAnyCompatible => seen.map_coerced(|array| array.unwrap_array_element_type().clone()),
1397            RangeAny | RangeAnyCompatible => seen.map_coerced(|range| range.unwrap_range_element_type().clone()),
1398            ListElementAnyCompatible => seen.map_coerced(|el| ScalarType::List {
1399                custom_id: None,
1400                element_type: Box::new(el),
1401            }),
1402            o => {
1403                assert!(
1404                    !o.is_polymorphic(),
1405                    "polymorphic parameters must track types they encounter to determine polymorphic solution"
1406                );
1407                return;
1408            }
1409        });
1410
1411        let compat_class = param
1412            .try_into()
1413            .expect("already returned for non-polymorphic params");
1414
1415        match &self.compat {
1416            None => self.compat = Some(compat_class),
1417            Some(c) => {
1418                assert_eq!(
1419                    c, &compat_class,
1420                    "do not know how to correlate polymorphic classes {:?} and {:?}",
1421                    c, &compat_class,
1422                )
1423            }
1424        };
1425    }
1426
1427    /// Attempt to resolve all polymorphic types to a single "key" type. For
1428    /// `target_for_param_type` to be useful, this must have already been
1429    /// called.
1430    fn determine_key(&mut self, ecx: &ExprContext) -> bool {
1431        self.key = if !self.seen.iter().any(|v| v.is_coerced()) {
1432            match &self.compat {
1433                // No encountered param was polymorphic
1434                None => None,
1435                // Params were polymorphic, but we never received a known type.
1436                // This cannot be delegated to `guess_best_common_type`, which
1437                // will incorrectly guess string, which is incompatible with
1438                // `BestCommonList`, `BestCommonMap`.
1439                Some(t) => match t {
1440                    PolymorphicCompatClass::BestCommonAny => Some(ScalarType::String),
1441                    PolymorphicCompatClass::BestCommonList => Some(ScalarType::List {
1442                        custom_id: None,
1443                        element_type: Box::new(ScalarType::String),
1444                    }),
1445                    PolymorphicCompatClass::BestCommonMap => Some(ScalarType::Map {
1446                        value_type: Box::new(ScalarType::String),
1447                        custom_id: None,
1448                    }),
1449                    // Do not infer type.
1450                    PolymorphicCompatClass::StructuralEq | PolymorphicCompatClass::Any => None,
1451                },
1452            }
1453        } else {
1454            // If we saw any polymorphic parameters, we must have determined the
1455            // compatibility type.
1456            let compat = self.compat.as_ref().unwrap();
1457
1458            let r = match compat {
1459                PolymorphicCompatClass::Any => {
1460                    let mut s = self
1461                        .seen
1462                        .iter()
1463                        .filter_map(|f| f.as_coerced().cloned())
1464                        .collect::<Vec<_>>();
1465                    let (candiate, remaining) =
1466                        s.split_first().expect("have at least one non-None element");
1467                    if remaining.iter().all(|r| r.base_eq(candiate)) {
1468                        s.remove(0)
1469                    } else {
1470                        return false;
1471                    }
1472                }
1473                _ => match typeconv::guess_best_common_type(ecx, &self.seen) {
1474                    Ok(r) => r,
1475                    Err(_) => return false,
1476                },
1477            };
1478
1479            // Ensure the best common type is compatible.
1480            for t in self.seen.iter() {
1481                if let CoercibleScalarType::Coerced(t) = t {
1482                    if !compat.compatible(ecx, t, &r) {
1483                        return false;
1484                    }
1485                }
1486            }
1487            Some(r)
1488        };
1489
1490        true
1491    }
1492
1493    // Determines the appropriate `ScalarType` for the given `ParamType` based
1494    // on the polymorphic solution.
1495    fn target_for_param_type(&self, param: &ParamType) -> Option<ScalarType> {
1496        use ParamType::*;
1497        assert_eq!(
1498            self.compat,
1499            Some(
1500                param
1501                    .try_into()
1502                    .expect("target_for_param_type only supports polymorphic parameters")
1503            ),
1504            "cannot use polymorphic solution for different compatibility classes"
1505        );
1506
1507        assert!(
1508            !matches!(param, RecordAny),
1509            "RecordAny should not be cast to a target type"
1510        );
1511
1512        match param {
1513            AnyElement | AnyCompatible | ListAnyCompatible | MapAnyCompatible | NonVecAny => {
1514                self.key.clone()
1515            }
1516            ArrayAny | ArrayAnyCompatible => self
1517                .key
1518                .as_ref()
1519                .map(|key| ScalarType::Array(Box::new(key.clone()))),
1520            ListAny => self.key.as_ref().map(|key| ScalarType::List {
1521                element_type: Box::new(key.clone()),
1522                custom_id: None,
1523            }),
1524            MapAny => self.key.as_ref().map(|key| ScalarType::Map {
1525                value_type: Box::new(key.clone()),
1526                custom_id: None,
1527            }),
1528            RangeAny | RangeAnyCompatible => self.key.as_ref().map(|key| ScalarType::Range {
1529                element_type: Box::new(key.clone()),
1530            }),
1531            ListElementAnyCompatible => self
1532                .key
1533                .as_ref()
1534                .map(|key| key.unwrap_list_element_type().clone()),
1535            _ => unreachable!(
1536                "cannot use polymorphic solution to resolve target type for param {:?}",
1537                param,
1538            ),
1539        }
1540    }
1541}
1542
1543fn coerce_args_to_types(
1544    ecx: &ExprContext,
1545    args: Vec<CoercibleScalarExpr>,
1546    params: &ParamList,
1547) -> Result<Vec<HirScalarExpr>, PlanError> {
1548    use ParamType::*;
1549
1550    let mut scalar_types: Vec<_> = args.iter().map(|e| ecx.scalar_type(e)).collect();
1551
1552    // See comment in `select_impl`.
1553    for ty in &mut scalar_types {
1554        ty.force_coerced_if_record();
1555    }
1556
1557    let polymorphic_solution = PolymorphicSolution::new(ecx, &scalar_types, params)
1558        .expect("polymorphic solution previously determined to be valid");
1559
1560    let do_convert =
1561        |arg: CoercibleScalarExpr, ty: &ScalarType| arg.cast_to(ecx, CastContext::Implicit, ty);
1562
1563    let mut res_exprs = Vec::with_capacity(args.len());
1564    for (i, cexpr) in args.into_iter().enumerate() {
1565        let expr = match &params[i] {
1566            Any => match cexpr {
1567                CoercibleScalarExpr::Parameter(n) => {
1568                    sql_bail!("could not determine data type of parameter ${}", n)
1569                }
1570                _ => cexpr.type_as_any(ecx)?,
1571            },
1572            RecordAny => match cexpr {
1573                CoercibleScalarExpr::LiteralString(_) => {
1574                    sql_bail!("input of anonymous composite types is not implemented");
1575                }
1576                // By passing the creation of the polymorphic solution, we've
1577                // already ensured that all of the record types are
1578                // intrinsically well-typed enough to move onto the next step.
1579                _ => cexpr.type_as_any(ecx)?,
1580            },
1581            Plain(ty) => do_convert(cexpr, ty)?,
1582            Internal => return Err(PlanError::InternalFunctionCall),
1583            p => {
1584                let target = polymorphic_solution
1585                    .target_for_param_type(p)
1586                    .ok_or_else(|| {
1587                        // n.b. This errors here, rather than during building
1588                        // the polymorphic solution, to make the error clearer.
1589                        // If we errored while constructing the polymorphic
1590                        // solution, an implementation would get discarded even
1591                        // if it were the only one, and it would appear as if a
1592                        // compatible solution did not exist. Instead, the
1593                        // problem is simply that we couldn't resolve the
1594                        // polymorphic type.
1595                        PlanError::UnsolvablePolymorphicFunctionInput
1596                    })?;
1597                do_convert(cexpr, &target)?
1598            }
1599        };
1600        res_exprs.push(expr);
1601    }
1602
1603    Ok(res_exprs)
1604}
1605
1606/// Provides shorthand for converting `Vec<ScalarType>` into `Vec<ParamType>`.
1607macro_rules! params {
1608    ([$($p:expr),*], $v:ident...) => { ParamList::Variadic { leading: vec![$($p.into(),)*], trailing: $v.into() } };
1609    ($v:ident...) => { ParamList::Variadic { leading: vec![], trailing: $v.into() } };
1610    ($($p:expr),*) => { ParamList::Exact(vec![$($p.into(),)*]) };
1611}
1612
1613macro_rules! impl_def {
1614    // Return type explicitly specified. This must be the case in situations
1615    // such as:
1616    // - Polymorphic functions: We have no way of understanding if the input
1617    //   type affects the return type, so you must tell us what the return type
1618    //   is.
1619    // - Explicitly defined Operations whose returned expression does not
1620    //   appropriately correlate to the function itself, e.g. returning a
1621    //   UnaryFunc from a FuncImpl that takes two parameters.
1622    // - Unimplemented/catalog-only functions
1623    ($params:expr, $op:expr, $return_type:expr, $oid:expr) => {{
1624        FuncImpl {
1625            oid: $oid,
1626            params: $params.into(),
1627            op: $op.into(),
1628            return_type: $return_type.into(),
1629        }
1630    }};
1631}
1632
1633/// Constructs builtin function map.
1634macro_rules! builtins {
1635    {
1636        $(
1637            $name:expr => $ty:ident {
1638                $($params:expr => $op:expr => $return_type:expr, $oid:expr;)+
1639            }
1640        ),+
1641    } => {{
1642
1643        let mut builtins = BTreeMap::new();
1644        $(
1645            let impls = vec![$(impl_def!($params, $op, $return_type, $oid)),+];
1646            let func = Func::$ty(impls);
1647            let expect_set_return = matches!(&func, Func::Table(_));
1648            for imp in func.func_impls() {
1649                assert_eq!(imp.return_is_set, expect_set_return, "wrong set return value for func with oid {}", imp.oid);
1650            }
1651            let old = builtins.insert($name, func);
1652            mz_ore::assert_none!(old, "duplicate entry in builtins list");
1653        )+
1654        builtins
1655    }};
1656}
1657
1658#[derive(Debug)]
1659pub struct TableFuncPlan {
1660    pub expr: HirRelationExpr,
1661    pub column_names: Vec<ColumnName>,
1662}
1663
1664#[derive(Debug)]
1665pub enum Func {
1666    Scalar(Vec<FuncImpl<HirScalarExpr>>),
1667    Aggregate(Vec<FuncImpl<(HirScalarExpr, AggregateFunc)>>),
1668    Table(Vec<FuncImpl<TableFuncPlan>>),
1669    ScalarWindow(Vec<FuncImpl<ScalarWindowFunc>>),
1670    ValueWindow(Vec<FuncImpl<(HirScalarExpr, ValueWindowFunc)>>),
1671}
1672
1673impl Func {
1674    pub fn func_impls(&self) -> Vec<FuncImplCatalogDetails> {
1675        match self {
1676            Func::Scalar(impls) => impls.iter().map(|f| f.details()).collect::<Vec<_>>(),
1677            Func::Aggregate(impls) => impls.iter().map(|f| f.details()).collect::<Vec<_>>(),
1678            Func::Table(impls) => impls.iter().map(|f| f.details()).collect::<Vec<_>>(),
1679            Func::ScalarWindow(impls) => impls.iter().map(|f| f.details()).collect::<Vec<_>>(),
1680            Func::ValueWindow(impls) => impls.iter().map(|f| f.details()).collect::<Vec<_>>(),
1681        }
1682    }
1683
1684    pub fn class(&self) -> &str {
1685        match self {
1686            Func::Scalar(..) => "scalar",
1687            Func::Aggregate(..) => "aggregate",
1688            Func::Table(..) => "table",
1689            Func::ScalarWindow(..) => "window",
1690            Func::ValueWindow(..) => "window",
1691        }
1692    }
1693}
1694
1695/// Functions using this macro should be transformed/planned away before
1696/// reaching function selection code, but still need to be present in the
1697/// catalog during planning.
1698macro_rules! catalog_name_only {
1699    ($name:expr) => {
1700        panic!(
1701            "{} should be planned away before reaching function selection",
1702            $name
1703        )
1704    };
1705}
1706
1707/// Generates an (OID, OID, TEXT) SQL implementation for has_X_privilege style functions.
1708macro_rules! privilege_fn {
1709    ( $fn_name:expr, $catalog_tbl:expr ) => {
1710        {
1711            let fn_name = $fn_name;
1712            let catalog_tbl = $catalog_tbl;
1713            let public_role = RoleId::Public;
1714            format!(
1715                "
1716                    CASE
1717                    -- We need to validate the privileges to return a proper error before anything
1718                    -- else.
1719                    WHEN NOT mz_internal.mz_validate_privileges($3)
1720                    OR $1 IS NULL
1721                    OR $2 IS NULL
1722                    OR $3 IS NULL
1723                    OR $1 NOT IN (SELECT oid FROM mz_catalog.mz_roles)
1724                    OR $2 NOT IN (SELECT oid FROM {catalog_tbl})
1725                    THEN NULL
1726                    ELSE COALESCE(
1727                        (
1728                            SELECT
1729                                bool_or(
1730                                    mz_internal.mz_acl_item_contains_privilege(privilege, $3)
1731                                )
1732                                    AS {fn_name}
1733                            FROM
1734                                (
1735                                    SELECT
1736                                        unnest(privileges)
1737                                    FROM
1738                                        {catalog_tbl}
1739                                    WHERE
1740                                        {catalog_tbl}.oid = $2
1741                                )
1742                                    AS user_privs (privilege)
1743                                LEFT JOIN mz_catalog.mz_roles ON
1744                                        mz_internal.mz_aclitem_grantee(privilege) = mz_roles.id
1745                            WHERE
1746                                mz_internal.mz_aclitem_grantee(privilege) = '{public_role}' OR pg_has_role($1, mz_roles.oid, 'USAGE')
1747                        ),
1748                        false
1749                    )
1750                    END
1751                ",
1752            )
1753        }
1754    };
1755}
1756
1757/// Correlates a built-in function name to its implementations.
1758pub static PG_CATALOG_BUILTINS: LazyLock<BTreeMap<&'static str, Func>> = LazyLock::new(|| {
1759    use ParamType::*;
1760    use ScalarBaseType::*;
1761    let mut builtins = builtins! {
1762        // Literal OIDs collected from PG 13 using a version of this query
1763        // ```sql
1764        // SELECT oid, proname, proargtypes::regtype[]
1765        // FROM pg_proc
1766        // WHERE proname IN (
1767        //      'ascii', 'array_upper', 'jsonb_build_object'
1768        // );
1769        // ```
1770        // Values are also available through
1771        // https://github.com/postgres/postgres/blob/master/src/include/catalog/pg_proc.dat
1772
1773        // Scalars.
1774        "abs" => Scalar {
1775            params!(Int16) => UnaryFunc::AbsInt16(func::AbsInt16) => Int16, 1398;
1776            params!(Int32) => UnaryFunc::AbsInt32(func::AbsInt32) => Int32, 1397;
1777            params!(Int64) => UnaryFunc::AbsInt64(func::AbsInt64) => Int64, 1396;
1778            params!(Numeric) => UnaryFunc::AbsNumeric(func::AbsNumeric) => Numeric, 1705;
1779            params!(Float32) => UnaryFunc::AbsFloat32(func::AbsFloat32) => Float32, 1394;
1780            params!(Float64) => UnaryFunc::AbsFloat64(func::AbsFloat64) => Float64, 1395;
1781        },
1782        "aclexplode" => Table {
1783            params!(ScalarType::Array(Box::new(ScalarType::AclItem))) =>  Operation::unary(move |_ecx, aclitems| {
1784                Ok(TableFuncPlan {
1785                    expr: HirRelationExpr::CallTable {
1786                        func: TableFunc::AclExplode,
1787                        exprs: vec![aclitems],
1788                    },
1789                    column_names: vec!["grantor".into(), "grantee".into(), "privilege_type".into(), "is_grantable".into()],
1790                })
1791            }) => ReturnType::set_of(RecordAny), 1689;
1792        },
1793        "array_cat" => Scalar {
1794            params!(ArrayAnyCompatible, ArrayAnyCompatible) => Operation::binary(|_ecx, lhs, rhs| {
1795                Ok(lhs.call_binary(rhs, BinaryFunc::ArrayArrayConcat))
1796            }) => ArrayAnyCompatible, 383;
1797        },
1798        "array_fill" => Scalar {
1799            params!(AnyElement, ScalarType::Array(Box::new(ScalarType::Int32))) => Operation::binary(|ecx, elem, dims| {
1800                let elem_type = ecx.scalar_type(&elem);
1801
1802                let elem_type = match elem_type.array_of_self_elem_type() {
1803                    Ok(elem_type) => elem_type,
1804                    Err(elem_type) => bail_unsupported!(
1805                        // This will be used in error msgs, therefore we call with `postgres_compat` false.
1806                        format!("array_fill on {}", ecx.humanize_scalar_type(&elem_type, false))
1807                    ),
1808                };
1809
1810                Ok(HirScalarExpr::call_variadic(VariadicFunc::ArrayFill { elem_type }, vec![elem, dims]))
1811            }) => ArrayAny, 1193;
1812            params!(
1813                AnyElement,
1814                ScalarType::Array(Box::new(ScalarType::Int32)),
1815                ScalarType::Array(Box::new(ScalarType::Int32))
1816            ) => Operation::variadic(|ecx, exprs| {
1817                let elem_type = ecx.scalar_type(&exprs[0]);
1818
1819                let elem_type = match elem_type.array_of_self_elem_type() {
1820                    Ok(elem_type) => elem_type,
1821                    Err(elem_type) => bail_unsupported!(
1822                        format!("array_fill on {}", ecx.humanize_scalar_type(&elem_type, false))
1823                    ),
1824                };
1825
1826                Ok(HirScalarExpr::call_variadic(VariadicFunc::ArrayFill { elem_type }, exprs))
1827            }) => ArrayAny, 1286;
1828        },
1829        "array_length" => Scalar {
1830            params![ArrayAny, Int64] => BinaryFunc::ArrayLength => Int32, 2176;
1831        },
1832        "array_lower" => Scalar {
1833            params!(ArrayAny, Int64) => BinaryFunc::ArrayLower => Int32, 2091;
1834        },
1835        "array_position" => Scalar {
1836            params!(ArrayAnyCompatible, AnyCompatible) => VariadicFunc::ArrayPosition => Int32, 3277;
1837            params!(ArrayAnyCompatible, AnyCompatible, Int32) => VariadicFunc::ArrayPosition => Int32, 3278;
1838        },
1839        "array_remove" => Scalar {
1840            params!(ArrayAnyCompatible, AnyCompatible) => BinaryFunc::ArrayRemove => ArrayAnyCompatible, 3167;
1841        },
1842        "array_to_string" => Scalar {
1843            params!(ArrayAny, String) => Operation::variadic(array_to_string) => String, 395;
1844            params!(ArrayAny, String, String) => Operation::variadic(array_to_string) => String, 384;
1845        },
1846        "array_upper" => Scalar {
1847            params!(ArrayAny, Int64) => BinaryFunc::ArrayUpper => Int32, 2092;
1848        },
1849        "ascii" => Scalar {
1850            params!(String) => UnaryFunc::Ascii(func::Ascii) => Int32, 1620;
1851        },
1852        "avg" => Scalar {
1853            params!(Int64) => Operation::nullary(|_ecx| catalog_name_only!("avg")) => Numeric, 2100;
1854            params!(Int32) => Operation::nullary(|_ecx| catalog_name_only!("avg")) => Numeric, 2101;
1855            params!(Int16) => Operation::nullary(|_ecx| catalog_name_only!("avg")) => Numeric, 2102;
1856            params!(UInt64) => Operation::nullary(|_ecx| catalog_name_only!("avg")) => Numeric, oid::FUNC_AVG_UINT64_OID;
1857            params!(UInt32) => Operation::nullary(|_ecx| catalog_name_only!("avg")) => Numeric, oid::FUNC_AVG_UINT32_OID;
1858            params!(UInt16) => Operation::nullary(|_ecx| catalog_name_only!("avg")) => Numeric, oid::FUNC_AVG_UINT16_OID;
1859            params!(Float32) => Operation::nullary(|_ecx| catalog_name_only!("avg")) => Float64, 2104;
1860            params!(Float64) => Operation::nullary(|_ecx| catalog_name_only!("avg")) => Float64, 2105;
1861            params!(Interval) => Operation::nullary(|_ecx| catalog_name_only!("avg")) => Interval, 2106;
1862        },
1863        "bit_count" => Scalar {
1864            params!(Bytes) => UnaryFunc::BitCountBytes(func::BitCountBytes) => Int64, 6163;
1865        },
1866        "bit_length" => Scalar {
1867            params!(Bytes) => UnaryFunc::BitLengthBytes(func::BitLengthBytes) => Int32, 1810;
1868            params!(String) => UnaryFunc::BitLengthString(func::BitLengthString) => Int32, 1811;
1869        },
1870        "btrim" => Scalar {
1871            params!(String) => UnaryFunc::TrimWhitespace(func::TrimWhitespace) => String, 885;
1872            params!(String, String) => BinaryFunc::Trim => String, 884;
1873        },
1874        "cbrt" => Scalar {
1875            params!(Float64) => UnaryFunc::CbrtFloat64(func::CbrtFloat64) => Float64, 1345;
1876        },
1877        "ceil" => Scalar {
1878            params!(Float32) => UnaryFunc::CeilFloat32(func::CeilFloat32) => Float32, oid::FUNC_CEIL_F32_OID;
1879            params!(Float64) => UnaryFunc::CeilFloat64(func::CeilFloat64) => Float64, 2308;
1880            params!(Numeric) => UnaryFunc::CeilNumeric(func::CeilNumeric) => Numeric, 1711;
1881        },
1882        "ceiling" => Scalar {
1883            params!(Float32) => UnaryFunc::CeilFloat32(func::CeilFloat32) => Float32, oid::FUNC_CEILING_F32_OID;
1884            params!(Float64) => UnaryFunc::CeilFloat64(func::CeilFloat64) => Float64, 2320;
1885            params!(Numeric) => UnaryFunc::CeilNumeric(func::CeilNumeric) => Numeric, 2167;
1886        },
1887        "char_length" => Scalar {
1888            params!(String) => UnaryFunc::CharLength(func::CharLength) => Int32, 1381;
1889        },
1890        // SQL exactly matches PostgreSQL's implementation.
1891        "col_description" => Scalar {
1892            params!(Oid, Int32) => sql_impl_func(
1893                "(SELECT description
1894                    FROM pg_description
1895                    WHERE objoid = $1 AND classoid = 'pg_class'::regclass AND objsubid = $2)"
1896                ) => String, 1216;
1897        },
1898        "concat" => Scalar {
1899            params!(Any...) => Operation::variadic(|ecx, cexprs| {
1900                if cexprs.is_empty() {
1901                    sql_bail!("No function matches the given name and argument types. \
1902                    You might need to add explicit type casts.")
1903                }
1904                let mut exprs = vec![];
1905                for expr in cexprs {
1906                    exprs.push(match ecx.scalar_type(&expr) {
1907                        // concat uses nonstandard bool -> string casts
1908                        // to match historical baggage in PostgreSQL.
1909                        ScalarType::Bool => expr.call_unary(UnaryFunc::CastBoolToStringNonstandard(func::CastBoolToStringNonstandard)),
1910                        // TODO(see <materialize#7572>): remove call to PadChar
1911                        ScalarType::Char { length } => expr.call_unary(UnaryFunc::PadChar(func::PadChar { length })),
1912                        _ => typeconv::to_string(ecx, expr)
1913                    });
1914                }
1915                Ok(HirScalarExpr::call_variadic(VariadicFunc::Concat, exprs))
1916            }) => String, 3058;
1917        },
1918        "concat_ws" => Scalar {
1919            params!([String], Any...) => Operation::variadic(|ecx, cexprs| {
1920                if cexprs.len() < 2 {
1921                    sql_bail!("No function matches the given name and argument types. \
1922                    You might need to add explicit type casts.")
1923                }
1924                let mut exprs = vec![];
1925                for expr in cexprs {
1926                    exprs.push(match ecx.scalar_type(&expr) {
1927                        // concat uses nonstandard bool -> string casts
1928                        // to match historical baggage in PostgreSQL.
1929                        ScalarType::Bool => expr.call_unary(UnaryFunc::CastBoolToStringNonstandard(func::CastBoolToStringNonstandard)),
1930                        // TODO(see <materialize#7572>): remove call to PadChar
1931                        ScalarType::Char { length } => expr.call_unary(UnaryFunc::PadChar(func::PadChar { length })),
1932                        _ => typeconv::to_string(ecx, expr)
1933                    });
1934                }
1935                Ok(HirScalarExpr::call_variadic(VariadicFunc::ConcatWs, exprs))
1936            }) => String, 3059;
1937        },
1938        "convert_from" => Scalar {
1939            params!(Bytes, String) => BinaryFunc::ConvertFrom => String, 1714;
1940        },
1941        "cos" => Scalar {
1942            params!(Float64) => UnaryFunc::Cos(func::Cos) => Float64, 1605;
1943        },
1944        "acos" => Scalar {
1945            params!(Float64) => UnaryFunc::Acos(func::Acos) => Float64, 1601;
1946        },
1947        "cosh" => Scalar {
1948            params!(Float64) => UnaryFunc::Cosh(func::Cosh) => Float64, 2463;
1949        },
1950        "acosh" => Scalar {
1951            params!(Float64) => UnaryFunc::Acosh(func::Acosh) => Float64, 2466;
1952        },
1953        "cot" => Scalar {
1954            params!(Float64) => UnaryFunc::Cot(func::Cot) => Float64, 1607;
1955        },
1956        "current_schema" => Scalar {
1957            // TODO: this should be `name`. This is tricky in Materialize
1958            // because `name` truncates to 63 characters but Materialize does
1959            // not have a limit on identifier length.
1960            params!() => UnmaterializableFunc::CurrentSchema => String, 1402;
1961        },
1962        "current_schemas" => Scalar {
1963            params!(Bool) => Operation::unary(|_ecx, e| {
1964                Ok(HirScalarExpr::if_then_else(
1965                     e,
1966                     HirScalarExpr::call_unmaterializable(UnmaterializableFunc::CurrentSchemasWithSystem),
1967                     HirScalarExpr::call_unmaterializable(UnmaterializableFunc::CurrentSchemasWithoutSystem),
1968                ))
1969                // TODO: this should be `name[]`. This is tricky in Materialize
1970                // because `name` truncates to 63 characters but Materialize
1971                // does not have a limit on identifier length.
1972            }) => ScalarType::Array(Box::new(ScalarType::String)), 1403;
1973        },
1974        "current_database" => Scalar {
1975            params!() => UnmaterializableFunc::CurrentDatabase => String, 861;
1976        },
1977        "current_catalog" => Scalar {
1978            params!() => UnmaterializableFunc::CurrentDatabase => String, oid::FUNC_CURRENT_CATALOG;
1979        },
1980        "current_setting" => Scalar {
1981            params!(String) => Operation::unary(|_ecx, name| {
1982                current_settings(name, HirScalarExpr::literal_false())
1983            }) => ScalarType::String, 2077;
1984            params!(String, Bool) => Operation::binary(|_ecx, name, missing_ok| {
1985                current_settings(name, missing_ok)
1986            }) => ScalarType::String, 3294;
1987        },
1988        "current_timestamp" => Scalar {
1989            params!() => UnmaterializableFunc::CurrentTimestamp => TimestampTz, oid::FUNC_CURRENT_TIMESTAMP_OID;
1990        },
1991        "current_user" => Scalar {
1992            params!() => UnmaterializableFunc::CurrentUser => String, 745;
1993        },
1994        "current_role" => Scalar {
1995            params!() => UnmaterializableFunc::CurrentUser => String, oid::FUNC_CURRENT_ROLE;
1996        },
1997        "user" => Scalar {
1998            params!() => UnmaterializableFunc::CurrentUser => String, oid::FUNC_USER;
1999        },
2000        "session_user" => Scalar {
2001            params!() => UnmaterializableFunc::SessionUser => String, 746;
2002        },
2003        "chr" => Scalar {
2004            params!(Int32) => UnaryFunc::Chr(func::Chr) => String, 1621;
2005        },
2006        "date" => Scalar {
2007            params!(String) => UnaryFunc::CastStringToDate(func::CastStringToDate) => Date, oid::FUNC_DATE_FROM_TEXT;
2008            params!(Timestamp) => UnaryFunc::CastTimestampToDate(func::CastTimestampToDate) => Date, 2029;
2009            params!(TimestampTz) => UnaryFunc::CastTimestampTzToDate(func::CastTimestampTzToDate) => Date, 1178;
2010        },
2011        "date_bin" => Scalar {
2012            params!(Interval, Timestamp) => Operation::binary(|ecx, stride, source| {
2013                ecx.require_feature_flag(&vars::ENABLE_BINARY_DATE_BIN)?;
2014                Ok(stride.call_binary(source, BinaryFunc::DateBinTimestamp))
2015            }) => Timestamp, oid::FUNC_MZ_DATE_BIN_UNIX_EPOCH_TS_OID;
2016            params!(Interval, TimestampTz) => Operation::binary(|ecx, stride, source| {
2017                ecx.require_feature_flag(&vars::ENABLE_BINARY_DATE_BIN)?;
2018                Ok(stride.call_binary(source, BinaryFunc::DateBinTimestampTz))
2019            }) => TimestampTz, oid::FUNC_MZ_DATE_BIN_UNIX_EPOCH_TSTZ_OID;
2020            params!(Interval, Timestamp, Timestamp) => VariadicFunc::DateBinTimestamp => Timestamp, 6177;
2021            params!(Interval, TimestampTz, TimestampTz) => VariadicFunc::DateBinTimestampTz => TimestampTz, 6178;
2022        },
2023        "extract" => Scalar {
2024            params!(String, Interval) => BinaryFunc::ExtractInterval => Numeric, 6204;
2025            params!(String, Time) => BinaryFunc::ExtractTime => Numeric, 6200;
2026            params!(String, Timestamp) => BinaryFunc::ExtractTimestamp => Numeric, 6202;
2027            params!(String, TimestampTz) => BinaryFunc::ExtractTimestampTz => Numeric, 6203;
2028            params!(String, Date) => BinaryFunc::ExtractDate => Numeric, 6199;
2029        },
2030        "date_part" => Scalar {
2031            params!(String, Interval) => BinaryFunc::DatePartInterval => Float64, 1172;
2032            params!(String, Time) => BinaryFunc::DatePartTime => Float64, 1385;
2033            params!(String, Timestamp) => BinaryFunc::DatePartTimestamp => Float64, 2021;
2034            params!(String, TimestampTz) => BinaryFunc::DatePartTimestampTz => Float64, 1171;
2035        },
2036        "date_trunc" => Scalar {
2037            params!(String, Timestamp) => BinaryFunc::DateTruncTimestamp => Timestamp, 2020;
2038            params!(String, TimestampTz) => BinaryFunc::DateTruncTimestampTz => TimestampTz, 1217;
2039            params!(String, Interval) => BinaryFunc::DateTruncInterval => Interval, 1218;
2040        },
2041        "daterange" => Scalar {
2042            params!(Date, Date) => Operation::variadic(|_ecx, mut exprs| {
2043                exprs.push(HirScalarExpr::literal(Datum::String("[)"), ScalarType::String));
2044                Ok(HirScalarExpr::call_variadic(VariadicFunc::RangeCreate { elem_type: ScalarType::Date },
2045                    exprs))
2046            }) => ScalarType::Range { element_type: Box::new(ScalarType::Date)}, 3941;
2047            params!(Date, Date, String) => Operation::variadic(|_ecx, exprs| {
2048                Ok(HirScalarExpr::call_variadic(VariadicFunc::RangeCreate { elem_type: ScalarType::Date },
2049                    exprs))
2050            }) => ScalarType::Range { element_type: Box::new(ScalarType::Date)}, 3942;
2051        },
2052        "degrees" => Scalar {
2053            params!(Float64) => UnaryFunc::Degrees(func::Degrees) => Float64, 1608;
2054        },
2055        "digest" => Scalar {
2056            params!(String, String) => BinaryFunc::DigestString => Bytes, oid::FUNC_PG_DIGEST_STRING;
2057            params!(Bytes, String) => BinaryFunc::DigestBytes => Bytes, oid::FUNC_PG_DIGEST_BYTES;
2058        },
2059        "exp" => Scalar {
2060            params!(Float64) => UnaryFunc::Exp(func::Exp) => Float64, 1347;
2061            params!(Numeric) => UnaryFunc::ExpNumeric(func::ExpNumeric) => Numeric, 1732;
2062        },
2063        "floor" => Scalar {
2064            params!(Float32) => UnaryFunc::FloorFloat32(func::FloorFloat32) => Float32, oid::FUNC_FLOOR_F32_OID;
2065            params!(Float64) => UnaryFunc::FloorFloat64(func::FloorFloat64) => Float64, 2309;
2066            params!(Numeric) => UnaryFunc::FloorNumeric(func::FloorNumeric) => Numeric, 1712;
2067        },
2068        "format_type" => Scalar {
2069            params!(Oid, Int32) => sql_impl_func(
2070                "CASE
2071                        WHEN $1 IS NULL THEN NULL
2072                        -- timestamp and timestamptz have the typmod in
2073                        -- a nonstandard location that requires special
2074                        -- handling.
2075                        WHEN $1 = 1114 AND $2 >= 0 THEN 'timestamp(' || $2 || ') without time zone'
2076                        WHEN $1 = 1184 AND $2 >= 0 THEN 'timestamp(' || $2 || ') with time zone'
2077                        ELSE coalesce((SELECT pg_catalog.concat(coalesce(mz_internal.mz_type_name($1), name), mz_internal.mz_render_typmod($1, $2)) FROM mz_catalog.mz_types WHERE oid = $1), '???')
2078                    END"
2079            ) => String, 1081;
2080        },
2081        "get_bit" => Scalar {
2082            params!(Bytes, Int32) => BinaryFunc::GetBit => Int32, 723;
2083        },
2084        "get_byte" => Scalar {
2085            params!(Bytes, Int32) => BinaryFunc::GetByte => Int32, 721;
2086        },
2087        "pg_get_ruledef" => Scalar {
2088            params!(Oid) => sql_impl_func("NULL::pg_catalog.text") => String, 1573;
2089            params!(Oid, Bool) => sql_impl_func("NULL::pg_catalog.text") => String, 2504;
2090        },
2091        "has_schema_privilege" => Scalar {
2092            params!(String, String, String) => sql_impl_func("has_schema_privilege(mz_internal.mz_role_oid($1), mz_internal.mz_schema_oid($2), $3)") => Bool, 2268;
2093            params!(String, Oid, String) => sql_impl_func("has_schema_privilege(mz_internal.mz_role_oid($1), $2, $3)") => Bool, 2269;
2094            params!(Oid, String, String) => sql_impl_func("has_schema_privilege($1, mz_internal.mz_schema_oid($2), $3)") => Bool, 2270;
2095            params!(Oid, Oid, String) => sql_impl_func(&privilege_fn!("has_schema_privilege", "mz_schemas")) => Bool, 2271;
2096            params!(String, String) => sql_impl_func("has_schema_privilege(current_user, $1, $2)") => Bool, 2272;
2097            params!(Oid, String) => sql_impl_func("has_schema_privilege(current_user, $1, $2)") => Bool, 2273;
2098        },
2099        "has_database_privilege" => Scalar {
2100            params!(String, String, String) => sql_impl_func("has_database_privilege(mz_internal.mz_role_oid($1), mz_internal.mz_database_oid($2), $3)") => Bool, 2250;
2101            params!(String, Oid, String) => sql_impl_func("has_database_privilege(mz_internal.mz_role_oid($1), $2, $3)") => Bool, 2251;
2102            params!(Oid, String, String) => sql_impl_func("has_database_privilege($1, mz_internal.mz_database_oid($2), $3)") => Bool, 2252;
2103            params!(Oid, Oid, String) => sql_impl_func(&privilege_fn!("has_database_privilege", "mz_databases")) => Bool, 2253;
2104            params!(String, String) => sql_impl_func("has_database_privilege(current_user, $1, $2)") => Bool, 2254;
2105            params!(Oid, String) => sql_impl_func("has_database_privilege(current_user, $1, $2)") => Bool, 2255;
2106        },
2107        "has_table_privilege" => Scalar {
2108            params!(String, String, String) => sql_impl_func("has_table_privilege(mz_internal.mz_role_oid($1), $2::regclass::oid, $3)") => Bool, 1922;
2109            params!(String, Oid, String) => sql_impl_func("has_table_privilege(mz_internal.mz_role_oid($1), $2, $3)") => Bool, 1923;
2110            params!(Oid, String, String) => sql_impl_func("has_table_privilege($1, $2::regclass::oid, $3)") => Bool, 1924;
2111            params!(Oid, Oid, String) => sql_impl_func(&privilege_fn!("has_table_privilege", "mz_relations")) => Bool, 1925;
2112            params!(String, String) => sql_impl_func("has_table_privilege(current_user, $1, $2)") => Bool, 1926;
2113            params!(Oid, String) => sql_impl_func("has_table_privilege(current_user, $1, $2)") => Bool, 1927;
2114        },
2115        "hmac" => Scalar {
2116            params!(String, String, String) => VariadicFunc::HmacString => Bytes, oid::FUNC_PG_HMAC_STRING;
2117            params!(Bytes, Bytes, String) => VariadicFunc::HmacBytes => Bytes, oid::FUNC_PG_HMAC_BYTES;
2118        },
2119        "initcap" => Scalar {
2120            params!(String) => UnaryFunc::Initcap(func::Initcap) => String, 872;
2121        },
2122        "int4range" => Scalar {
2123            params!(Int32, Int32) => Operation::variadic(|_ecx, mut exprs| {
2124                exprs.push(HirScalarExpr::literal(Datum::String("[)"), ScalarType::String));
2125                Ok(HirScalarExpr::call_variadic(VariadicFunc::RangeCreate { elem_type: ScalarType::Int32 },
2126                    exprs))
2127            }) => ScalarType::Range { element_type: Box::new(ScalarType::Int32)}, 3840;
2128            params!(Int32, Int32, String) => Operation::variadic(|_ecx, exprs| {
2129                Ok(HirScalarExpr::call_variadic(VariadicFunc::RangeCreate { elem_type: ScalarType::Int32 },
2130                    exprs))
2131            }) => ScalarType::Range { element_type: Box::new(ScalarType::Int32)}, 3841;
2132        },
2133        "int8range" => Scalar {
2134            params!(Int64, Int64) => Operation::variadic(|_ecx, mut exprs| {
2135                exprs.push(HirScalarExpr::literal(Datum::String("[)"), ScalarType::String));
2136                Ok(HirScalarExpr::call_variadic(VariadicFunc::RangeCreate { elem_type: ScalarType::Int64 },
2137                    exprs))
2138            }) => ScalarType::Range { element_type: Box::new(ScalarType::Int64)}, 3945;
2139            params!(Int64, Int64, String) => Operation::variadic(|_ecx, exprs| {
2140                Ok(HirScalarExpr::call_variadic(VariadicFunc::RangeCreate { elem_type: ScalarType::Int64 },
2141                    exprs))
2142                }) => ScalarType::Range { element_type: Box::new(ScalarType::Int64)}, 3946;
2143        },
2144        "isempty" => Scalar {
2145            params!(RangeAny) => UnaryFunc::RangeEmpty(func::RangeEmpty) => Bool, 3850;
2146        },
2147        "jsonb_array_length" => Scalar {
2148            params!(Jsonb) => UnaryFunc::JsonbArrayLength(func::JsonbArrayLength) => Int32, 3207;
2149        },
2150        "jsonb_build_array" => Scalar {
2151            params!() => VariadicFunc::JsonbBuildArray => Jsonb, 3272;
2152            params!(Any...) => Operation::variadic(|ecx, exprs| Ok(HirScalarExpr::call_variadic(VariadicFunc::JsonbBuildArray,
2153                exprs.into_iter().map(|e| typeconv::to_jsonb(ecx, e)).collect()))) => Jsonb, 3271;
2154        },
2155        "jsonb_build_object" => Scalar {
2156            params!() => VariadicFunc::JsonbBuildObject => Jsonb, 3274;
2157            params!(Any...) => Operation::variadic(|ecx, exprs| {
2158                if exprs.len() % 2 != 0 {
2159                    sql_bail!("argument list must have even number of elements")
2160                }
2161                Ok(HirScalarExpr::call_variadic(
2162                    VariadicFunc::JsonbBuildObject,
2163                    exprs.into_iter().tuples().map(|(key, val)| {
2164                        let key = typeconv::to_string(ecx, key);
2165                        let val = typeconv::to_jsonb(ecx, val);
2166                        vec![key, val]
2167                    }).flatten().collect()))
2168            }) => Jsonb, 3273;
2169        },
2170        "jsonb_pretty" => Scalar {
2171            params!(Jsonb) => UnaryFunc::JsonbPretty(func::JsonbPretty) => String, 3306;
2172        },
2173        "jsonb_strip_nulls" => Scalar {
2174            params!(Jsonb) => UnaryFunc::JsonbStripNulls(func::JsonbStripNulls) => Jsonb, 3262;
2175        },
2176        "jsonb_typeof" => Scalar {
2177            params!(Jsonb) => UnaryFunc::JsonbTypeof(func::JsonbTypeof) => String, 3210;
2178        },
2179        "justify_days" => Scalar {
2180            params!(Interval) => UnaryFunc::JustifyDays(func::JustifyDays) => Interval, 1295;
2181        },
2182        "justify_hours" => Scalar {
2183            params!(Interval) => UnaryFunc::JustifyHours(func::JustifyHours) => Interval, 1175;
2184        },
2185        "justify_interval" => Scalar {
2186            params!(Interval) => UnaryFunc::JustifyInterval(func::JustifyInterval) => Interval, 2711;
2187        },
2188        "left" => Scalar {
2189            params!(String, Int32) => BinaryFunc::Left => String, 3060;
2190        },
2191        "length" => Scalar {
2192            params!(Bytes) => UnaryFunc::ByteLengthBytes(func::ByteLengthBytes) => Int32, 2010;
2193            // bpcharlen is redundant with automatic coercion to string, 1318.
2194            params!(String) => UnaryFunc::CharLength(func::CharLength) => Int32, 1317;
2195            params!(Bytes, String) => BinaryFunc::EncodedBytesCharLength => Int32, 1713;
2196        },
2197        "like_escape" => Scalar {
2198            params!(String, String) => BinaryFunc::LikeEscape => String, 1637;
2199        },
2200        "ln" => Scalar {
2201            params!(Float64) => UnaryFunc::Ln(func::Ln) => Float64, 1341;
2202            params!(Numeric) => UnaryFunc::LnNumeric(func::LnNumeric) => Numeric, 1734;
2203        },
2204        "log10" => Scalar {
2205            params!(Float64) => UnaryFunc::Log10(func::Log10) => Float64, 1194;
2206            params!(Numeric) => UnaryFunc::Log10Numeric(func::Log10Numeric) => Numeric, 1481;
2207        },
2208        "log" => Scalar {
2209            params!(Float64) => UnaryFunc::Log10(func::Log10) => Float64, 1340;
2210            params!(Numeric) => UnaryFunc::Log10Numeric(func::Log10Numeric) => Numeric, 1741;
2211            params!(Numeric, Numeric) => BinaryFunc::LogNumeric => Numeric, 1736;
2212        },
2213        "lower" => Scalar {
2214            params!(String) => UnaryFunc::Lower(func::Lower) => String, 870;
2215            params!(RangeAny) => UnaryFunc::RangeLower(func::RangeLower) => AnyElement, 3848;
2216        },
2217        "lower_inc" => Scalar {
2218            params!(RangeAny) => UnaryFunc::RangeLowerInc(func::RangeLowerInc) => Bool, 3851;
2219        },
2220        "lower_inf" => Scalar {
2221            params!(RangeAny) => UnaryFunc::RangeLowerInf(func::RangeLowerInf) => Bool, 3853;
2222        },
2223        "lpad" => Scalar {
2224            params!(String, Int32) => VariadicFunc::PadLeading => String, 879;
2225            params!(String, Int32, String) => VariadicFunc::PadLeading => String, 873;
2226        },
2227        "ltrim" => Scalar {
2228            params!(String) => UnaryFunc::TrimLeadingWhitespace(func::TrimLeadingWhitespace) => String, 881;
2229            params!(String, String) => BinaryFunc::TrimLeading => String, 875;
2230        },
2231        "makeaclitem" => Scalar {
2232            params!(Oid, Oid, String, Bool) => VariadicFunc::MakeAclItem => AclItem, 1365;
2233        },
2234        "make_timestamp" => Scalar {
2235            params!(Int64, Int64, Int64, Int64, Int64, Float64) => VariadicFunc::MakeTimestamp => Timestamp, 3461;
2236        },
2237        "md5" => Scalar {
2238            params!(String) => Operation::unary(move |_ecx, input| {
2239                let algorithm = HirScalarExpr::literal(Datum::String("md5"), ScalarType::String);
2240                let encoding = HirScalarExpr::literal(Datum::String("hex"), ScalarType::String);
2241                Ok(input.call_binary(algorithm, BinaryFunc::DigestString).call_binary(encoding, BinaryFunc::Encode))
2242            }) => String, 2311;
2243            params!(Bytes) => Operation::unary(move |_ecx, input| {
2244                let algorithm = HirScalarExpr::literal(Datum::String("md5"), ScalarType::String);
2245                let encoding = HirScalarExpr::literal(Datum::String("hex"), ScalarType::String);
2246                Ok(input.call_binary(algorithm, BinaryFunc::DigestBytes).call_binary(encoding, BinaryFunc::Encode))
2247            }) => String, 2321;
2248        },
2249        "mod" => Scalar {
2250            params!(Numeric, Numeric) => Operation::nullary(|_ecx| catalog_name_only!("mod")) => Numeric, 1728;
2251            params!(Int16, Int16) => Operation::nullary(|_ecx| catalog_name_only!("mod")) => Int16, 940;
2252            params!(Int32, Int32) => Operation::nullary(|_ecx| catalog_name_only!("mod")) => Int32, 941;
2253            params!(Int64, Int64) => Operation::nullary(|_ecx| catalog_name_only!("mod")) => Int64, 947;
2254            params!(UInt16, UInt16) => Operation::nullary(|_ecx| catalog_name_only!("mod")) => UInt16, oid::FUNC_MOD_UINT16_OID;
2255            params!(UInt32, UInt32) => Operation::nullary(|_ecx| catalog_name_only!("mod")) => UInt32, oid::FUNC_MOD_UINT32_OID;
2256            params!(UInt64, UInt64) => Operation::nullary(|_ecx| catalog_name_only!("mod")) => UInt64, oid::FUNC_MOD_UINT64_OID;
2257        },
2258        "now" => Scalar {
2259            params!() => UnmaterializableFunc::CurrentTimestamp => TimestampTz, 1299;
2260        },
2261        "numrange" => Scalar {
2262            params!(Numeric, Numeric) => Operation::variadic(|_ecx, mut exprs| {
2263                exprs.push(HirScalarExpr::literal(Datum::String("[)"), ScalarType::String));
2264                Ok(HirScalarExpr::call_variadic(VariadicFunc::RangeCreate { elem_type: ScalarType::Numeric { max_scale: None } },
2265                    exprs))
2266            }) =>  ScalarType::Range { element_type: Box::new(ScalarType::Numeric { max_scale: None })}, 3844;
2267            params!(Numeric, Numeric, String) => Operation::variadic(|_ecx, exprs| {
2268                Ok(HirScalarExpr::call_variadic(VariadicFunc::RangeCreate { elem_type: ScalarType::Numeric { max_scale: None } },
2269                    exprs))
2270            }) => ScalarType::Range { element_type: Box::new(ScalarType::Numeric { max_scale: None })}, 3845;
2271        },
2272        "octet_length" => Scalar {
2273            params!(Bytes) => UnaryFunc::ByteLengthBytes(func::ByteLengthBytes) => Int32, 720;
2274            params!(String) => UnaryFunc::ByteLengthString(func::ByteLengthString) => Int32, 1374;
2275            params!(Char) => Operation::unary(|ecx, e| {
2276                let length = ecx.scalar_type(&e).unwrap_char_length();
2277                Ok(e.call_unary(UnaryFunc::PadChar(func::PadChar { length }))
2278                    .call_unary(UnaryFunc::ByteLengthString(func::ByteLengthString))
2279                )
2280            }) => Int32, 1375;
2281        },
2282        // SQL closely matches PostgreSQL's implementation.
2283        // We don't yet support casting to regnamespace, so use our constant for
2284        // the oid of 'pg_catalog'.
2285        "obj_description" => Scalar {
2286            params!(Oid, String) => sql_impl_func(&format!(
2287                "(SELECT description FROM pg_description
2288                  WHERE objoid = $1
2289                    AND classoid = (
2290                      SELECT oid FROM pg_class WHERE relname = $2 AND relnamespace = {})
2291                    AND objsubid = 0)",
2292                oid::SCHEMA_PG_CATALOG_OID
2293            )) => String, 1215;
2294        },
2295        "pg_column_size" => Scalar {
2296            params!(Any) => UnaryFunc::PgColumnSize(func::PgColumnSize) => Int32, 1269;
2297        },
2298        "pg_size_pretty" => Scalar {
2299            params!(Numeric) => UnaryFunc::PgSizePretty(func::PgSizePretty) => String, 3166;
2300        },
2301        "mz_row_size" => Scalar {
2302            params!(Any) => Operation::unary(|ecx, e| {
2303                let s = ecx.scalar_type(&e);
2304                if !matches!(s, ScalarType::Record{..}) {
2305                    sql_bail!("mz_row_size requires a record type");
2306                }
2307                Ok(e.call_unary(UnaryFunc::MzRowSize(func::MzRowSize)))
2308            }) => Int32, oid::FUNC_MZ_ROW_SIZE;
2309        },
2310        "parse_ident" => Scalar {
2311            params!(String) => Operation::unary(|_ecx, ident| {
2312                Ok(ident.call_binary(HirScalarExpr::literal_true(), BinaryFunc::ParseIdent))
2313            }) => ScalarType::Array(Box::new(ScalarType::String)),
2314                oid::FUNC_PARSE_IDENT_DEFAULT_STRICT;
2315            params!(String, Bool) => BinaryFunc::ParseIdent
2316                => ScalarType::Array(Box::new(ScalarType::String)), 1268;
2317        },
2318        "pg_encoding_to_char" => Scalar {
2319            // Materialize only supports UT8-encoded databases. Return 'UTF8' if Postgres'
2320            // encoding id for UTF8 (6) is provided, otherwise return 'NULL'.
2321            params!(Int64) => sql_impl_func("CASE WHEN $1 = 6 THEN 'UTF8' ELSE NULL END") => String, 1597;
2322        },
2323        "pg_backend_pid" => Scalar {
2324            params!() => UnmaterializableFunc::PgBackendPid => Int32, 2026;
2325        },
2326        // pg_get_constraintdef gives more info about a constraint within the `pg_constraint`
2327        // view. Certain meta commands rely on this function not throwing an error, but the
2328        // `pg_constraint` view is empty in materialize. Therefore we know any oid provided is
2329        // not a valid constraint, so we can return NULL which is what PostgreSQL does when
2330        // provided an invalid OID.
2331        "pg_get_constraintdef" => Scalar {
2332            params!(Oid) => Operation::unary(|_ecx, _oid|
2333                Ok(HirScalarExpr::literal_null(ScalarType::String))) => String, 1387;
2334            params!(Oid, Bool) => Operation::binary(|_ecx, _oid, _pretty|
2335                Ok(HirScalarExpr::literal_null(ScalarType::String))) => String, 2508;
2336        },
2337        // pg_get_indexdef reconstructs the creating command for an index. We only support
2338        // arrangement based indexes, so we can hardcode that in.
2339        // TODO(jkosh44): In order to include the index WITH options, they will need to be saved somewhere in the catalog
2340        "pg_get_indexdef" => Scalar {
2341            params!(Oid) => sql_impl_func(
2342                "(SELECT 'CREATE INDEX ' || i.name || ' ON ' || r.name || ' USING arrangement (' || (
2343                        SELECT pg_catalog.string_agg(cols.col_exp, ',' ORDER BY cols.index_position)
2344                        FROM (
2345                            SELECT c.name AS col_exp, ic.index_position
2346                            FROM mz_catalog.mz_index_columns AS ic
2347                            JOIN mz_catalog.mz_indexes AS i2 ON ic.index_id = i2.id
2348                            JOIN mz_catalog.mz_columns AS c ON i2.on_id = c.id AND ic.on_position = c.position
2349                            WHERE ic.index_id = i.id AND ic.on_expression IS NULL
2350                            UNION
2351                            SELECT ic.on_expression AS col_exp, ic.index_position
2352                            FROM mz_catalog.mz_index_columns AS ic
2353                            WHERE ic.index_id = i.id AND ic.on_expression IS NOT NULL
2354                        ) AS cols
2355                    ) || ')'
2356                    FROM mz_catalog.mz_indexes AS i
2357                    JOIN mz_catalog.mz_relations AS r ON i.on_id = r.id
2358                    WHERE i.oid = $1)"
2359            ) => String, 1643;
2360            // A position of 0 is treated as if no position was given.
2361            // Third parameter, pretty, is ignored.
2362            params!(Oid, Int32, Bool) => sql_impl_func(
2363                "(SELECT CASE WHEN $2 = 0 THEN pg_catalog.pg_get_indexdef($1) ELSE
2364                        (SELECT c.name
2365                        FROM mz_catalog.mz_indexes AS i
2366                        JOIN mz_catalog.mz_index_columns AS ic ON i.id = ic.index_id
2367                        JOIN mz_catalog.mz_columns AS c ON i.on_id = c.id AND ic.on_position = c.position
2368                        WHERE i.oid = $1 AND ic.on_expression IS NULL AND ic.index_position = $2
2369                        UNION
2370                        SELECT ic.on_expression
2371                        FROM mz_catalog.mz_indexes AS i
2372                        JOIN mz_catalog.mz_index_columns AS ic ON i.id = ic.index_id
2373                        WHERE i.oid = $1 AND ic.on_expression IS NOT NULL AND ic.index_position = $2)
2374                    END)"
2375            ) => String, 2507;
2376        },
2377        // pg_get_viewdef returns the (query part of) the given view's definition.
2378        // We currently don't support pretty-printing (the `Bool`/`Int32` parameters).
2379        "pg_get_viewdef" => Scalar {
2380            params!(String) => sql_impl_func(
2381                "(SELECT definition FROM mz_catalog.mz_views WHERE name = $1)"
2382            ) => String, 1640;
2383            params!(Oid) => sql_impl_func(
2384                "(SELECT definition FROM mz_catalog.mz_views WHERE oid = $1)"
2385            ) => String, 1641;
2386            params!(String, Bool) => sql_impl_func(
2387                "(SELECT definition FROM mz_catalog.mz_views WHERE name = $1)"
2388            ) => String, 2505;
2389            params!(Oid, Bool) => sql_impl_func(
2390                "(SELECT definition FROM mz_catalog.mz_views WHERE oid = $1)"
2391            ) => String, 2506;
2392            params!(Oid, Int32) => sql_impl_func(
2393                "(SELECT definition FROM mz_catalog.mz_views WHERE oid = $1)"
2394            ) => String, 3159;
2395        },
2396        // pg_get_expr is meant to convert the textual version of
2397        // pg_node_tree data into parseable expressions. However, we don't
2398        // use the pg_get_expr structure anywhere and the equivalent columns
2399        // in Materialize (e.g. index expressions) are already stored as
2400        // parseable expressions. So, we offer this function in the catalog
2401        // for ORM support, but make no effort to provide its semantics,
2402        // e.g. this also means we drop the Oid argument on the floor.
2403        "pg_get_expr" => Scalar {
2404            params!(String, Oid) => Operation::binary(|_ecx, l, _r| Ok(l)) => String, 1716;
2405            params!(String, Oid, Bool) => Operation::variadic(move |_ecx, mut args| Ok(args.remove(0))) => String, 2509;
2406        },
2407        "pg_get_userbyid" => Scalar {
2408            params!(Oid) => sql_impl_func(
2409                "CASE \
2410                   WHEN $1 IS NULL THEN NULL \
2411                   ELSE COALESCE(\
2412                     (SELECT name FROM mz_catalog.mz_roles WHERE oid = $1),\
2413                     'unknown (OID=' || $1 || ')'\
2414                   ) \
2415                END"
2416            ) => String, 1642;
2417        },
2418        // The privilege param is validated but ignored. That's because we haven't implemented
2419        // NOINHERIT roles, so it has no effect on the result.
2420        //
2421        // In PostgreSQL, this should always return true for superusers. In Materialize it's
2422        // impossible to determine if a role is a superuser since it's specific to a session. So we
2423        // cannot copy PostgreSQL semantics there.
2424        "pg_has_role" => Scalar {
2425            params!(String, String, String) => sql_impl_func("pg_has_role(mz_internal.mz_role_oid($1), mz_internal.mz_role_oid($2), $3)") => Bool, 2705;
2426            params!(String, Oid, String) => sql_impl_func("pg_has_role(mz_internal.mz_role_oid($1), $2, $3)") => Bool, 2706;
2427            params!(Oid, String, String) => sql_impl_func("pg_has_role($1, mz_internal.mz_role_oid($2), $3)") => Bool, 2707;
2428            params!(Oid, Oid, String) => sql_impl_func(
2429                "CASE
2430                -- We need to validate the privilege to return a proper error before anything
2431                -- else.
2432                WHEN NOT mz_internal.mz_validate_role_privilege($3)
2433                OR $1 IS NULL
2434                OR $2 IS NULL
2435                OR $3 IS NULL
2436                THEN NULL
2437                WHEN $1 NOT IN (SELECT oid FROM mz_catalog.mz_roles)
2438                OR $2 NOT IN (SELECT oid FROM mz_catalog.mz_roles)
2439                THEN false
2440                ELSE $2::text IN (SELECT UNNEST(mz_internal.mz_role_oid_memberships() -> $1::text))
2441                END",
2442            ) => Bool, 2708;
2443            params!(String, String) => sql_impl_func("pg_has_role(current_user, $1, $2)") => Bool, 2709;
2444            params!(Oid, String) => sql_impl_func("pg_has_role(current_user, $1, $2)") => Bool, 2710;
2445        },
2446        // pg_is_in_recovery indicates whether a recovery is still in progress. Materialize does
2447        // not have a concept of recovery, so we default to always returning false.
2448        "pg_is_in_recovery" => Scalar {
2449            params!() => Operation::nullary(|_ecx| {
2450                Ok(HirScalarExpr::literal_false())
2451            }) => Bool, 3810;
2452        },
2453        "pg_postmaster_start_time" => Scalar {
2454            params!() => UnmaterializableFunc::PgPostmasterStartTime => TimestampTz, 2560;
2455        },
2456        "pg_relation_size" => Scalar {
2457            params!(RegClass, String) => sql_impl_func("CASE WHEN $1 IS NULL OR $2 IS NULL THEN NULL ELSE -1::pg_catalog.int8 END") => Int64, 2332;
2458            params!(RegClass) => sql_impl_func("CASE WHEN $1 IS NULL THEN NULL ELSE -1::pg_catalog.int8 END") => Int64, 2325;
2459        },
2460        "pg_stat_get_numscans" => Scalar {
2461            params!(Oid) => sql_impl_func("CASE WHEN $1 IS NULL THEN NULL ELSE -1::pg_catalog.int8 END") => Int64, 1928;
2462        },
2463        "pg_table_is_visible" => Scalar {
2464            params!(Oid) => sql_impl_func(
2465                "(SELECT s.name = ANY(pg_catalog.current_schemas(true))
2466                     FROM mz_catalog.mz_objects o JOIN mz_catalog.mz_schemas s ON o.schema_id = s.id
2467                     WHERE o.oid = $1)"
2468            ) => Bool, 2079;
2469        },
2470        "pg_type_is_visible" => Scalar {
2471            params!(Oid) => sql_impl_func(
2472                "(SELECT s.name = ANY(pg_catalog.current_schemas(true))
2473                     FROM mz_catalog.mz_types t JOIN mz_catalog.mz_schemas s ON t.schema_id = s.id
2474                     WHERE t.oid = $1)"
2475            ) => Bool, 2080;
2476        },
2477        "pg_function_is_visible" => Scalar {
2478            params!(Oid) => sql_impl_func(
2479                "(SELECT s.name = ANY(pg_catalog.current_schemas(true))
2480                     FROM mz_catalog.mz_functions f JOIN mz_catalog.mz_schemas s ON f.schema_id = s.id
2481                     WHERE f.oid = $1)"
2482            ) => Bool, 2081;
2483        },
2484        // pg_tablespace_location indicates what path in the filesystem that a given tablespace is
2485        // located in. This concept does not make sense though in Materialize which is a cloud
2486        // native database, so we just return the null value.
2487        "pg_tablespace_location" => Scalar {
2488            params!(Oid) => Operation::unary(|_ecx, _e| {
2489                Ok(HirScalarExpr::literal_null(ScalarType::String))
2490            }) => String, 3778;
2491        },
2492        "pg_typeof" => Scalar {
2493            params!(Any) => Operation::new(|ecx, exprs, params, _order_by| {
2494                // pg_typeof reports the type *before* coercion.
2495                let name = match ecx.scalar_type(&exprs[0]) {
2496                    CoercibleScalarType::Uncoerced => "unknown".to_string(),
2497                    CoercibleScalarType::Record(_) => "record".to_string(),
2498                    CoercibleScalarType::Coerced(ty) => ecx.humanize_scalar_type(&ty, true),
2499                };
2500
2501                // For consistency with other functions, verify that
2502                // coercion is possible, though we don't actually care about
2503                // the coerced results.
2504                coerce_args_to_types(ecx, exprs, params)?;
2505
2506                // TODO(benesch): make this function have return type
2507                // regtype, when we support that type. Document the function
2508                // at that point. For now, it's useful enough to have this
2509                // halfway version that returns a string.
2510                Ok(HirScalarExpr::literal(Datum::String(&name), ScalarType::String))
2511            }) => String, 1619;
2512        },
2513        "position" => Scalar {
2514            params!(String, String) => BinaryFunc::Position => Int32, 849;
2515        },
2516        "pow" => Scalar {
2517            params!(Float64, Float64) => Operation::nullary(|_ecx| catalog_name_only!("pow")) => Float64, 1346;
2518        },
2519        "power" => Scalar {
2520            params!(Float64, Float64) => BinaryFunc::Power => Float64, 1368;
2521            params!(Numeric, Numeric) => BinaryFunc::PowerNumeric => Numeric, 2169;
2522        },
2523        "quote_ident" => Scalar {
2524            params!(String) => UnaryFunc::QuoteIdent(func::QuoteIdent) => String, 1282;
2525        },
2526        "radians" => Scalar {
2527            params!(Float64) => UnaryFunc::Radians(func::Radians) => Float64, 1609;
2528        },
2529        "repeat" => Scalar {
2530            params!(String, Int32) => BinaryFunc::RepeatString => String, 1622;
2531        },
2532        "regexp_match" => Scalar {
2533            params!(String, String) => VariadicFunc::RegexpMatch => ScalarType::Array(Box::new(ScalarType::String)), 3396;
2534            params!(String, String, String) => VariadicFunc::RegexpMatch => ScalarType::Array(Box::new(ScalarType::String)), 3397;
2535        },
2536        "replace" => Scalar {
2537            params!(String, String, String) => VariadicFunc::Replace => String, 2087;
2538        },
2539        "right" => Scalar {
2540            params!(String, Int32) => BinaryFunc::Right => String, 3061;
2541        },
2542        "round" => Scalar {
2543            params!(Float32) => UnaryFunc::RoundFloat32(func::RoundFloat32) => Float32, oid::FUNC_ROUND_F32_OID;
2544            params!(Float64) => UnaryFunc::RoundFloat64(func::RoundFloat64) => Float64, 1342;
2545            params!(Numeric) => UnaryFunc::RoundNumeric(func::RoundNumeric) => Numeric, 1708;
2546            params!(Numeric, Int32) => BinaryFunc::RoundNumeric => Numeric, 1707;
2547        },
2548        "rtrim" => Scalar {
2549            params!(String) => UnaryFunc::TrimTrailingWhitespace(func::TrimTrailingWhitespace) => String, 882;
2550            params!(String, String) => BinaryFunc::TrimTrailing => String, 876;
2551        },
2552        "sha224" => Scalar {
2553            params!(Bytes) => digest("sha224") => Bytes, 3419;
2554        },
2555        "sha256" => Scalar {
2556            params!(Bytes) => digest("sha256") => Bytes, 3420;
2557        },
2558        "sha384" => Scalar {
2559            params!(Bytes) => digest("sha384") => Bytes, 3421;
2560        },
2561        "sha512" => Scalar {
2562            params!(Bytes) => digest("sha512") => Bytes, 3422;
2563        },
2564        "sin" => Scalar {
2565            params!(Float64) => UnaryFunc::Sin(func::Sin) => Float64, 1604;
2566        },
2567        "asin" => Scalar {
2568            params!(Float64) => UnaryFunc::Asin(func::Asin) => Float64, 1600;
2569        },
2570        "sinh" => Scalar {
2571            params!(Float64) => UnaryFunc::Sinh(func::Sinh) => Float64, 2462;
2572        },
2573        "asinh" => Scalar {
2574            params!(Float64) => UnaryFunc::Asinh(func::Asinh) => Float64, 2465;
2575        },
2576        "split_part" => Scalar {
2577            params!(String, String, Int32) => VariadicFunc::SplitPart => String, 2088;
2578        },
2579        "stddev" => Scalar {
2580            params!(Float32) => Operation::nullary(|_ecx| catalog_name_only!("stddev")) => Float64, 2157;
2581            params!(Float64) => Operation::nullary(|_ecx| catalog_name_only!("stddev")) => Float64, 2158;
2582            params!(Int16) => Operation::nullary(|_ecx| catalog_name_only!("stddev")) => Numeric, 2156;
2583            params!(Int32) => Operation::nullary(|_ecx| catalog_name_only!("stddev")) => Numeric, 2155;
2584            params!(Int64) => Operation::nullary(|_ecx| catalog_name_only!("stddev")) => Numeric, 2154;
2585            params!(UInt16) => Operation::nullary(|_ecx| catalog_name_only!("stddev")) => Numeric, oid::FUNC_STDDEV_UINT16_OID;
2586            params!(UInt32) => Operation::nullary(|_ecx| catalog_name_only!("stddev")) => Numeric, oid::FUNC_STDDEV_UINT32_OID;
2587            params!(UInt64) => Operation::nullary(|_ecx| catalog_name_only!("stddev")) => Numeric, oid::FUNC_STDDEV_UINT64_OID;
2588        },
2589        "stddev_pop" => Scalar {
2590            params!(Float32) => Operation::nullary(|_ecx| catalog_name_only!("stddev_pop")) => Float64, 2727;
2591            params!(Float64) => Operation::nullary(|_ecx| catalog_name_only!("stddev_pop")) => Float64, 2728;
2592            params!(Int16) => Operation::nullary(|_ecx| catalog_name_only!("stddev_pop")) => Numeric, 2726;
2593            params!(Int32) => Operation::nullary(|_ecx| catalog_name_only!("stddev_pop")) => Numeric, 2725;
2594            params!(Int64) => Operation::nullary(|_ecx| catalog_name_only!("stddev_pop")) => Numeric, 2724;
2595            params!(UInt16) => Operation::nullary(|_ecx| catalog_name_only!("stddev_pop")) => Numeric, oid::FUNC_STDDEV_POP_UINT16_OID;
2596            params!(UInt32) => Operation::nullary(|_ecx| catalog_name_only!("stddev_pop")) => Numeric, oid::FUNC_STDDEV_POP_UINT32_OID;
2597            params!(UInt64) => Operation::nullary(|_ecx| catalog_name_only!("stddev_pop")) => Numeric, oid::FUNC_STDDEV_POP_UINT64_OID;
2598        },
2599        "stddev_samp" => Scalar {
2600            params!(Float32) => Operation::nullary(|_ecx| catalog_name_only!("stddev_samp")) => Float64, 2715;
2601            params!(Float64) => Operation::nullary(|_ecx| catalog_name_only!("stddev_samp")) => Float64, 2716;
2602            params!(Int16) => Operation::nullary(|_ecx| catalog_name_only!("stddev_samp")) => Numeric, 2714;
2603            params!(Int32) => Operation::nullary(|_ecx| catalog_name_only!("stddev_samp")) => Numeric, 2713;
2604            params!(Int64) => Operation::nullary(|_ecx| catalog_name_only!("stddev_samp")) => Numeric, 2712;
2605            params!(UInt16) => Operation::nullary(|_ecx| catalog_name_only!("stddev_samp")) => Numeric, oid::FUNC_STDDEV_SAMP_UINT16_OID;
2606            params!(UInt32) => Operation::nullary(|_ecx| catalog_name_only!("stddev_samp")) => Numeric, oid::FUNC_STDDEV_SAMP_UINT32_OID;
2607            params!(UInt64) => Operation::nullary(|_ecx| catalog_name_only!("stddev_samp")) => Numeric, oid::FUNC_STDDEV_SAMP_UINT64_OID;
2608        },
2609        "substr" => Scalar {
2610            params!(String, Int32) => VariadicFunc::Substr => String, 883;
2611            params!(String, Int32, Int32) => VariadicFunc::Substr => String, 877;
2612        },
2613        "substring" => Scalar {
2614            params!(String, Int32) => VariadicFunc::Substr => String, 937;
2615            params!(String, Int32, Int32) => VariadicFunc::Substr => String, 936;
2616        },
2617        "sqrt" => Scalar {
2618            params!(Float64) => UnaryFunc::SqrtFloat64(func::SqrtFloat64) => Float64, 1344;
2619            params!(Numeric) => UnaryFunc::SqrtNumeric(func::SqrtNumeric) => Numeric, 1730;
2620        },
2621        "tan" => Scalar {
2622            params!(Float64) => UnaryFunc::Tan(func::Tan) => Float64, 1606;
2623        },
2624        "atan" => Scalar {
2625            params!(Float64) => UnaryFunc::Atan(func::Atan) => Float64, 1602;
2626        },
2627        "tanh" => Scalar {
2628            params!(Float64) => UnaryFunc::Tanh(func::Tanh) => Float64, 2464;
2629        },
2630        "atanh" => Scalar {
2631            params!(Float64) => UnaryFunc::Atanh(func::Atanh) => Float64, 2467;
2632        },
2633        "age" => Scalar {
2634            params!(Timestamp, Timestamp) => BinaryFunc::AgeTimestamp => Interval, 2058;
2635            params!(TimestampTz, TimestampTz) => BinaryFunc::AgeTimestampTz => Interval, 1199;
2636        },
2637        "timezone" => Scalar {
2638            params!(String, Timestamp) => BinaryFunc::TimezoneTimestamp => TimestampTz, 2069;
2639            params!(String, TimestampTz) => BinaryFunc::TimezoneTimestampTz => Timestamp, 1159;
2640            // PG defines this as `text timetz`
2641            params!(String, Time) => Operation::binary(|ecx, lhs, rhs| {
2642                // NOTE: this overload is wrong. It should take and return a
2643                // `timetz`, which is a type we don't support because it has
2644                // inscrutable semantics (timezones are meaningless without a
2645                // date). This implementation attempted to extend those already
2646                // inscrutable semantics to the `time` type, which makes matters
2647                // even worse.
2648                //
2649                // This feature flag ensures we don't get *new* uses of this
2650                // function. At some point in the future, we should either
2651                // remove this overload entirely, after validating there are no
2652                // catalogs in production that rely on this overload, or we
2653                // should properly support the `timetz` type and adjust this
2654                // overload accordingly.
2655                ecx.require_feature_flag(&ENABLE_TIME_AT_TIME_ZONE)?;
2656                Ok(HirScalarExpr::call_variadic(
2657                    VariadicFunc::TimezoneTime,
2658                    vec![
2659                        lhs,
2660                        rhs,
2661                        HirScalarExpr::call_unmaterializable(UnmaterializableFunc::CurrentTimestamp),
2662                    ],
2663                ))
2664            }) => Time, 2037;
2665            params!(Interval, Timestamp) => BinaryFunc::TimezoneIntervalTimestamp => TimestampTz, 2070;
2666            params!(Interval, TimestampTz) => BinaryFunc::TimezoneIntervalTimestampTz => Timestamp, 1026;
2667            // PG defines this as `interval timetz`
2668            params!(Interval, Time) => BinaryFunc::TimezoneIntervalTime => Time, 2038;
2669        },
2670        "to_char" => Scalar {
2671            params!(Timestamp, String) => BinaryFunc::ToCharTimestamp => String, 2049;
2672            params!(TimestampTz, String) => BinaryFunc::ToCharTimestampTz => String, 1770;
2673        },
2674        // > Returns the value as json or jsonb. Arrays and composites
2675        // > are converted (recursively) to arrays and objects;
2676        // > otherwise, if there is a cast from the type to json, the
2677        // > cast function will be used to perform the conversion;
2678        // > otherwise, a scalar value is produced. For any scalar type
2679        // > other than a number, a Boolean, or a null value, the text
2680        // > representation will be used, in such a fashion that it is a
2681        // > valid json or jsonb value.
2682        //
2683        // https://www.postgresql.org/docs/current/functions-json.html
2684        "to_jsonb" => Scalar {
2685            params!(Any) => Operation::unary(|ecx, e| {
2686                // TODO(see <materialize#7572>): remove this
2687                let e = match ecx.scalar_type(&e) {
2688                    ScalarType::Char { length } => e.call_unary(UnaryFunc::PadChar(func::PadChar { length })),
2689                    _ => e,
2690                };
2691                Ok(typeconv::to_jsonb(ecx, e))
2692            }) => Jsonb, 3787;
2693        },
2694        "to_timestamp" => Scalar {
2695            params!(Float64) => UnaryFunc::ToTimestamp(func::ToTimestamp) => TimestampTz, 1158;
2696        },
2697        "translate" => Scalar {
2698            params!(String, String, String) => VariadicFunc::Translate => String, 878;
2699        },
2700        "trunc" => Scalar {
2701            params!(Float32) => UnaryFunc::TruncFloat32(func::TruncFloat32) => Float32, oid::FUNC_TRUNC_F32_OID;
2702            params!(Float64) => UnaryFunc::TruncFloat64(func::TruncFloat64) => Float64, 1343;
2703            params!(Numeric) => UnaryFunc::TruncNumeric(func::TruncNumeric) => Numeric, 1710;
2704        },
2705        "tsrange" => Scalar {
2706            params!(Timestamp, Timestamp) => Operation::variadic(|_ecx, mut exprs| {
2707                exprs.push(HirScalarExpr::literal(Datum::String("[)"), ScalarType::String));
2708                Ok(HirScalarExpr::call_variadic(VariadicFunc::RangeCreate { elem_type: ScalarType::Timestamp {precision: None}},
2709                    exprs))
2710            }) =>  ScalarType::Range { element_type: Box::new(ScalarType::Timestamp { precision: None})}, 3933;
2711            params!(Timestamp, Timestamp, String) => Operation::variadic(|_ecx, exprs| {
2712                Ok(HirScalarExpr::call_variadic(VariadicFunc::RangeCreate { elem_type: ScalarType::Timestamp {precision: None}},
2713                    exprs))
2714            }) => ScalarType::Range { element_type: Box::new(ScalarType::Timestamp { precision: None})}, 3934;
2715        },
2716        "tstzrange" => Scalar {
2717            params!(TimestampTz, TimestampTz) => Operation::variadic(|_ecx, mut exprs| {
2718                exprs.push(HirScalarExpr::literal(Datum::String("[)"), ScalarType::String));
2719                Ok(HirScalarExpr::call_variadic(VariadicFunc::RangeCreate { elem_type: ScalarType::TimestampTz {precision: None}},
2720                    exprs,))
2721            }) =>  ScalarType::Range { element_type: Box::new(ScalarType::TimestampTz { precision: None})}, 3937;
2722            params!(TimestampTz, TimestampTz, String) => Operation::variadic(|_ecx, exprs| {
2723                Ok(HirScalarExpr::call_variadic(VariadicFunc::RangeCreate { elem_type: ScalarType::TimestampTz {precision: None}},
2724                    exprs))
2725            }) => ScalarType::Range { element_type: Box::new(ScalarType::TimestampTz { precision: None})}, 3938;
2726        },
2727        "upper" => Scalar {
2728            params!(String) => UnaryFunc::Upper(func::Upper) => String, 871;
2729            params!(RangeAny) => UnaryFunc::RangeUpper(func::RangeUpper) => AnyElement, 3849;
2730        },
2731        "upper_inc" => Scalar {
2732            params!(RangeAny) => UnaryFunc::RangeUpperInc(func::RangeUpperInc) => Bool, 3852;
2733        },
2734        "upper_inf" => Scalar {
2735            params!(RangeAny) => UnaryFunc::RangeUpperInf(func::RangeUpperInf) => Bool, 3854;
2736        },
2737        "uuid_generate_v5" => Scalar {
2738            params!(Uuid, String) => BinaryFunc::UuidGenerateV5 => Uuid, oid::FUNC_PG_UUID_GENERATE_V5;
2739        },
2740        "variance" => Scalar {
2741            params!(Float32) => Operation::nullary(|_ecx| catalog_name_only!("variance")) => Float64, 2151;
2742            params!(Float64) => Operation::nullary(|_ecx| catalog_name_only!("variance")) => Float64, 2152;
2743            params!(Int16) => Operation::nullary(|_ecx| catalog_name_only!("variance")) => Numeric, 2150;
2744            params!(Int32) => Operation::nullary(|_ecx| catalog_name_only!("variance")) => Numeric, 2149;
2745            params!(Int64) => Operation::nullary(|_ecx| catalog_name_only!("variance")) => Numeric, 2148;
2746            params!(UInt16) => Operation::nullary(|_ecx| catalog_name_only!("variance")) => Numeric, oid::FUNC_VARIANCE_UINT16_OID;
2747            params!(UInt32) => Operation::nullary(|_ecx| catalog_name_only!("variance")) => Numeric, oid::FUNC_VARIANCE_UINT32_OID;
2748            params!(UInt64) => Operation::nullary(|_ecx| catalog_name_only!("variance")) => Numeric, oid::FUNC_VARIANCE_UINT64_OID;
2749        },
2750        "var_pop" => Scalar {
2751            params!(Float32) => Operation::nullary(|_ecx| catalog_name_only!("var_pop")) => Float64, 2721;
2752            params!(Float64) => Operation::nullary(|_ecx| catalog_name_only!("var_pop")) => Float64, 2722;
2753            params!(Int16) => Operation::nullary(|_ecx| catalog_name_only!("var_pop")) => Numeric, 2720;
2754            params!(Int32) => Operation::nullary(|_ecx| catalog_name_only!("var_pop")) => Numeric, 2719;
2755            params!(Int64) => Operation::nullary(|_ecx| catalog_name_only!("var_pop")) => Numeric, 2718;
2756            params!(UInt16) => Operation::nullary(|_ecx| catalog_name_only!("var_pop")) => Numeric, oid::FUNC_VAR_POP_UINT16_OID;
2757            params!(UInt32) => Operation::nullary(|_ecx| catalog_name_only!("var_pop")) => Numeric, oid::FUNC_VAR_POP_UINT32_OID;
2758            params!(UInt64) => Operation::nullary(|_ecx| catalog_name_only!("var_pop")) => Numeric, oid::FUNC_VAR_POP_UINT64_OID;
2759        },
2760        "var_samp" => Scalar {
2761            params!(Float32) => Operation::nullary(|_ecx| catalog_name_only!("var_samp")) => Float64, 2644;
2762            params!(Float64) => Operation::nullary(|_ecx| catalog_name_only!("var_samp")) => Float64, 2645;
2763            params!(Int16) => Operation::nullary(|_ecx| catalog_name_only!("var_samp")) => Numeric, 2643;
2764            params!(Int32) => Operation::nullary(|_ecx| catalog_name_only!("var_samp")) => Numeric, 2642;
2765            params!(Int64) => Operation::nullary(|_ecx| catalog_name_only!("var_samp")) => Numeric, 2641;
2766            params!(UInt16) => Operation::nullary(|_ecx| catalog_name_only!("var_samp")) => Numeric, oid::FUNC_VAR_SAMP_UINT16_OID;
2767            params!(UInt32) => Operation::nullary(|_ecx| catalog_name_only!("var_samp")) => Numeric, oid::FUNC_VAR_SAMP_UINT32_OID;
2768            params!(UInt64) => Operation::nullary(|_ecx| catalog_name_only!("var_samp")) => Numeric, oid::FUNC_VAR_SAMP_UINT64_OID;
2769        },
2770        "version" => Scalar {
2771            params!() => UnmaterializableFunc::Version => String, 89;
2772        },
2773
2774        // Internal conversion stubs.
2775        "aclitemin" => Scalar {
2776            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("aclitemin")) => AclItem, 1031;
2777        },
2778        "any_in" => Scalar {
2779            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("any_in")) => Any, 2294;
2780        },
2781        "anyarray_in" => Scalar {
2782            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("anyarray_in")) => ArrayAny, 2296;
2783        },
2784        "anycompatible_in" => Scalar {
2785            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("anycompatible_in")) => AnyCompatible, 5086;
2786        },
2787        "anycompatiblearray_in" => Scalar {
2788            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("anycompatiblearray_in")) => ArrayAnyCompatible, 5088;
2789        },
2790        "anycompatiblenonarray_in" => Scalar {
2791            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("anycompatiblenonarray_in")) => NonVecAnyCompatible, 5092;
2792        },
2793        "anycompatiblerange_in" => Scalar {
2794            params!(String, Oid, Int32) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("anycompatiblerange_in")) => RangeAnyCompatible, 5094;
2795        },
2796        "anyelement_in" => Scalar {
2797            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("anyelement_in")) => AnyElement, 2312;
2798        },
2799        "anynonarray_in" => Scalar {
2800            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("anynonarray_in")) => NonVecAny, 2777;
2801        },
2802        "anyrange_in" => Scalar {
2803            params!(String, Oid, Int32) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("anyrange_in")) => RangeAny, 3832;
2804        },
2805        "array_in" => Scalar {
2806            params!(String, Oid, Int32) =>
2807                Operation::variadic(|_ecx, _exprs| bail_unsupported!("array_in")) => ArrayAny, 750;
2808        },
2809        "boolin" => Scalar {
2810            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("boolin")) => Bool, 1242;
2811        },
2812        "bpcharin" => Scalar {
2813            params!(String, Oid, Int32) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("bpcharin")) => Char, 1044;
2814        },
2815        "byteain" => Scalar {
2816            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("byteain")) => Bytes, 1244;
2817        },
2818        "charin" => Scalar {
2819            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("charin")) => PgLegacyChar, 1245;
2820        },
2821        "date_in" => Scalar {
2822            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("date_in")) => Date, 1084;
2823        },
2824        "float4in" => Scalar {
2825            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("float4in")) => Float32, 200;
2826        },
2827        "float8in" => Scalar {
2828            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("float8in")) => Float64, 214;
2829        },
2830        "int2in" => Scalar {
2831            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("int2in")) => Int16, 38;
2832        },
2833        "int2vectorin" => Scalar {
2834            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("int2vectorin")) => Int2Vector, 40;
2835        },
2836        "int4in" => Scalar {
2837            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("int4in")) => Int32, 42;
2838        },
2839        "int8in" => Scalar {
2840            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("int8in")) => Int64, 460;
2841        },
2842        "internal_in" => Scalar {
2843            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("internal_in")) => Internal, 2304;
2844        },
2845        "interval_in" => Scalar {
2846            params!(String, Oid, Int32) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("interval_in")) => Interval, 1160;
2847        },
2848        "jsonb_in" => Scalar {
2849            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("jsonb_in")) => Jsonb, 3806;
2850        },
2851        "namein" => Scalar {
2852            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("namein")) => PgLegacyName, 34;
2853        },
2854        "numeric_in" => Scalar {
2855            params!(String, Oid, Int32) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("numeric_in")) => Numeric, 1701;
2856        },
2857        "oidin" => Scalar {
2858            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("oidin")) => Oid, 1798;
2859        },
2860        "range_in" => Scalar {
2861            params!(String, Oid, Int32) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("range_in")) => RangeAny, 3834;
2862        },
2863        "record_in" => Scalar {
2864            params!(String, Oid, Int32) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("record_in")) => RecordAny, 2290;
2865        },
2866        "regclassin" => Scalar {
2867            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("regclassin")) => RegClass, 2218;
2868        },
2869        "regprocin" => Scalar {
2870            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("regprocin")) => RegProc, 44;
2871        },
2872        "regtypein" => Scalar {
2873            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("regtypein")) => RegType, 2220;
2874        },
2875        "textin" => Scalar {
2876            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("textin")) => String, 46;
2877        },
2878        "time_in" => Scalar {
2879            params!(String, Oid, Int32) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("time_in")) => Time, 1143;
2880        },
2881        "timestamp_in" => Scalar {
2882            params!(String, Oid, Int32) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("timestamp_in")) => Timestamp, 1312;
2883        },
2884        "timestamptz_in" => Scalar {
2885            params!(String, Oid, Int32) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("timestamptz_in")) => TimestampTz, 1150;
2886        },
2887        "varcharin" => Scalar {
2888            params!(String, Oid, Int32) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("varcharin")) => VarChar, 1046;
2889        },
2890        "uuid_in" => Scalar {
2891            params!(String) => Operation::variadic(|_ecx, _exprs| bail_unsupported!("uuid_in")) => Uuid, 2952;
2892        },
2893        "boolrecv" => Scalar {
2894            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("boolrecv")) => Bool, 2436;
2895        },
2896        "textrecv" => Scalar {
2897            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("textrecv")) => String, 2414;
2898        },
2899        "anyarray_recv" => Scalar {
2900            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("anyarray_recv")) => ArrayAny, 2502;
2901        },
2902        "bytearecv" => Scalar {
2903            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("bytearecv")) => Bytes, 2412;
2904        },
2905        "bpcharrecv" => Scalar {
2906            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("bpcharrecv")) => Char, 2430;
2907        },
2908        "charrecv" => Scalar {
2909            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("charrecv")) => PgLegacyChar, 2434;
2910        },
2911        "date_recv" => Scalar {
2912            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("date_recv")) => Date, 2468;
2913        },
2914        "float4recv" => Scalar {
2915            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("float4recv")) => Float32, 2424;
2916        },
2917        "float8recv" => Scalar {
2918            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("float8recv")) => Float64, 2426;
2919        },
2920        "int4recv" => Scalar {
2921            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("int4recv")) => Int32, 2406;
2922        },
2923        "int8recv" => Scalar {
2924            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("int8recv")) => Int64, 2408;
2925        },
2926        "interval_recv" => Scalar {
2927            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("interval_recv")) => Interval, 2478;
2928        },
2929        "jsonb_recv" => Scalar {
2930            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("jsonb_recv")) => Jsonb, 3805;
2931        },
2932        "namerecv" => Scalar {
2933            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("namerecv")) => PgLegacyName, 2422;
2934        },
2935        "numeric_recv" => Scalar {
2936            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("numeric_recv")) => Numeric, 2460;
2937        },
2938        "oidrecv" => Scalar {
2939            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("oidrecv")) => Oid, 2418;
2940        },
2941        "record_recv" => Scalar {
2942            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("recordrerecord_recvcv")) => RecordAny, 2402;
2943        },
2944        "regclassrecv" => Scalar {
2945            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("regclassrecv")) => RegClass, 2452;
2946        },
2947        "regprocrecv" => Scalar {
2948            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("regprocrecv")) => RegProc, 2444;
2949        },
2950        "regtyperecv" => Scalar {
2951            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("regtyperecv")) => RegType, 2454;
2952        },
2953        "int2recv" => Scalar {
2954            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("int2recv")) => Int16, 2404;
2955        },
2956        "time_recv" => Scalar {
2957            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("time_recv")) => Time, 2470;
2958        },
2959        "timestamp_recv" => Scalar {
2960            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("timestamp_recv")) => Timestamp, 2474;
2961        },
2962        "timestamptz_recv" => Scalar {
2963            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("timestamptz_recv")) => TimestampTz, 2476;
2964        },
2965        "uuid_recv" => Scalar {
2966            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("uuid_recv")) => Uuid, 2961;
2967        },
2968        "varcharrecv" => Scalar {
2969            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("varcharrecv")) => VarChar, 2432;
2970        },
2971        "int2vectorrecv" => Scalar {
2972            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("int2vectorrecv")) => Int2Vector, 2410;
2973        },
2974        "anycompatiblearray_recv" => Scalar {
2975            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("anycompatiblearray_recv")) => ArrayAnyCompatible, 5090;
2976        },
2977        "array_recv" => Scalar {
2978            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("array_recv")) => ArrayAny, 2400;
2979        },
2980        "range_recv" => Scalar {
2981            params!(Internal) => Operation::nullary(|_ecx| catalog_name_only!("range_recv")) => RangeAny, 3836;
2982        },
2983
2984
2985        // Aggregates.
2986        "array_agg" => Aggregate {
2987            params!(NonVecAny) => Operation::unary_ordered(|ecx, e, order_by| {
2988                let elem_type = ecx.scalar_type(&e);
2989
2990                let elem_type = match elem_type.array_of_self_elem_type() {
2991                    Ok(elem_type) => elem_type,
2992                    Err(elem_type) => bail_unsupported!(
2993                        format!("array_agg on {}", ecx.humanize_scalar_type(&elem_type, false))
2994                    ),
2995                };
2996
2997                // ArrayConcat excepts all inputs to be arrays, so wrap all input datums into
2998                // arrays.
2999                let e_arr = HirScalarExpr::call_variadic(
3000                    VariadicFunc::ArrayCreate { elem_type },
3001                    vec![e],
3002                );
3003                Ok((e_arr, AggregateFunc::ArrayConcat { order_by }))
3004            }) => ArrayAny, 2335;
3005            params!(ArrayAny) => Operation::unary(|_ecx, _e| bail_unsupported!("array_agg on arrays")) => ArrayAny, 4053;
3006        },
3007        "bool_and" => Aggregate {
3008            params!(Bool) => Operation::nullary(|_ecx| catalog_name_only!("bool_and")) => Bool, 2517;
3009        },
3010        "bool_or" => Aggregate {
3011            params!(Bool) => Operation::nullary(|_ecx| catalog_name_only!("bool_or")) => Bool, 2518;
3012        },
3013        "count" => Aggregate {
3014            params!() => Operation::nullary(|_ecx| {
3015                // COUNT(*) is equivalent to COUNT(true).
3016                // This is mirrored in `AggregateExpr::is_count_asterisk`, so if you modify this,
3017                // then attend to that code also (in both HIR and MIR).
3018                Ok((HirScalarExpr::literal_true(), AggregateFunc::Count))
3019            }) => Int64, 2803;
3020            params!(Any) => AggregateFunc::Count => Int64, 2147;
3021        },
3022        "max" => Aggregate {
3023            params!(Bool) => AggregateFunc::MaxBool => Bool, oid::FUNC_MAX_BOOL_OID;
3024            params!(Int16) => AggregateFunc::MaxInt16 => Int16, 2117;
3025            params!(Int32) => AggregateFunc::MaxInt32 => Int32, 2116;
3026            params!(Int64) => AggregateFunc::MaxInt64 => Int64, 2115;
3027            params!(UInt16) => AggregateFunc::MaxUInt16 => UInt16, oid::FUNC_MAX_UINT16_OID;
3028            params!(UInt32) => AggregateFunc::MaxUInt32 => UInt32, oid::FUNC_MAX_UINT32_OID;
3029            params!(UInt64) => AggregateFunc::MaxUInt64 => UInt64, oid::FUNC_MAX_UINT64_OID;
3030            params!(MzTimestamp) => AggregateFunc::MaxMzTimestamp => MzTimestamp, oid::FUNC_MAX_MZ_TIMESTAMP_OID;
3031            params!(Float32) => AggregateFunc::MaxFloat32 => Float32, 2119;
3032            params!(Float64) => AggregateFunc::MaxFloat64 => Float64, 2120;
3033            params!(String) => AggregateFunc::MaxString => String, 2129;
3034            // TODO(see <materialize#7572>): make this its own function
3035            params!(Char) => AggregateFunc::MaxString => Char, 2244;
3036            params!(Date) => AggregateFunc::MaxDate => Date, 2122;
3037            params!(Timestamp) => AggregateFunc::MaxTimestamp => Timestamp, 2126;
3038            params!(TimestampTz) => AggregateFunc::MaxTimestampTz => TimestampTz, 2127;
3039            params!(Numeric) => AggregateFunc::MaxNumeric => Numeric, oid::FUNC_MAX_NUMERIC_OID;
3040            params!(Interval) => AggregateFunc::MaxInterval => Interval, 2128;
3041            params!(Time) => AggregateFunc::MaxTime => Time, 2123;
3042        },
3043        "min" => Aggregate {
3044            params!(Bool) => AggregateFunc::MinBool => Bool, oid::FUNC_MIN_BOOL_OID;
3045            params!(Int16) => AggregateFunc::MinInt16 => Int16, 2133;
3046            params!(Int32) => AggregateFunc::MinInt32 => Int32, 2132;
3047            params!(Int64) => AggregateFunc::MinInt64 => Int64, 2131;
3048            params!(UInt16) => AggregateFunc::MinUInt16 => UInt16, oid::FUNC_MIN_UINT16_OID;
3049            params!(UInt32) => AggregateFunc::MinUInt32 => UInt32, oid::FUNC_MIN_UINT32_OID;
3050            params!(UInt64) => AggregateFunc::MinUInt64 => UInt64, oid::FUNC_MIN_UINT64_OID;
3051            params!(MzTimestamp) => AggregateFunc::MinMzTimestamp => MzTimestamp, oid::FUNC_MIN_MZ_TIMESTAMP_OID;
3052            params!(Float32) => AggregateFunc::MinFloat32 => Float32, 2135;
3053            params!(Float64) => AggregateFunc::MinFloat64 => Float64, 2136;
3054            params!(String) => AggregateFunc::MinString => String, 2145;
3055            // TODO(see <materialize#7572>): make this its own function
3056            params!(Char) => AggregateFunc::MinString => Char, 2245;
3057            params!(Date) => AggregateFunc::MinDate => Date, 2138;
3058            params!(Timestamp) => AggregateFunc::MinTimestamp => Timestamp, 2142;
3059            params!(TimestampTz) => AggregateFunc::MinTimestampTz => TimestampTz, 2143;
3060            params!(Numeric) => AggregateFunc::MinNumeric => Numeric, oid::FUNC_MIN_NUMERIC_OID;
3061            params!(Interval) => AggregateFunc::MinInterval => Interval, 2144;
3062            params!(Time) => AggregateFunc::MinTime => Time, 2139;
3063        },
3064        "jsonb_agg" => Aggregate {
3065            params!(Any) => Operation::unary_ordered(|ecx, e, order_by| {
3066                // TODO(see <materialize#7572>): remove this
3067                let e = match ecx.scalar_type(&e) {
3068                    ScalarType::Char { length } => e.call_unary(UnaryFunc::PadChar(func::PadChar { length })),
3069                    _ => e,
3070                };
3071                // `AggregateFunc::JsonbAgg` filters out `Datum::Null` (it
3072                // needs to have *some* identity input), but the semantics
3073                // of the SQL function require that `Datum::Null` is treated
3074                // as `Datum::JsonbNull`. This call to `coalesce` converts
3075                // between the two semantics.
3076                let json_null = HirScalarExpr::literal(Datum::JsonNull, ScalarType::Jsonb);
3077                let e = HirScalarExpr::call_variadic(
3078                    VariadicFunc::Coalesce,
3079                    vec![typeconv::to_jsonb(ecx, e), json_null],
3080                );
3081                Ok((e, AggregateFunc::JsonbAgg { order_by }))
3082            }) => Jsonb, 3267;
3083        },
3084        "jsonb_object_agg" => Aggregate {
3085            params!(Any, Any) => Operation::binary_ordered(|ecx, key, val, order_by| {
3086                // TODO(see <materialize#7572>): remove this
3087                let key = match ecx.scalar_type(&key) {
3088                    ScalarType::Char { length } => key.call_unary(UnaryFunc::PadChar(func::PadChar { length })),
3089                    _ => key,
3090                };
3091                let val = match ecx.scalar_type(&val) {
3092                    ScalarType::Char { length } => val.call_unary(UnaryFunc::PadChar(func::PadChar { length })),
3093                    _ => val,
3094                };
3095
3096                let json_null = HirScalarExpr::literal(Datum::JsonNull, ScalarType::Jsonb);
3097                let key = typeconv::to_string(ecx, key);
3098                // `AggregateFunc::JsonbObjectAgg` uses the same underlying
3099                // implementation as `AggregateFunc::MapAgg`, so it's our
3100                // responsibility to apply the JSON-specific behavior of casting
3101                // SQL nulls to JSON nulls; otherwise the produced `Datum::Map`
3102                // can contain `Datum::Null` values that are not valid for the
3103                // `ScalarType::Jsonb` type.
3104                let val = HirScalarExpr::call_variadic(
3105                    VariadicFunc::Coalesce,
3106                    vec![typeconv::to_jsonb(ecx, val), json_null],
3107                );
3108                let e = HirScalarExpr::call_variadic(
3109                    VariadicFunc::RecordCreate {
3110                        field_names: vec![ColumnName::from("key"), ColumnName::from("val")],
3111                    },
3112                    vec![key, val],
3113                );
3114                Ok((e, AggregateFunc::JsonbObjectAgg { order_by }))
3115            }) => Jsonb, 3270;
3116        },
3117        "string_agg" => Aggregate {
3118            params!(String, String) => Operation::binary_ordered(|_ecx, value, sep, order_by| {
3119                let e = HirScalarExpr::call_variadic(
3120                    VariadicFunc::RecordCreate {
3121                        field_names: vec![ColumnName::from("value"), ColumnName::from("sep")],
3122                    },
3123                    vec![value, sep],
3124                );
3125                Ok((e, AggregateFunc::StringAgg { order_by }))
3126            }) => String, 3538;
3127            params!(Bytes, Bytes) => Operation::binary(|_ecx, _l, _r| bail_unsupported!("string_agg on BYTEA")) => Bytes, 3545;
3128        },
3129        "string_to_array" => Scalar {
3130            params!(String, String) => VariadicFunc::StringToArray => ScalarType::Array(Box::new(ScalarType::String)), 376;
3131            params!(String, String, String) => VariadicFunc::StringToArray => ScalarType::Array(Box::new(ScalarType::String)), 394;
3132        },
3133        "sum" => Aggregate {
3134            params!(Int16) => AggregateFunc::SumInt16 => Int64, 2109;
3135            params!(Int32) => AggregateFunc::SumInt32 => Int64, 2108;
3136            params!(Int64) => AggregateFunc::SumInt64 => Numeric, 2107;
3137            params!(UInt16) => AggregateFunc::SumUInt16 => UInt64, oid::FUNC_SUM_UINT16_OID;
3138            params!(UInt32) => AggregateFunc::SumUInt32 => UInt64, oid::FUNC_SUM_UINT32_OID;
3139            params!(UInt64) => AggregateFunc::SumUInt64 => Numeric, oid::FUNC_SUM_UINT64_OID;
3140            params!(Float32) => AggregateFunc::SumFloat32 => Float32, 2110;
3141            params!(Float64) => AggregateFunc::SumFloat64 => Float64, 2111;
3142            params!(Numeric) => AggregateFunc::SumNumeric => Numeric, 2114;
3143            params!(Interval) => Operation::unary(|_ecx, _e| {
3144                // Explicitly providing this unsupported overload
3145                // prevents `sum(NULL)` from choosing the `Float64`
3146                // implementation, so that we match PostgreSQL's behavior.
3147                // Plus we will one day want to support this overload.
3148                bail_unsupported!("sum(interval)");
3149            }) => Interval, 2113;
3150        },
3151
3152        // Scalar window functions.
3153        "row_number" => ScalarWindow {
3154            params!() => ScalarWindowFunc::RowNumber => Int64, 3100;
3155        },
3156        "rank" => ScalarWindow {
3157            params!() => ScalarWindowFunc::Rank => Int64, 3101;
3158        },
3159        "dense_rank" => ScalarWindow {
3160            params!() => ScalarWindowFunc::DenseRank => Int64, 3102;
3161        },
3162        "lag" => ValueWindow {
3163            // All args are encoded into a single record to be handled later
3164            params!(AnyElement) => Operation::unary(|ecx, e| {
3165                let typ = ecx.scalar_type(&e);
3166                let e = HirScalarExpr::call_variadic(
3167                    VariadicFunc::RecordCreate {
3168                        field_names: vec![ColumnName::from("expr"), ColumnName::from("offset"), ColumnName::from("default")],
3169                    },
3170                    vec![e, HirScalarExpr::literal(Datum::Int32(1), ScalarType::Int32), HirScalarExpr::literal_null(typ)],
3171                );
3172                Ok((e, ValueWindowFunc::Lag))
3173            }) => AnyElement, 3106;
3174            params!(AnyElement, Int32) => Operation::binary(|ecx, e, offset| {
3175                let typ = ecx.scalar_type(&e);
3176                let e = HirScalarExpr::call_variadic(
3177                    VariadicFunc::RecordCreate {
3178                        field_names: vec![ColumnName::from("expr"), ColumnName::from("offset"), ColumnName::from("default")],
3179                    },
3180                    vec![e, offset, HirScalarExpr::literal_null(typ)],
3181                );
3182                Ok((e, ValueWindowFunc::Lag))
3183            }) => AnyElement, 3107;
3184            params!(AnyCompatible, Int32, AnyCompatible) => Operation::variadic(|_ecx, exprs| {
3185                let e = HirScalarExpr::call_variadic(
3186                    VariadicFunc::RecordCreate {
3187                        field_names: vec![ColumnName::from("expr"), ColumnName::from("offset"), ColumnName::from("default")],
3188                    },
3189                    exprs,
3190                );
3191                Ok((e, ValueWindowFunc::Lag))
3192            }) => AnyCompatible, 3108;
3193        },
3194        "lead" => ValueWindow {
3195            // All args are encoded into a single record to be handled later
3196            params!(AnyElement) => Operation::unary(|ecx, e| {
3197                let typ = ecx.scalar_type(&e);
3198                let e = HirScalarExpr::call_variadic(
3199                    VariadicFunc::RecordCreate {
3200                        field_names: vec![ColumnName::from("expr"), ColumnName::from("offset"), ColumnName::from("default")],
3201                    },
3202                    vec![e, HirScalarExpr::literal(Datum::Int32(1), ScalarType::Int32), HirScalarExpr::literal_null(typ)],
3203                );
3204                Ok((e, ValueWindowFunc::Lead))
3205            }) => AnyElement, 3109;
3206            params!(AnyElement, Int32) => Operation::binary(|ecx, e, offset| {
3207                let typ = ecx.scalar_type(&e);
3208                let e = HirScalarExpr::call_variadic(
3209                    VariadicFunc::RecordCreate {
3210                        field_names: vec![ColumnName::from("expr"), ColumnName::from("offset"), ColumnName::from("default")],
3211                    },
3212                    vec![e, offset, HirScalarExpr::literal_null(typ)],
3213                );
3214                Ok((e, ValueWindowFunc::Lead))
3215            }) => AnyElement, 3110;
3216            params!(AnyCompatible, Int32, AnyCompatible) => Operation::variadic(|_ecx, exprs| {
3217                let e = HirScalarExpr::call_variadic(
3218                    VariadicFunc::RecordCreate {
3219                        field_names: vec![ColumnName::from("expr"), ColumnName::from("offset"), ColumnName::from("default")],
3220                    },
3221                    exprs,
3222                );
3223                Ok((e, ValueWindowFunc::Lead))
3224            }) => AnyCompatible, 3111;
3225        },
3226        "first_value" => ValueWindow {
3227            params!(AnyElement) => ValueWindowFunc::FirstValue => AnyElement, 3112;
3228        },
3229        "last_value" => ValueWindow {
3230            params!(AnyElement) => ValueWindowFunc::LastValue => AnyElement, 3113;
3231        },
3232
3233        // Table functions.
3234        "generate_series" => Table {
3235            params!(Int32, Int32, Int32) => Operation::variadic(move |_ecx, exprs| {
3236                Ok(TableFuncPlan {
3237                    expr: HirRelationExpr::CallTable {
3238                        func: TableFunc::GenerateSeriesInt32,
3239                        exprs,
3240                    },
3241                    column_names: vec!["generate_series".into()],
3242                })
3243            }) => ReturnType::set_of(Int32.into()), 1066;
3244            params!(Int32, Int32) => Operation::binary(move |_ecx, start, stop| {
3245                Ok(TableFuncPlan {
3246                    expr: HirRelationExpr::CallTable {
3247                        func: TableFunc::GenerateSeriesInt32,
3248                        exprs: vec![start, stop, HirScalarExpr::literal(Datum::Int32(1), ScalarType::Int32)],
3249                    },
3250                    column_names: vec!["generate_series".into()],
3251                })
3252            }) => ReturnType::set_of(Int32.into()), 1067;
3253            params!(Int64, Int64, Int64) => Operation::variadic(move |_ecx, exprs| {
3254                Ok(TableFuncPlan {
3255                    expr: HirRelationExpr::CallTable {
3256                        func: TableFunc::GenerateSeriesInt64,
3257                        exprs,
3258                    },
3259                    column_names: vec!["generate_series".into()],
3260                })
3261            }) => ReturnType::set_of(Int64.into()), 1068;
3262            params!(Int64, Int64) => Operation::binary(move |_ecx, start, stop| {
3263                Ok(TableFuncPlan {
3264                    expr: HirRelationExpr::CallTable {
3265                        func: TableFunc::GenerateSeriesInt64,
3266                        exprs: vec![start, stop, HirScalarExpr::literal(Datum::Int64(1), ScalarType::Int64)],
3267                    },
3268                    column_names: vec!["generate_series".into()],
3269                })
3270            }) => ReturnType::set_of(Int64.into()), 1069;
3271            params!(Timestamp, Timestamp, Interval) => Operation::variadic(move |_ecx, exprs| {
3272                Ok(TableFuncPlan {
3273                    expr: HirRelationExpr::CallTable {
3274                        func: TableFunc::GenerateSeriesTimestamp,
3275                        exprs,
3276                    },
3277                    column_names: vec!["generate_series".into()],
3278                })
3279            }) => ReturnType::set_of(Timestamp.into()), 938;
3280            params!(TimestampTz, TimestampTz, Interval) => Operation::variadic(move |_ecx, exprs| {
3281                Ok(TableFuncPlan {
3282                    expr: HirRelationExpr::CallTable {
3283                        func: TableFunc::GenerateSeriesTimestampTz,
3284                        exprs,
3285                    },
3286                    column_names: vec!["generate_series".into()],
3287                })
3288            }) => ReturnType::set_of(TimestampTz.into()), 939;
3289        },
3290
3291        "generate_subscripts" => Table {
3292            params!(ArrayAny, Int32) => Operation::variadic(move |_ecx, exprs| {
3293                Ok(TableFuncPlan {
3294                    expr: HirRelationExpr::CallTable {
3295                        func: TableFunc::GenerateSubscriptsArray,
3296                        exprs,
3297                    },
3298                    column_names: vec!["generate_subscripts".into()],
3299                })
3300            }) => ReturnType::set_of(Int32.into()), 1192;
3301        },
3302
3303        "jsonb_array_elements" => Table {
3304            params!(Jsonb) => Operation::unary(move |_ecx, jsonb| {
3305                Ok(TableFuncPlan {
3306                    expr: HirRelationExpr::CallTable {
3307                        func: TableFunc::JsonbArrayElements { stringify: false },
3308                        exprs: vec![jsonb],
3309                    },
3310                    column_names: vec!["value".into()],
3311                })
3312            }) => ReturnType::set_of(Jsonb.into()), 3219;
3313        },
3314        "jsonb_array_elements_text" => Table {
3315            params!(Jsonb) => Operation::unary(move |_ecx, jsonb| {
3316                Ok(TableFuncPlan {
3317                    expr: HirRelationExpr::CallTable {
3318                        func: TableFunc::JsonbArrayElements { stringify: true },
3319                        exprs: vec![jsonb],
3320                    },
3321                    column_names: vec!["value".into()],
3322                })
3323            }) => ReturnType::set_of(String.into()), 3465;
3324        },
3325        "jsonb_each" => Table {
3326            params!(Jsonb) => Operation::unary(move |_ecx, jsonb| {
3327                Ok(TableFuncPlan {
3328                    expr: HirRelationExpr::CallTable {
3329                        func: TableFunc::JsonbEach { stringify: false },
3330                        exprs: vec![jsonb],
3331                    },
3332                    column_names: vec!["key".into(), "value".into()],
3333                })
3334            }) => ReturnType::set_of(RecordAny), 3208;
3335        },
3336        "jsonb_each_text" => Table {
3337            params!(Jsonb) => Operation::unary(move |_ecx, jsonb| {
3338                Ok(TableFuncPlan {
3339                    expr: HirRelationExpr::CallTable {
3340                        func: TableFunc::JsonbEach { stringify: true },
3341                        exprs: vec![jsonb],
3342                    },
3343                    column_names: vec!["key".into(), "value".into()],
3344                })
3345            }) => ReturnType::set_of(RecordAny), 3932;
3346        },
3347        "jsonb_object_keys" => Table {
3348            params!(Jsonb) => Operation::unary(move |_ecx, jsonb| {
3349                Ok(TableFuncPlan {
3350                    expr: HirRelationExpr::CallTable {
3351                        func: TableFunc::JsonbObjectKeys,
3352                        exprs: vec![jsonb],
3353                    },
3354                    column_names: vec!["jsonb_object_keys".into()],
3355                })
3356            }) => ReturnType::set_of(String.into()), 3931;
3357        },
3358        // Note that these implementations' input to `generate_series` is
3359        // contrived to match Flink's expected values. There are other,
3360        // equally valid windows we could generate.
3361        "date_bin_hopping" => Table {
3362            // (hop, width, timestamp)
3363            params!(Interval, Interval, Timestamp) => experimental_sql_impl_table_func(&vars::ENABLE_DATE_BIN_HOPPING, "
3364                    SELECT *
3365                    FROM pg_catalog.generate_series(
3366                        pg_catalog.date_bin($1, $3 + $1, '1970-01-01') - $2, $3, $1
3367                    ) AS dbh(date_bin_hopping)
3368                ") => ReturnType::set_of(Timestamp.into()), oid::FUNC_MZ_DATE_BIN_HOPPING_UNIX_EPOCH_TS_OID;
3369            // (hop, width, timestamp)
3370            params!(Interval, Interval, TimestampTz) => experimental_sql_impl_table_func(&vars::ENABLE_DATE_BIN_HOPPING, "
3371                    SELECT *
3372                    FROM pg_catalog.generate_series(
3373                        pg_catalog.date_bin($1, $3 + $1, '1970-01-01') - $2, $3, $1
3374                    ) AS dbh(date_bin_hopping)
3375                ") => ReturnType::set_of(TimestampTz.into()), oid::FUNC_MZ_DATE_BIN_HOPPING_UNIX_EPOCH_TSTZ_OID;
3376            // (hop, width, timestamp, origin)
3377            params!(Interval, Interval, Timestamp, Timestamp) => experimental_sql_impl_table_func(&vars::ENABLE_DATE_BIN_HOPPING, "
3378                    SELECT *
3379                    FROM pg_catalog.generate_series(
3380                        pg_catalog.date_bin($1, $3 + $1, $4) - $2, $3, $1
3381                    ) AS dbh(date_bin_hopping)
3382                ") => ReturnType::set_of(Timestamp.into()), oid::FUNC_MZ_DATE_BIN_HOPPING_TS_OID;
3383            // (hop, width, timestamp, origin)
3384            params!(Interval, Interval, TimestampTz, TimestampTz) => experimental_sql_impl_table_func(&vars::ENABLE_DATE_BIN_HOPPING, "
3385                    SELECT *
3386                    FROM pg_catalog.generate_series(
3387                        pg_catalog.date_bin($1, $3 + $1, $4) - $2, $3, $1
3388                    ) AS dbh(date_bin_hopping)
3389                ") => ReturnType::set_of(TimestampTz.into()), oid::FUNC_MZ_DATE_BIN_HOPPING_TSTZ_OID;
3390        },
3391        "encode" => Scalar {
3392            params!(Bytes, String) => BinaryFunc::Encode => String, 1946;
3393        },
3394        "decode" => Scalar {
3395            params!(String, String) => BinaryFunc::Decode => Bytes, 1947;
3396        },
3397        "regexp_split_to_array" => Scalar {
3398            params!(String, String) => VariadicFunc::RegexpSplitToArray => ScalarType::Array(Box::new(ScalarType::String)), 2767;
3399            params!(String, String, String) => VariadicFunc::RegexpSplitToArray => ScalarType::Array(Box::new(ScalarType::String)), 2768;
3400        },
3401        "regexp_split_to_table" => Table {
3402            params!(String, String) => sql_impl_table_func("
3403                SELECT unnest(regexp_split_to_array($1, $2))
3404            ") => ReturnType::set_of(String.into()), 2765;
3405            params!(String, String, String) => sql_impl_table_func("
3406                SELECT unnest(regexp_split_to_array($1, $2, $3))
3407            ") => ReturnType::set_of(String.into()), 2766;
3408        },
3409        "regexp_replace" => Scalar {
3410            params!(String, String, String) => VariadicFunc::RegexpReplace => String, 2284;
3411            params!(String, String, String, String) => VariadicFunc::RegexpReplace => String, 2285;
3412            // TODO: PostgreSQL supports additional five and six argument forms of this function which
3413            // allow controlling where to start the replacement and how many replacements to make.
3414        },
3415        "regexp_matches" => Table {
3416            params!(String, String) => Operation::variadic(move |_ecx, exprs| {
3417                let column_names = vec!["regexp_matches".into()];
3418                Ok(TableFuncPlan {
3419                    expr: HirRelationExpr::CallTable {
3420                        func: TableFunc::RegexpMatches,
3421                        exprs: vec![exprs[0].clone(), exprs[1].clone()],
3422                    },
3423                    column_names,
3424                })
3425            }) => ReturnType::set_of(ScalarType::Array(Box::new(ScalarType::String)).into()), 2763;
3426            params!(String, String, String) => Operation::variadic(move |_ecx, exprs| {
3427                let column_names = vec!["regexp_matches".into()];
3428                Ok(TableFuncPlan {
3429                    expr: HirRelationExpr::CallTable {
3430                        func: TableFunc::RegexpMatches,
3431                        exprs: vec![exprs[0].clone(), exprs[1].clone(), exprs[2].clone()],
3432                    },
3433                    column_names,
3434                })
3435            }) => ReturnType::set_of(ScalarType::Array(Box::new(ScalarType::String)).into()), 2764;
3436        },
3437        "reverse" => Scalar {
3438            params!(String) => UnaryFunc::Reverse(func::Reverse) => String, 3062;
3439        }
3440    };
3441
3442    // Add side-effecting functions, which are defined in a separate module
3443    // using a restricted set of function definition features (e.g., no
3444    // overloads) to make them easier to plan.
3445    for sef_builtin in PG_CATALOG_SEF_BUILTINS.values() {
3446        builtins.insert(
3447            sef_builtin.name,
3448            Func::Scalar(vec![FuncImpl {
3449                oid: sef_builtin.oid,
3450                params: ParamList::Exact(
3451                    sef_builtin
3452                        .param_types
3453                        .iter()
3454                        .map(|t| ParamType::from(t.clone()))
3455                        .collect(),
3456                ),
3457                return_type: ReturnType::scalar(ParamType::from(
3458                    sef_builtin.return_type.scalar_type.clone(),
3459                )),
3460                op: Operation::variadic(|_ecx, _e| {
3461                    bail_unsupported!(format!("{} in this position", sef_builtin.name))
3462                }),
3463            }]),
3464        );
3465    }
3466
3467    builtins
3468});
3469
3470pub static INFORMATION_SCHEMA_BUILTINS: LazyLock<BTreeMap<&'static str, Func>> =
3471    LazyLock::new(|| {
3472        use ParamType::*;
3473        builtins! {
3474            "_pg_expandarray" => Table {
3475                // See: https://github.com/postgres/postgres/blob/16e3ad5d143795b05a21dc887c2ab384cce4bcb8/src/backend/catalog/information_schema.sql#L43
3476                params!(ArrayAny) => sql_impl_table_func("
3477                    SELECT
3478                        $1[s] AS x,
3479                        s - pg_catalog.array_lower($1, 1) + 1 AS n
3480                    FROM pg_catalog.generate_series(
3481                        pg_catalog.array_lower($1, 1),
3482                        pg_catalog.array_upper($1, 1),
3483                        1) as g(s)
3484                ") => ReturnType::set_of(RecordAny), oid::FUNC_PG_EXPAND_ARRAY;
3485            }
3486        }
3487    });
3488
3489pub static MZ_CATALOG_BUILTINS: LazyLock<BTreeMap<&'static str, Func>> = LazyLock::new(|| {
3490    use ParamType::*;
3491    use ScalarBaseType::*;
3492    builtins! {
3493        "constant_time_eq" => Scalar {
3494            params!(Bytes, Bytes) => BinaryFunc::ConstantTimeEqBytes => Bool, oid::FUNC_CONSTANT_TIME_EQ_BYTES_OID;
3495            params!(String, String) => BinaryFunc::ConstantTimeEqString => Bool, oid::FUNC_CONSTANT_TIME_EQ_STRING_OID;
3496        },
3497        // Note: this is the original version of the AVG(...) function, as it existed prior to
3498        // v0.66. We updated the internal type promotion used when summing values to increase
3499        // precision, but objects (e.g. materialized views) that already used the AVG(...) function
3500        // could not be changed. So we migrated all existing uses of the AVG(...) function to this
3501        // version.
3502        //
3503        // TODO(parkmycar): When objects no longer depend on this function we can safely delete it.
3504        "avg_internal_v1" => Scalar {
3505            params!(Int64) => Operation::nullary(|_ecx| catalog_name_only!("avg_internal_v1")) => Numeric, oid::FUNC_AVG_INTERNAL_V1_INT64_OID;
3506            params!(Int32) => Operation::nullary(|_ecx| catalog_name_only!("avg_internal_v1")) => Numeric, oid::FUNC_AVG_INTERNAL_V1_INT32_OID;
3507            params!(Int16) => Operation::nullary(|_ecx| catalog_name_only!("avg_internal_v1")) => Numeric, oid::FUNC_AVG_INTERNAL_V1_INT16_OID;
3508            params!(UInt64) => Operation::nullary(|_ecx| catalog_name_only!("avg_internal_v1")) => Numeric, oid::FUNC_AVG_INTERNAL_V1_UINT64_OID;
3509            params!(UInt32) => Operation::nullary(|_ecx| catalog_name_only!("avg_internal_v1")) => Numeric, oid::FUNC_AVG_INTERNAL_V1_UINT32_OID;
3510            params!(UInt16) => Operation::nullary(|_ecx| catalog_name_only!("avg_internal_v1")) => Numeric, oid::FUNC_AVG_INTERNAL_V1_UINT16_OID;
3511            params!(Float32) => Operation::nullary(|_ecx| catalog_name_only!("avg_internal_v1")) => Float64, oid::FUNC_AVG_INTERNAL_V1_FLOAT32_OID;
3512            params!(Float64) => Operation::nullary(|_ecx| catalog_name_only!("avg_internal_v1")) => Float64, oid::FUNC_AVG_INTERNAL_V1_FLOAT64_OID;
3513            params!(Interval) => Operation::nullary(|_ecx| catalog_name_only!("avg_internal_v1")) => Interval, oid::FUNC_AVG_INTERNAL_V1_INTERVAL_OID;
3514        },
3515        "csv_extract" => Table {
3516            params!(Int64, String) => Operation::binary(move |_ecx, ncols, input| {
3517                const MAX_EXTRACT_COLUMNS: i64 = 8192;
3518                const TOO_MANY_EXTRACT_COLUMNS: i64 = MAX_EXTRACT_COLUMNS + 1;
3519
3520                let ncols = match ncols.into_literal_int64() {
3521                    None | Some(i64::MIN..=0) => {
3522                        sql_bail!("csv_extract number of columns must be a positive integer literal");
3523                    },
3524                    Some(ncols @ 1..=MAX_EXTRACT_COLUMNS) => ncols,
3525                    Some(ncols @ TOO_MANY_EXTRACT_COLUMNS..) => {
3526                        return Err(PlanError::TooManyColumns {
3527                            max_num_columns: usize::try_from(MAX_EXTRACT_COLUMNS).unwrap_or(usize::MAX),
3528                            req_num_columns: usize::try_from(ncols).unwrap_or(usize::MAX),
3529                        });
3530                    },
3531                };
3532                let ncols = usize::try_from(ncols).expect("known to be greater than zero");
3533
3534                let column_names = (1..=ncols).map(|i| format!("column{}", i).into()).collect();
3535                Ok(TableFuncPlan {
3536                    expr: HirRelationExpr::CallTable {
3537                        func: TableFunc::CsvExtract(ncols),
3538                        exprs: vec![input],
3539                    },
3540                    column_names,
3541                })
3542            }) => ReturnType::set_of(RecordAny), oid::FUNC_CSV_EXTRACT_OID;
3543        },
3544        "concat_agg" => Aggregate {
3545            params!(Any) => Operation::unary(|_ecx, _e| bail_unsupported!("concat_agg")) => String, oid::FUNC_CONCAT_AGG_OID;
3546        },
3547        "crc32" => Scalar {
3548            params!(String) => UnaryFunc::Crc32String(func::Crc32String) => UInt32, oid::FUNC_CRC32_STRING_OID;
3549            params!(Bytes) => UnaryFunc::Crc32Bytes(func::Crc32Bytes) => UInt32, oid::FUNC_CRC32_BYTES_OID;
3550        },
3551        "datediff" => Scalar {
3552            params!(String, Timestamp, Timestamp) => VariadicFunc::DateDiffTimestamp => Int64, oid::FUNC_DATEDIFF_TIMESTAMP;
3553            params!(String, TimestampTz, TimestampTz) => VariadicFunc::DateDiffTimestampTz => Int64, oid::FUNC_DATEDIFF_TIMESTAMPTZ;
3554            params!(String, Date, Date) => VariadicFunc::DateDiffDate => Int64, oid::FUNC_DATEDIFF_DATE;
3555            params!(String, Time, Time) => VariadicFunc::DateDiffTime => Int64, oid::FUNC_DATEDIFF_TIME;
3556        },
3557        // We can't use the `privilege_fn!` macro because the macro relies on the object having an
3558        // OID, and clusters do not have OIDs.
3559        "has_cluster_privilege" => Scalar {
3560            params!(String, String, String) => sql_impl_func("has_cluster_privilege(mz_internal.mz_role_oid($1), $2, $3)") => Bool, oid::FUNC_HAS_CLUSTER_PRIVILEGE_TEXT_TEXT_TEXT_OID;
3561            params!(Oid, String, String) => sql_impl_func(&format!("
3562                CASE
3563                -- We must first check $2 to avoid a potentially null error message (an error itself).
3564                WHEN $2 IS NULL
3565                THEN NULL
3566                -- Validate the cluster name in order to return a proper error.
3567                WHEN NOT EXISTS (SELECT name FROM mz_clusters WHERE name = $2)
3568                THEN mz_unsafe.mz_error_if_null(NULL::boolean, 'error cluster \"' || $2 || '\" does not exist')
3569                -- Validate the privileges and other arguments.
3570                WHEN NOT mz_internal.mz_validate_privileges($3)
3571                OR $1 IS NULL
3572                OR $3 IS NULL
3573                OR $1 NOT IN (SELECT oid FROM mz_catalog.mz_roles)
3574                THEN NULL
3575                ELSE COALESCE(
3576                    (
3577                        SELECT
3578                            bool_or(
3579                                mz_internal.mz_acl_item_contains_privilege(privilege, $3)
3580                            )
3581                                AS has_cluster_privilege
3582                        FROM
3583                            (
3584                                SELECT
3585                                    unnest(privileges)
3586                                FROM
3587                                    mz_clusters
3588                                WHERE
3589                                    mz_clusters.name = $2
3590                            )
3591                                AS user_privs (privilege)
3592                            LEFT JOIN mz_catalog.mz_roles ON
3593                                    mz_internal.mz_aclitem_grantee(privilege) = mz_roles.id
3594                        WHERE
3595                            mz_internal.mz_aclitem_grantee(privilege) = '{}' OR pg_has_role($1, mz_roles.oid, 'USAGE')
3596                    ),
3597                    false
3598                )
3599                END
3600            ", RoleId::Public)) => Bool, oid::FUNC_HAS_CLUSTER_PRIVILEGE_OID_TEXT_TEXT_OID;
3601            params!(String, String) => sql_impl_func("has_cluster_privilege(current_user, $1, $2)") => Bool, oid::FUNC_HAS_CLUSTER_PRIVILEGE_TEXT_TEXT_OID;
3602        },
3603        "has_connection_privilege" => Scalar {
3604            params!(String, String, String) => sql_impl_func("has_connection_privilege(mz_internal.mz_role_oid($1), mz_internal.mz_connection_oid($2), $3)") => Bool, oid::FUNC_HAS_CONNECTION_PRIVILEGE_TEXT_TEXT_TEXT_OID;
3605            params!(String, Oid, String) => sql_impl_func("has_connection_privilege(mz_internal.mz_role_oid($1), $2, $3)") => Bool, oid::FUNC_HAS_CONNECTION_PRIVILEGE_TEXT_OID_TEXT_OID;
3606            params!(Oid, String, String) => sql_impl_func("has_connection_privilege($1, mz_internal.mz_connection_oid($2), $3)") => Bool, oid::FUNC_HAS_CONNECTION_PRIVILEGE_OID_TEXT_TEXT_OID;
3607            params!(Oid, Oid, String) => sql_impl_func(&privilege_fn!("has_connection_privilege", "mz_connections")) => Bool, oid::FUNC_HAS_CONNECTION_PRIVILEGE_OID_OID_TEXT_OID;
3608            params!(String, String) => sql_impl_func("has_connection_privilege(current_user, $1, $2)") => Bool, oid::FUNC_HAS_CONNECTION_PRIVILEGE_TEXT_TEXT_OID;
3609            params!(Oid, String) => sql_impl_func("has_connection_privilege(current_user, $1, $2)") => Bool, oid::FUNC_HAS_CONNECTION_PRIVILEGE_OID_TEXT_OID;
3610        },
3611        "has_role" => Scalar {
3612            params!(String, String, String) => sql_impl_func("pg_has_role($1, $2, $3)") => Bool, oid::FUNC_HAS_ROLE_TEXT_TEXT_TEXT_OID;
3613            params!(String, Oid, String) => sql_impl_func("pg_has_role($1, $2, $3)") => Bool, oid::FUNC_HAS_ROLE_TEXT_OID_TEXT_OID;
3614            params!(Oid, String, String) => sql_impl_func("pg_has_role($1, $2, $3)") => Bool, oid::FUNC_HAS_ROLE_OID_TEXT_TEXT_OID;
3615            params!(Oid, Oid, String) => sql_impl_func("pg_has_role($1, $2, $3)") => Bool, oid::FUNC_HAS_ROLE_OID_OID_TEXT_OID;
3616            params!(String, String) => sql_impl_func("pg_has_role($1, $2)") => Bool, oid::FUNC_HAS_ROLE_TEXT_TEXT_OID;
3617            params!(Oid, String) => sql_impl_func("pg_has_role($1, $2)") => Bool, oid::FUNC_HAS_ROLE_OID_TEXT_OID;
3618        },
3619        "has_secret_privilege" => Scalar {
3620            params!(String, String, String) => sql_impl_func("has_secret_privilege(mz_internal.mz_role_oid($1), mz_internal.mz_secret_oid($2), $3)") => Bool, oid::FUNC_HAS_SECRET_PRIVILEGE_TEXT_TEXT_TEXT_OID;
3621            params!(String, Oid, String) => sql_impl_func("has_secret_privilege(mz_internal.mz_role_oid($1), $2, $3)") => Bool, oid::FUNC_HAS_SECRET_PRIVILEGE_TEXT_OID_TEXT_OID;
3622            params!(Oid, String, String) => sql_impl_func("has_secret_privilege($1, mz_internal.mz_secret_oid($2), $3)") => Bool, oid::FUNC_HAS_SECRET_PRIVILEGE_OID_TEXT_TEXT_OID;
3623            params!(Oid, Oid, String) => sql_impl_func(&privilege_fn!("has_secret_privilege", "mz_secrets")) => Bool, oid::FUNC_HAS_SECRET_PRIVILEGE_OID_OID_TEXT_OID;
3624            params!(String, String) => sql_impl_func("has_secret_privilege(current_user, $1, $2)") => Bool, oid::FUNC_HAS_SECRET_PRIVILEGE_TEXT_TEXT_OID;
3625            params!(Oid, String) => sql_impl_func("has_secret_privilege(current_user, $1, $2)") => Bool, oid::FUNC_HAS_SECRET_PRIVILEGE_OID_TEXT_OID;
3626        },
3627        "has_system_privilege" => Scalar {
3628            params!(String, String) => sql_impl_func("has_system_privilege(mz_internal.mz_role_oid($1), $2)") => Bool, oid::FUNC_HAS_SYSTEM_PRIVILEGE_TEXT_TEXT_OID;
3629            params!(Oid, String) => sql_impl_func(&format!("
3630                CASE
3631                -- We need to validate the privileges to return a proper error before
3632                -- anything else.
3633                WHEN NOT mz_internal.mz_validate_privileges($2)
3634                OR $1 IS NULL
3635                OR $2 IS NULL
3636                OR $1 NOT IN (SELECT oid FROM mz_catalog.mz_roles)
3637                THEN NULL
3638                ELSE COALESCE(
3639                    (
3640                        SELECT
3641                            bool_or(
3642                                mz_internal.mz_acl_item_contains_privilege(privileges, $2)
3643                            )
3644                                AS has_system_privilege
3645                        FROM mz_catalog.mz_system_privileges
3646                        LEFT JOIN mz_catalog.mz_roles ON
3647                                mz_internal.mz_aclitem_grantee(privileges) = mz_roles.id
3648                        WHERE
3649                            mz_internal.mz_aclitem_grantee(privileges) = '{}' OR pg_has_role($1, mz_roles.oid, 'USAGE')
3650                    ),
3651                    false
3652                )
3653                END
3654            ", RoleId::Public)) => Bool, oid::FUNC_HAS_SYSTEM_PRIVILEGE_OID_TEXT_OID;
3655            params!(String) => sql_impl_func("has_system_privilege(current_user, $1)") => Bool, oid::FUNC_HAS_SYSTEM_PRIVILEGE_TEXT_OID;
3656        },
3657        "has_type_privilege" => Scalar {
3658            params!(String, String, String) => sql_impl_func("has_type_privilege(mz_internal.mz_role_oid($1), $2::regtype::oid, $3)") => Bool, 3138;
3659            params!(String, Oid, String) => sql_impl_func("has_type_privilege(mz_internal.mz_role_oid($1), $2, $3)") => Bool, 3139;
3660            params!(Oid, String, String) => sql_impl_func("has_type_privilege($1, $2::regtype::oid, $3)") => Bool, 3140;
3661            params!(Oid, Oid, String) => sql_impl_func(&privilege_fn!("has_type_privilege", "mz_types")) => Bool, 3141;
3662            params!(String, String) => sql_impl_func("has_type_privilege(current_user, $1, $2)") => Bool, 3142;
3663            params!(Oid, String) => sql_impl_func("has_type_privilege(current_user, $1, $2)") => Bool, 3143;
3664        },
3665        "kafka_murmur2" => Scalar {
3666            params!(String) => UnaryFunc::KafkaMurmur2String(func::KafkaMurmur2String) => Int32, oid::FUNC_KAFKA_MURMUR2_STRING_OID;
3667            params!(Bytes) => UnaryFunc::KafkaMurmur2Bytes(func::KafkaMurmur2Bytes) => Int32, oid::FUNC_KAFKA_MURMUR2_BYTES_OID;
3668        },
3669        "list_agg" => Aggregate {
3670            params!(Any) => Operation::unary_ordered(|ecx, e, order_by| {
3671                if let ScalarType::Char {.. }  = ecx.scalar_type(&e) {
3672                    bail_unsupported!("list_agg on char");
3673                };
3674                // ListConcat excepts all inputs to be lists, so wrap all input datums into
3675                // lists.
3676                let e_arr = HirScalarExpr::call_variadic(
3677                    VariadicFunc::ListCreate { elem_type: ecx.scalar_type(&e) },
3678                    vec![e],
3679                );
3680                Ok((e_arr, AggregateFunc::ListConcat { order_by }))
3681            }) => ListAnyCompatible,  oid::FUNC_LIST_AGG_OID;
3682        },
3683        "list_append" => Scalar {
3684            vec![ListAnyCompatible, ListElementAnyCompatible] => BinaryFunc::ListElementConcat => ListAnyCompatible, oid::FUNC_LIST_APPEND_OID;
3685        },
3686        "list_cat" => Scalar {
3687            vec![ListAnyCompatible, ListAnyCompatible] => BinaryFunc::ListListConcat => ListAnyCompatible, oid::FUNC_LIST_CAT_OID;
3688        },
3689        "list_n_layers" => Scalar {
3690            vec![ListAny] => Operation::unary(|ecx, e| {
3691                ecx.require_feature_flag(&crate::session::vars::ENABLE_LIST_N_LAYERS)?;
3692                let d = ecx.scalar_type(&e).unwrap_list_n_layers();
3693                match i32::try_from(d) {
3694                    Ok(d) => Ok(HirScalarExpr::literal(Datum::Int32(d), ScalarType::Int32)),
3695                    Err(_) => sql_bail!("list has more than {} layers", i32::MAX),
3696                }
3697
3698            }) => Int32, oid::FUNC_LIST_N_LAYERS_OID;
3699        },
3700        "list_length" => Scalar {
3701            vec![ListAny] => UnaryFunc::ListLength(func::ListLength) => Int32, oid::FUNC_LIST_LENGTH_OID;
3702        },
3703        "list_length_max" => Scalar {
3704            vec![ListAny, Plain(ScalarType::Int64)] => Operation::binary(|ecx, lhs, rhs| {
3705                ecx.require_feature_flag(&crate::session::vars::ENABLE_LIST_LENGTH_MAX)?;
3706                let max_layer = ecx.scalar_type(&lhs).unwrap_list_n_layers();
3707                Ok(lhs.call_binary(rhs, BinaryFunc::ListLengthMax { max_layer }))
3708            }) => Int32, oid::FUNC_LIST_LENGTH_MAX_OID;
3709        },
3710        "list_prepend" => Scalar {
3711            vec![ListElementAnyCompatible, ListAnyCompatible] => BinaryFunc::ElementListConcat => ListAnyCompatible, oid::FUNC_LIST_PREPEND_OID;
3712        },
3713        "list_remove" => Scalar {
3714            vec![ListAnyCompatible, ListElementAnyCompatible] => Operation::binary(|ecx, lhs, rhs| {
3715                ecx.require_feature_flag(&crate::session::vars::ENABLE_LIST_REMOVE)?;
3716                Ok(lhs.call_binary(rhs, BinaryFunc::ListRemove))
3717            }) => ListAnyCompatible, oid::FUNC_LIST_REMOVE_OID;
3718        },
3719        "map_agg" => Aggregate {
3720            params!(String, Any) => Operation::binary_ordered(|ecx, key, val, order_by| {
3721                let (value_type, val) = match ecx.scalar_type(&val) {
3722                    // TODO(see <materialize#7572>): remove this
3723                    ScalarType::Char { length } => (ScalarType::Char { length }, val.call_unary(UnaryFunc::PadChar(func::PadChar { length }))),
3724                    typ => (typ, val),
3725                };
3726
3727                let e = HirScalarExpr::call_variadic(
3728                    VariadicFunc::RecordCreate {
3729                        field_names: vec![ColumnName::from("key"), ColumnName::from("val")],
3730                    },
3731                    vec![key, val],
3732                );
3733
3734                Ok((e, AggregateFunc::MapAgg { order_by, value_type }))
3735            }) => MapAny, oid::FUNC_MAP_AGG;
3736        },
3737        "map_build" => Scalar {
3738            // TODO: support a function to construct maps that looks like...
3739            //
3740            // params!([String], Any...) => Operation::variadic(|ecx, exprs| {
3741            //
3742            // ...the challenge here is that we don't support constructing other
3743            // complex types from varidaic functions and instead use a SQL
3744            // keyword; however that doesn't work very well for map because the
3745            // intuitive syntax would be something akin to `MAP[key=>value]`,
3746            // but that doesn't work out of the box because `key=>value` looks
3747            // like an expression.
3748            params!(ListAny) => Operation::unary(|ecx, expr| {
3749                let ty = ecx.scalar_type(&expr);
3750
3751                // This is a fake error but should suffice given how exotic the
3752                // function is.
3753                let err = || {
3754                    Err(sql_err!(
3755                        "function map_build({}) does not exist",
3756                        ecx.humanize_scalar_type(&ty.clone(), false)
3757                    ))
3758                };
3759
3760                // This function only accepts lists of records whose schema is
3761                // (text, T).
3762                let value_type = match &ty {
3763                    ScalarType::List { element_type, .. } => match &**element_type {
3764                        ScalarType::Record { fields, .. } if fields.len() == 2 => {
3765                            if fields[0].1.scalar_type != ScalarType::String {
3766                                return err();
3767                            }
3768
3769                            fields[1].1.scalar_type.clone()
3770                        }
3771                        _ => return err(),
3772                    },
3773                    _ => unreachable!("input guaranteed to be list"),
3774                };
3775
3776                Ok(expr.call_unary(UnaryFunc::MapBuildFromRecordList(
3777                    func::MapBuildFromRecordList { value_type },
3778                )))
3779            }) => MapAny, oid::FUNC_MAP_BUILD;
3780        },
3781        "map_length" => Scalar {
3782            params![MapAny] => UnaryFunc::MapLength(func::MapLength) => Int32, oid::FUNC_MAP_LENGTH_OID;
3783        },
3784        "mz_environment_id" => Scalar {
3785            params!() => UnmaterializableFunc::MzEnvironmentId => String, oid::FUNC_MZ_ENVIRONMENT_ID_OID;
3786        },
3787        "mz_is_superuser" => Scalar {
3788            params!() => UnmaterializableFunc::MzIsSuperuser => ScalarType::Bool, oid::FUNC_MZ_IS_SUPERUSER;
3789        },
3790        "mz_logical_timestamp" => Scalar {
3791            params!() => Operation::nullary(|_ecx| sql_bail!("mz_logical_timestamp() has been renamed to mz_now()")) => MzTimestamp, oid::FUNC_MZ_LOGICAL_TIMESTAMP_OID;
3792        },
3793        "mz_now" => Scalar {
3794            params!() => UnmaterializableFunc::MzNow => MzTimestamp, oid::FUNC_MZ_NOW_OID;
3795        },
3796        "mz_uptime" => Scalar {
3797            params!() => UnmaterializableFunc::MzUptime => Interval, oid::FUNC_MZ_UPTIME_OID;
3798        },
3799        "mz_version" => Scalar {
3800            params!() => UnmaterializableFunc::MzVersion => String, oid::FUNC_MZ_VERSION_OID;
3801        },
3802        "mz_version_num" => Scalar {
3803            params!() => UnmaterializableFunc::MzVersionNum => Int32, oid::FUNC_MZ_VERSION_NUM_OID;
3804        },
3805        "pretty_sql" => Scalar {
3806            params!(String, Int32) => BinaryFunc::PrettySql => String, oid::FUNC_PRETTY_SQL;
3807            params!(String) => Operation::unary(|_ecx, s| {
3808                let width = HirScalarExpr::literal(Datum::Int32(mz_sql_pretty::DEFAULT_WIDTH.try_into().expect("must fit")), ScalarType::Int32);
3809                Ok(s.call_binary(width, BinaryFunc::PrettySql))
3810            }) => String, oid::FUNC_PRETTY_SQL_NOWIDTH;
3811        },
3812        "regexp_extract" => Table {
3813            params!(String, String) => Operation::binary(move |_ecx, regex, haystack| {
3814                let regex = match regex.into_literal_string() {
3815                    None => sql_bail!("regexp_extract requires a string literal as its first argument"),
3816                    Some(regex) => mz_expr::AnalyzedRegex::new(&regex, mz_expr::AnalyzedRegexOpts::default()).map_err(|e| sql_err!("analyzing regex: {}", e))?,
3817                };
3818                let column_names = regex
3819                    .capture_groups_iter()
3820                    .map(|cg| {
3821                        cg.name.clone().unwrap_or_else(|| format!("column{}", cg.index)).into()
3822                    })
3823                    .collect::<Vec<_>>();
3824                if column_names.is_empty(){
3825                    sql_bail!("regexp_extract must specify at least one capture group");
3826                }
3827                Ok(TableFuncPlan {
3828                    expr: HirRelationExpr::CallTable {
3829                        func: TableFunc::RegexpExtract(regex),
3830                        exprs: vec![haystack],
3831                    },
3832                    column_names,
3833                })
3834            }) => ReturnType::set_of(RecordAny), oid::FUNC_REGEXP_EXTRACT_OID;
3835        },
3836        "repeat_row" => Table {
3837            params!(Int64) => Operation::unary(move |ecx, n| {
3838                ecx.require_feature_flag(&crate::session::vars::ENABLE_REPEAT_ROW)?;
3839                Ok(TableFuncPlan {
3840                    expr: HirRelationExpr::CallTable {
3841                        func: TableFunc::Repeat,
3842                        exprs: vec![n],
3843                    },
3844                    column_names: vec![]
3845                })
3846            }) => ReturnType::none(true), oid::FUNC_REPEAT_OID;
3847        },
3848        "seahash" => Scalar {
3849            params!(String) => UnaryFunc::SeahashString(func::SeahashString) => UInt32, oid::FUNC_SEAHASH_STRING_OID;
3850            params!(Bytes) => UnaryFunc::SeahashBytes(func::SeahashBytes) => UInt32, oid::FUNC_SEAHASH_BYTES_OID;
3851        },
3852        "starts_with" => Scalar {
3853            params!(String, String) => BinaryFunc::StartsWith => Bool, 3696;
3854        },
3855        "timezone_offset" => Scalar {
3856            params!(String, TimestampTz) => BinaryFunc::TimezoneOffset => RecordAny, oid::FUNC_TIMEZONE_OFFSET;
3857        },
3858        "try_parse_monotonic_iso8601_timestamp" => Scalar {
3859            params!(String) => Operation::unary(move |_ecx, e| {
3860                Ok(e.call_unary(UnaryFunc::TryParseMonotonicIso8601Timestamp(func::TryParseMonotonicIso8601Timestamp)))
3861            }) => Timestamp, oid::FUNC_TRY_PARSE_MONOTONIC_ISO8601_TIMESTAMP;
3862        },
3863        "unnest" => Table {
3864            vec![ArrayAny] => Operation::unary(move |ecx, e| {
3865                let el_typ = ecx.scalar_type(&e).unwrap_array_element_type().clone();
3866                Ok(TableFuncPlan {
3867                    expr: HirRelationExpr::CallTable {
3868                        func: TableFunc::UnnestArray { el_typ },
3869                        exprs: vec![e],
3870                    },
3871                    column_names: vec!["unnest".into()],
3872                })
3873            }) =>
3874                // This return type should be equivalent to "ArrayElementAny", but this would be its sole use.
3875                ReturnType::set_of(AnyElement), 2331;
3876            vec![ListAny] => Operation::unary(move |ecx, e| {
3877                let el_typ = ecx.scalar_type(&e).unwrap_list_element_type().clone();
3878                Ok(TableFuncPlan {
3879                    expr: HirRelationExpr::CallTable {
3880                        func: TableFunc::UnnestList { el_typ },
3881                        exprs: vec![e],
3882                    },
3883                    column_names: vec!["unnest".into()],
3884                })
3885            }) =>
3886                // This return type should be equivalent to "ListElementAny", but this would be its sole use.
3887                ReturnType::set_of(Any), oid::FUNC_UNNEST_LIST_OID;
3888            vec![MapAny] => Operation::unary(move |ecx, e| {
3889                let value_type = ecx.scalar_type(&e).unwrap_map_value_type().clone();
3890                Ok(TableFuncPlan {
3891                    expr: HirRelationExpr::CallTable {
3892                        func: TableFunc::UnnestMap { value_type },
3893                        exprs: vec![e],
3894                    },
3895                    column_names: vec!["key".into(), "value".into()],
3896                })
3897            }) =>
3898                // This return type should be equivalent to "ListElementAny", but this would be its sole use.
3899                ReturnType::set_of(Any), oid::FUNC_UNNEST_MAP_OID;
3900        }
3901    }
3902});
3903
3904pub static MZ_INTERNAL_BUILTINS: LazyLock<BTreeMap<&'static str, Func>> = LazyLock::new(|| {
3905    use ParamType::*;
3906    use ScalarBaseType::*;
3907    builtins! {
3908        "aclitem_grantor" => Scalar {
3909            params!(AclItem) => UnaryFunc::AclItemGrantor(func::AclItemGrantor) => Oid, oid::FUNC_ACL_ITEM_GRANTOR_OID;
3910        },
3911        "aclitem_grantee" => Scalar {
3912            params!(AclItem) => UnaryFunc::AclItemGrantee(func::AclItemGrantee) => Oid, oid::FUNC_ACL_ITEM_GRANTEE_OID;
3913        },
3914        "aclitem_privileges" => Scalar {
3915            params!(AclItem) => UnaryFunc::AclItemPrivileges(func::AclItemPrivileges) => String, oid::FUNC_ACL_ITEM_PRIVILEGES_OID;
3916        },
3917        "is_rbac_enabled" => Scalar {
3918            params!() => UnmaterializableFunc::IsRbacEnabled => Bool, oid::FUNC_IS_RBAC_ENABLED_OID;
3919        },
3920        "make_mz_aclitem" => Scalar {
3921            params!(String, String, String) => VariadicFunc::MakeMzAclItem => MzAclItem, oid::FUNC_MAKE_MZ_ACL_ITEM_OID;
3922        },
3923        "mz_acl_item_contains_privilege" => Scalar {
3924            params!(MzAclItem, String) => BinaryFunc::MzAclItemContainsPrivilege => Bool, oid::FUNC_MZ_ACL_ITEM_CONTAINS_PRIVILEGE_OID;
3925        },
3926        "mz_aclexplode" => Table {
3927            params!(ScalarType::Array(Box::new(ScalarType::MzAclItem))) =>  Operation::unary(move |_ecx, mz_aclitems| {
3928                Ok(TableFuncPlan {
3929                    expr: HirRelationExpr::CallTable {
3930                        func: TableFunc::MzAclExplode,
3931                        exprs: vec![mz_aclitems],
3932                    },
3933                    column_names: vec!["grantor".into(), "grantee".into(), "privilege_type".into(), "is_grantable".into()],
3934                })
3935            }) => ReturnType::set_of(RecordAny), oid::FUNC_MZ_ACL_ITEM_EXPLODE_OID;
3936        },
3937        "mz_aclitem_grantor" => Scalar {
3938            params!(MzAclItem) => UnaryFunc::MzAclItemGrantor(func::MzAclItemGrantor) => String, oid::FUNC_MZ_ACL_ITEM_GRANTOR_OID;
3939        },
3940        "mz_aclitem_grantee" => Scalar {
3941            params!(MzAclItem) => UnaryFunc::MzAclItemGrantee(func::MzAclItemGrantee) => String, oid::FUNC_MZ_ACL_ITEM_GRANTEE_OID;
3942        },
3943        "mz_aclitem_privileges" => Scalar {
3944            params!(MzAclItem) => UnaryFunc::MzAclItemPrivileges(func::MzAclItemPrivileges) => String, oid::FUNC_MZ_ACL_ITEM_PRIVILEGES_OID;
3945        },
3946        // There is no regclass equivalent for roles to look up connections, so we
3947        // have this helper function instead.
3948        //
3949        // TODO: invent an OID alias for connections
3950        "mz_connection_oid" => Scalar {
3951            params!(String) => sql_impl_func("
3952                CASE
3953                WHEN $1 IS NULL THEN NULL
3954                ELSE (
3955                    mz_unsafe.mz_error_if_null(
3956                        (SELECT oid FROM mz_catalog.mz_objects WHERE name = $1 AND type = 'connection'),
3957                        'connection \"' || $1 || '\" does not exist'
3958                    )
3959                )
3960                END
3961            ") => Oid, oid::FUNC_CONNECTION_OID_OID;
3962        },
3963        "mz_format_privileges" => Scalar {
3964            params!(String) => UnaryFunc::MzFormatPrivileges(func::MzFormatPrivileges) => ScalarType::Array(Box::new(ScalarType::String)), oid::FUNC_MZ_FORMAT_PRIVILEGES_OID;
3965        },
3966        "mz_name_rank" => Table {
3967            // Determines the id, rank of all objects that can be matched using
3968            // the provided args.
3969            params!(
3970                // Database
3971                String,
3972                // Schemas/search path
3973                ParamType::Plain(ScalarType::Array(Box::new(ScalarType::String))),
3974                // Item name
3975                String,
3976                // Get rank among particular OID alias (e.g. regclass)
3977                String
3978            ) =>
3979            // credit for using rank() to @def-
3980            sql_impl_table_func("
3981            -- The best ranked name is the one that belongs to the schema correlated with the lowest
3982            -- index in the search path
3983            SELECT id, name, count, min(schema_pref) OVER () = schema_pref AS best_ranked FROM (
3984                SELECT DISTINCT
3985                    o.id,
3986                    ARRAY[CASE WHEN s.database_id IS NULL THEN NULL ELSE d.name END, s.name, o.name]
3987                    AS name,
3988                    o.count,
3989                    pg_catalog.array_position($2, s.name) AS schema_pref
3990                FROM
3991                    (
3992                        SELECT
3993                            o.id,
3994                            o.schema_id,
3995                            o.name,
3996                            count(*)
3997                        FROM mz_catalog.mz_objects AS o
3998                        JOIN mz_internal.mz_object_oid_alias AS a
3999                            ON o.type = a.object_type
4000                        WHERE o.name = CAST($3 AS pg_catalog.text) AND a.oid_alias = $4
4001                        GROUP BY 1, 2, 3
4002                    )
4003                        AS o
4004                    JOIN mz_catalog.mz_schemas AS s ON o.schema_id = s.id
4005                    JOIN
4006                        unnest($2) AS search_schema (name)
4007                        ON search_schema.name = s.name
4008                    JOIN
4009                        (
4010                            SELECT id, name FROM mz_catalog.mz_databases
4011                            -- If the provided database does not exist, add a row for it so that it
4012                            -- can still join against ambient schemas.
4013                            UNION ALL
4014                            SELECT '', $1 WHERE $1 NOT IN (SELECT name FROM mz_catalog.mz_databases)
4015                        ) AS d
4016                        ON d.id = COALESCE(s.database_id, d.id)
4017                WHERE d.name = CAST($1 AS pg_catalog.text)
4018            );
4019            ") => ReturnType::set_of(RecordAny), oid::FUNC_MZ_NAME_RANK;
4020        },
4021        "mz_resolve_object_name" => Table {
4022            params!(String, String) =>
4023            // Normalize the input name, and for any NULL values (e.g. not database qualified), use
4024            // the defaults used during name resolution.
4025            sql_impl_table_func("
4026                SELECT
4027                    o.id, o.oid, o.schema_id, o.name, o.type, o.owner_id, o.privileges
4028                FROM
4029                    (SELECT mz_internal.mz_normalize_object_name($2))
4030                            AS normalized (n),
4031                    mz_internal.mz_name_rank(
4032                        COALESCE(n[1], pg_catalog.current_database()),
4033                        CASE
4034                            WHEN n[2] IS NULL
4035                                THEN pg_catalog.current_schemas(true)
4036                            ELSE
4037                                ARRAY[n[2]]
4038                        END,
4039                        n[3],
4040                        $1
4041                    ) AS r,
4042                    mz_catalog.mz_objects AS o
4043                WHERE r.id = o.id AND r.best_ranked;
4044            ") => ReturnType::set_of(RecordAny), oid::FUNC_MZ_RESOLVE_OBJECT_NAME;
4045        },
4046        // Returns the an array representing the minimal namespace a user must
4047        // provide to refer to an item whose name is the first argument.
4048        //
4049        // The first argument must be a fully qualified name (i.e. contain
4050        // database.schema.object), with each level of the namespace being an
4051        // element.
4052        //
4053        // The second argument represents the `GlobalId` of the resolved object.
4054        // This is a safeguard to ensure that the name we are resolving refers
4055        // to the expected entry. For example, this helps us disambiguate cases
4056        // where e.g. types and functions have the same name.
4057        "mz_minimal_name_qualification" => Scalar {
4058            params!(ScalarType::Array(Box::new(ScalarType::String)), String) => {
4059                sql_impl_func("(
4060                    SELECT
4061                    CASE
4062                        WHEN $1::pg_catalog.text[] IS NULL
4063                            THEN NULL
4064                    -- If DB doesn't match, requires full qual
4065                        WHEN $1[1] != pg_catalog.current_database()
4066                            THEN $1
4067                    -- If not in currently searchable schema, must be schema qualified
4068                        WHEN NOT $1[2] = ANY(pg_catalog.current_schemas(true))
4069                            THEN ARRAY[$1[2], $1[3]]
4070                    ELSE
4071                        minimal_name
4072                    END
4073                FROM (
4074                    -- Subquery so we return one null row in the cases where
4075                    -- there are no matches.
4076                    SELECT (
4077                        SELECT DISTINCT
4078                            CASE
4079                                -- If there is only one item with this name and it's rank 1,
4080                                -- it is uniquely nameable with just the final element
4081                                WHEN best_ranked AND count = 1
4082                                    THEN ARRAY[r.name[3]]
4083                                -- Otherwise, it is findable in the search path, so does not
4084                                -- need database qualification
4085                                ELSE
4086                                    ARRAY[r.name[2], r.name[3]]
4087                            END AS minimal_name
4088                        FROM mz_catalog.mz_objects AS o
4089                            JOIN mz_internal.mz_object_oid_alias AS a
4090                                ON o.type = a.object_type,
4091                            -- implied lateral to put the OID alias into scope
4092                            mz_internal.mz_name_rank(
4093                                pg_catalog.current_database(),
4094                                pg_catalog.current_schemas(true),
4095                                $1[3],
4096                                a.oid_alias
4097                            ) AS r
4098                        WHERE o.id = $2 AND r.id = $2
4099                    )
4100                )
4101            )")
4102            } => ScalarType::Array(Box::new(ScalarType::String)), oid::FUNC_MZ_MINIMINAL_NAME_QUALIFICATION;
4103        },
4104        "mz_global_id_to_name" => Scalar {
4105            params!(String) => sql_impl_func("
4106            CASE
4107                WHEN $1 IS NULL THEN NULL
4108                ELSE (
4109                    SELECT array_to_string(minimal_name, '.')
4110                    FROM (
4111                        SELECT mz_unsafe.mz_error_if_null(
4112                            (
4113                                -- Return the fully-qualified name
4114                                SELECT DISTINCT ARRAY[qual.d, qual.s, item.name]
4115                                FROM
4116                                    mz_catalog.mz_objects AS item
4117                                JOIN
4118                                (
4119                                    SELECT
4120                                        d.name AS d,
4121                                        s.name AS s,
4122                                        s.id AS schema_id
4123                                    FROM
4124                                        mz_catalog.mz_schemas AS s
4125                                        LEFT JOIN
4126                                            (SELECT id, name FROM mz_catalog.mz_databases)
4127                                            AS d
4128                                            ON s.database_id = d.id
4129                                ) AS qual
4130                                ON qual.schema_id = item.schema_id
4131                                WHERE item.id = CAST($1 AS text)
4132                            ),
4133                            'global ID ' || $1 || ' does not exist'
4134                        )
4135                    ) AS n (fqn),
4136                    LATERAL (
4137                        -- Get the minimal qualification of the fully qualified name
4138                        SELECT mz_internal.mz_minimal_name_qualification(fqn, $1)
4139                    ) AS m (minimal_name)
4140                )
4141                END
4142            ") => String, oid::FUNC_MZ_GLOBAL_ID_TO_NAME;
4143        },
4144        "mz_normalize_object_name" => Scalar {
4145            params!(String) => sql_impl_func("
4146            (
4147                SELECT
4148                    CASE
4149                        WHEN $1 IS NULL THEN NULL
4150                        WHEN pg_catalog.array_length(ident, 1) > 3
4151                            THEN mz_unsafe.mz_error_if_null(
4152                                NULL::pg_catalog.text[],
4153                                'improper relation name (too many dotted names): ' || $1
4154                            )
4155                        ELSE pg_catalog.array_cat(
4156                            pg_catalog.array_fill(
4157                                CAST(NULL AS pg_catalog.text),
4158                                ARRAY[3 - pg_catalog.array_length(ident, 1)]
4159                            ),
4160                            ident
4161                        )
4162                    END
4163                FROM (
4164                    SELECT pg_catalog.parse_ident($1) AS ident
4165                ) AS i
4166            )") => ScalarType::Array(Box::new(ScalarType::String)), oid::FUNC_MZ_NORMALIZE_OBJECT_NAME;
4167        },
4168        "mz_normalize_schema_name" => Scalar {
4169            params!(String) => sql_impl_func("
4170             (
4171                SELECT
4172                    CASE
4173                        WHEN $1 IS NULL THEN NULL
4174                        WHEN pg_catalog.array_length(ident, 1) > 2
4175                            THEN mz_unsafe.mz_error_if_null(
4176                                NULL::pg_catalog.text[],
4177                                'improper schema name (too many dotted names): ' || $1
4178                            )
4179                        ELSE pg_catalog.array_cat(
4180                            pg_catalog.array_fill(
4181                                CAST(NULL AS pg_catalog.text),
4182                                ARRAY[2 - pg_catalog.array_length(ident, 1)]
4183                            ),
4184                            ident
4185                        )
4186                    END
4187                FROM (
4188                    SELECT pg_catalog.parse_ident($1) AS ident
4189                ) AS i
4190            )") => ScalarType::Array(Box::new(ScalarType::String)), oid::FUNC_MZ_NORMALIZE_SCHEMA_NAME;
4191        },
4192        "mz_render_typmod" => Scalar {
4193            params!(Oid, Int32) => BinaryFunc::MzRenderTypmod => String, oid::FUNC_MZ_RENDER_TYPMOD_OID;
4194        },
4195        "mz_role_oid_memberships" => Scalar {
4196            params!() => UnmaterializableFunc::MzRoleOidMemberships => ScalarType::Map{ value_type: Box::new(ScalarType::Array(Box::new(ScalarType::String))), custom_id: None }, oid::FUNC_MZ_ROLE_OID_MEMBERSHIPS;
4197        },
4198        // There is no regclass equivalent for databases to look up oids, so we have this helper function instead.
4199        "mz_database_oid" => Scalar {
4200            params!(String) => sql_impl_func("
4201                CASE
4202                WHEN $1 IS NULL THEN NULL
4203                ELSE (
4204                    mz_unsafe.mz_error_if_null(
4205                        (SELECT oid FROM mz_databases WHERE name = $1),
4206                        'database \"' || $1 || '\" does not exist'
4207                    )
4208                )
4209                END
4210            ") => Oid, oid::FUNC_DATABASE_OID_OID;
4211        },
4212        // There is no regclass equivalent for schemas to look up oids, so we have this helper function instead.
4213        "mz_schema_oid" => Scalar {
4214            params!(String) => sql_impl_func("
4215            CASE
4216                WHEN $1 IS NULL THEN NULL
4217            ELSE
4218                mz_unsafe.mz_error_if_null(
4219                    (
4220                        SELECT
4221                            (
4222                                SELECT s.oid
4223                                FROM mz_catalog.mz_schemas AS s
4224                                LEFT JOIN mz_databases AS d ON s.database_id = d.id
4225                                WHERE
4226                                    (
4227                                        -- Filter to only schemas in the named database or the
4228                                        -- current database if no database was specified.
4229                                        d.name = COALESCE(n[1], pg_catalog.current_database())
4230                                        -- Always include all ambient schemas.
4231                                        OR s.database_id IS NULL
4232                                    )
4233                                    AND s.name = n[2]
4234                            )
4235                        FROM mz_internal.mz_normalize_schema_name($1) AS n
4236                    ),
4237                    'schema \"' || $1 || '\" does not exist'
4238                )
4239            END
4240            ") => Oid, oid::FUNC_SCHEMA_OID_OID;
4241        },
4242        // There is no regclass equivalent for roles to look up oids, so we have this helper function instead.
4243        "mz_role_oid" => Scalar {
4244            params!(String) => sql_impl_func("
4245                CASE
4246                WHEN $1 IS NULL THEN NULL
4247                ELSE (
4248                    mz_unsafe.mz_error_if_null(
4249                        (SELECT oid FROM mz_catalog.mz_roles WHERE name = $1),
4250                        'role \"' || $1 || '\" does not exist'
4251                    )
4252                )
4253                END
4254            ") => Oid, oid::FUNC_ROLE_OID_OID;
4255        },
4256        // There is no regclass equivalent for roles to look up secrets, so we
4257        // have this helper function instead.
4258        //
4259        // TODO: invent an OID alias for secrets
4260        "mz_secret_oid" => Scalar {
4261            params!(String) => sql_impl_func("
4262                CASE
4263                WHEN $1 IS NULL THEN NULL
4264                ELSE (
4265                    mz_unsafe.mz_error_if_null(
4266                        (SELECT oid FROM mz_catalog.mz_objects WHERE name = $1 AND type = 'secret'),
4267                        'secret \"' || $1 || '\" does not exist'
4268                    )
4269                )
4270                END
4271            ") => Oid, oid::FUNC_SECRET_OID_OID;
4272        },
4273        // This ought to be exposed in `mz_catalog`, but its name is rather
4274        // confusing. It does not identify the SQL session, but the
4275        // invocation of this `environmentd` process.
4276        "mz_session_id" => Scalar {
4277            params!() => UnmaterializableFunc::MzSessionId => Uuid, oid::FUNC_MZ_SESSION_ID_OID;
4278        },
4279        "mz_type_name" => Scalar {
4280            params!(Oid) => UnaryFunc::MzTypeName(func::MzTypeName) => String, oid::FUNC_MZ_TYPE_NAME;
4281        },
4282        "mz_validate_privileges" => Scalar {
4283            params!(String) => UnaryFunc::MzValidatePrivileges(func::MzValidatePrivileges) => Bool, oid::FUNC_MZ_VALIDATE_PRIVILEGES_OID;
4284        },
4285        "mz_validate_role_privilege" => Scalar {
4286            params!(String) => UnaryFunc::MzValidateRolePrivilege(func::MzValidateRolePrivilege) => Bool, oid::FUNC_MZ_VALIDATE_ROLE_PRIVILEGE_OID;
4287        }
4288    }
4289});
4290
4291pub static MZ_UNSAFE_BUILTINS: LazyLock<BTreeMap<&'static str, Func>> = LazyLock::new(|| {
4292    use ParamType::*;
4293    use ScalarBaseType::*;
4294    builtins! {
4295        "mz_all" => Aggregate {
4296            params!(Any) => AggregateFunc::All => Bool, oid::FUNC_MZ_ALL_OID;
4297        },
4298        "mz_any" => Aggregate {
4299            params!(Any) => AggregateFunc::Any => Bool, oid::FUNC_MZ_ANY_OID;
4300        },
4301        "mz_avg_promotion_internal_v1" => Scalar {
4302            // Promotes a numeric type to the smallest fractional type that
4303            // can represent it. This is primarily useful for the avg
4304            // aggregate function, so that the avg of an integer column does
4305            // not get truncated to an integer, which would be surprising to
4306            // users (#549).
4307            params!(Float32) => Operation::identity() => Float32, oid::FUNC_MZ_AVG_PROMOTION_F32_OID_INTERNAL_V1;
4308            params!(Float64) => Operation::identity() => Float64, oid::FUNC_MZ_AVG_PROMOTION_F64_OID_INTERNAL_V1;
4309            params!(Int16) => Operation::unary(|ecx, e| {
4310                typeconv::plan_cast(
4311                    ecx, CastContext::Explicit, e, &ScalarType::Numeric {max_scale: None},
4312                )
4313            }) => Numeric, oid::FUNC_MZ_AVG_PROMOTION_I16_OID_INTERNAL_V1;
4314            params!(Int32) => Operation::unary(|ecx, e| {
4315                typeconv::plan_cast(
4316                    ecx, CastContext::Explicit, e, &ScalarType::Numeric {max_scale: None},
4317                )
4318            }) => Numeric, oid::FUNC_MZ_AVG_PROMOTION_I32_OID_INTERNAL_V1;
4319            params!(UInt16) => Operation::unary(|ecx, e| {
4320                typeconv::plan_cast(
4321                    ecx, CastContext::Explicit, e, &ScalarType::Numeric {max_scale: None},
4322                )
4323            }) => Numeric, oid::FUNC_MZ_AVG_PROMOTION_U16_OID_INTERNAL_V1;
4324            params!(UInt32) => Operation::unary(|ecx, e| {
4325                typeconv::plan_cast(
4326                    ecx, CastContext::Explicit, e, &ScalarType::Numeric {max_scale: None},
4327                )
4328            }) => Numeric, oid::FUNC_MZ_AVG_PROMOTION_U32_OID_INTERNAL_V1;
4329        },
4330        "mz_avg_promotion" => Scalar {
4331            // Promotes a numeric type to the smallest fractional type that
4332            // can represent it. This is primarily useful for the avg
4333            // aggregate function, so that the avg of an integer column does
4334            // not get truncated to an integer, which would be surprising to
4335            // users (#549).
4336            params!(Float32) => Operation::identity() => Float32, oid::FUNC_MZ_AVG_PROMOTION_F32_OID;
4337            params!(Float64) => Operation::identity() => Float64, oid::FUNC_MZ_AVG_PROMOTION_F64_OID;
4338            params!(Int16) => Operation::unary(|ecx, e| {
4339                typeconv::plan_cast(
4340                    ecx, CastContext::Explicit, e, &ScalarType::Numeric {max_scale: None},
4341                )
4342            }) => Numeric, oid::FUNC_MZ_AVG_PROMOTION_I16_OID;
4343            params!(Int32) => Operation::unary(|ecx, e| {
4344                typeconv::plan_cast(
4345                    ecx, CastContext::Explicit, e, &ScalarType::Numeric {max_scale: None},
4346                )
4347            }) => Numeric, oid::FUNC_MZ_AVG_PROMOTION_I32_OID;
4348            params!(Int64) => Operation::unary(|ecx, e| {
4349                typeconv::plan_cast(
4350                    ecx, CastContext::Explicit, e, &ScalarType::Numeric {max_scale: None},
4351                )
4352            }) => Numeric, oid::FUNC_MZ_AVG_PROMOTION_I64_OID;
4353            params!(UInt16) => Operation::unary(|ecx, e| {
4354                typeconv::plan_cast(
4355                    ecx, CastContext::Explicit, e, &ScalarType::Numeric {max_scale: None},
4356                )
4357            }) => Numeric, oid::FUNC_MZ_AVG_PROMOTION_U16_OID;
4358            params!(UInt32) => Operation::unary(|ecx, e| {
4359                typeconv::plan_cast(
4360                    ecx, CastContext::Explicit, e, &ScalarType::Numeric {max_scale: None},
4361                )
4362            }) => Numeric, oid::FUNC_MZ_AVG_PROMOTION_U32_OID;
4363            params!(UInt64) => Operation::unary(|ecx, e| {
4364                typeconv::plan_cast(
4365                    ecx, CastContext::Explicit, e, &ScalarType::Numeric {max_scale: None},
4366                )
4367            }) => Numeric, oid::FUNC_MZ_AVG_PROMOTION_U64_OID;
4368            params!(Numeric) => Operation::unary(|ecx, e| {
4369                typeconv::plan_cast(
4370                    ecx, CastContext::Explicit, e, &ScalarType::Numeric {max_scale: None},
4371                )
4372            }) => Numeric, oid::FUNC_MZ_AVG_PROMOTION_NUMERIC_OID;
4373        },
4374        "mz_error_if_null" => Scalar {
4375            // If the first argument is NULL, returns an EvalError::Internal whose error
4376            // message is the second argument.
4377            params!(Any, String) => VariadicFunc::ErrorIfNull => Any, oid::FUNC_MZ_ERROR_IF_NULL_OID;
4378        },
4379        "mz_sleep" => Scalar {
4380            params!(Float64) => UnaryFunc::Sleep(func::Sleep) => TimestampTz, oid::FUNC_MZ_SLEEP_OID;
4381        },
4382        "mz_panic" => Scalar {
4383            params!(String) => UnaryFunc::Panic(func::Panic) => String, oid::FUNC_MZ_PANIC_OID;
4384        }
4385    }
4386});
4387
4388fn digest(algorithm: &'static str) -> Operation<HirScalarExpr> {
4389    Operation::unary(move |_ecx, input| {
4390        let algorithm = HirScalarExpr::literal(Datum::String(algorithm), ScalarType::String);
4391        Ok(input.call_binary(algorithm, BinaryFunc::DigestBytes))
4392    })
4393}
4394
4395fn array_to_string(
4396    ecx: &ExprContext,
4397    exprs: Vec<HirScalarExpr>,
4398) -> Result<HirScalarExpr, PlanError> {
4399    let elem_type = match ecx.scalar_type(&exprs[0]) {
4400        ScalarType::Array(elem_type) => *elem_type,
4401        _ => unreachable!("array_to_string is guaranteed to receive array as first argument"),
4402    };
4403    Ok(HirScalarExpr::call_variadic(
4404        VariadicFunc::ArrayToString { elem_type },
4405        exprs,
4406    ))
4407}
4408
4409/// Correlates an operator with all of its implementations.
4410pub static OP_IMPLS: LazyLock<BTreeMap<&'static str, Func>> = LazyLock::new(|| {
4411    use BinaryFunc::*;
4412    use ParamType::*;
4413    use ScalarBaseType::*;
4414    builtins! {
4415        // Literal OIDs collected from PG 13 using a version of this query
4416        // ```sql
4417        // SELECT
4418        //     oid,
4419        //     oprname,
4420        //     oprleft::regtype,
4421        //     oprright::regtype
4422        // FROM
4423        //     pg_operator
4424        // WHERE
4425        //     oprname IN (
4426        //         '+', '-', '*', '/', '%',
4427        //         '|', '&', '#', '~', '<<', '>>',
4428        //         '~~', '!~~'
4429        //     )
4430        // ORDER BY
4431        //     oprname;
4432        // ```
4433        // Values are also available through
4434        // https://github.com/postgres/postgres/blob/master/src/include/catalog/pg_operator.dat
4435
4436        // ARITHMETIC
4437        "+" => Scalar {
4438            params!(Any) => Operation::new(|ecx, exprs, _params, _order_by| {
4439                // Unary plus has unusual compatibility requirements.
4440                //
4441                // In PostgreSQL, it is only defined for numeric types, so
4442                // `+$1` and `+'1'` get coerced to `Float64` per the usual
4443                // rules, but `+'1'::text` is rejected.
4444                //
4445                // In SQLite, unary plus can be applied to *any* type, and
4446                // is always the identity function.
4447                //
4448                // To try to be compatible with both PostgreSQL and SQlite,
4449                // we accept explicitly-typed arguments of any type, but try
4450                // to coerce unknown-type arguments as `Float64`.
4451                typeconv::plan_coerce(ecx, exprs.into_element(), &ScalarType::Float64)
4452            }) => Any, oid::OP_UNARY_PLUS_OID;
4453            params!(Int16, Int16) => AddInt16 => Int16, 550;
4454            params!(Int32, Int32) => AddInt32 => Int32, 551;
4455            params!(Int64, Int64) => AddInt64 => Int64, 684;
4456            params!(UInt16, UInt16) => AddUInt16 => UInt16, oid::FUNC_ADD_UINT16;
4457            params!(UInt32, UInt32) => AddUInt32 => UInt32, oid::FUNC_ADD_UINT32;
4458            params!(UInt64, UInt64) => AddUInt64 => UInt64, oid::FUNC_ADD_UINT64;
4459            params!(Float32, Float32) => AddFloat32 => Float32, 586;
4460            params!(Float64, Float64) => AddFloat64 => Float64, 591;
4461            params!(Interval, Interval) => AddInterval => Interval, 1337;
4462            params!(Timestamp, Interval) => AddTimestampInterval => Timestamp, 2066;
4463            params!(Interval, Timestamp) => {
4464                Operation::binary(|_ecx, lhs, rhs| Ok(rhs.call_binary(lhs, AddTimestampInterval)))
4465            } => Timestamp, 2553;
4466            params!(TimestampTz, Interval) => AddTimestampTzInterval => TimestampTz, 1327;
4467            params!(Interval, TimestampTz) => {
4468                Operation::binary(|_ecx, lhs, rhs| Ok(rhs.call_binary(lhs, AddTimestampTzInterval)))
4469            } => TimestampTz, 2554;
4470            params!(Date, Interval) => AddDateInterval => Timestamp, 1076;
4471            params!(Interval, Date) => {
4472                Operation::binary(|_ecx, lhs, rhs| Ok(rhs.call_binary(lhs, AddDateInterval)))
4473            } => Timestamp, 2551;
4474            params!(Date, Time) => AddDateTime => Timestamp, 1360;
4475            params!(Time, Date) => {
4476                Operation::binary(|_ecx, lhs, rhs| Ok(rhs.call_binary(lhs, AddDateTime)))
4477            } => Timestamp, 1363;
4478            params!(Time, Interval) => AddTimeInterval => Time, 1800;
4479            params!(Interval, Time) => {
4480                Operation::binary(|_ecx, lhs, rhs| Ok(rhs.call_binary(lhs, AddTimeInterval)))
4481            } => Time, 1849;
4482            params!(Numeric, Numeric) => AddNumeric => Numeric, 1758;
4483            params!(RangeAny, RangeAny) => RangeUnion => RangeAny, 3898;
4484        },
4485        "-" => Scalar {
4486            params!(Int16) => UnaryFunc::NegInt16(func::NegInt16) => Int16, 559;
4487            params!(Int32) => UnaryFunc::NegInt32(func::NegInt32) => Int32, 558;
4488            params!(Int64) => UnaryFunc::NegInt64(func::NegInt64) => Int64, 484;
4489            params!(Float32) => UnaryFunc::NegFloat32(func::NegFloat32) => Float32, 584;
4490            params!(Float64) => UnaryFunc::NegFloat64(func::NegFloat64) => Float64, 585;
4491            params!(Numeric) => UnaryFunc::NegNumeric(func::NegNumeric) => Numeric, 17510;
4492            params!(Interval) => UnaryFunc::NegInterval(func::NegInterval) => Interval, 1336;
4493            params!(Int32, Int32) => SubInt32 => Int32, 555;
4494            params!(Int64, Int64) => SubInt64 => Int64, 685;
4495            params!(UInt16, UInt16) => SubUInt16 => UInt16, oid::FUNC_SUB_UINT16;
4496            params!(UInt32, UInt32) => SubUInt32 => UInt32, oid::FUNC_SUB_UINT32;
4497            params!(UInt64, UInt64) => SubUInt64 => UInt64, oid::FUNC_SUB_UINT64;
4498            params!(Float32, Float32) => SubFloat32 => Float32, 587;
4499            params!(Float64, Float64) => SubFloat64 => Float64, 592;
4500            params!(Numeric, Numeric) => SubNumeric => Numeric, 17590;
4501            params!(Interval, Interval) => SubInterval => Interval, 1338;
4502            params!(Timestamp, Timestamp) => SubTimestamp => Interval, 2067;
4503            params!(TimestampTz, TimestampTz) => SubTimestampTz => Interval, 1328;
4504            params!(Timestamp, Interval) => SubTimestampInterval => Timestamp, 2068;
4505            params!(TimestampTz, Interval) => SubTimestampTzInterval => TimestampTz, 1329;
4506            params!(Date, Date) => SubDate => Int32, 1099;
4507            params!(Date, Interval) => SubDateInterval => Timestamp, 1077;
4508            params!(Time, Time) => SubTime => Interval, 1399;
4509            params!(Time, Interval) => SubTimeInterval => Time, 1801;
4510            params!(Jsonb, Int64) => JsonbDeleteInt64 => Jsonb, 3286;
4511            params!(Jsonb, String) => JsonbDeleteString => Jsonb, 3285;
4512            params!(RangeAny, RangeAny) => RangeDifference => RangeAny, 3899;
4513            // TODO(jamii) there should be corresponding overloads for
4514            // Array(Int64) and Array(String)
4515        },
4516        "*" => Scalar {
4517            params!(Int16, Int16) => MulInt16 => Int16, 526;
4518            params!(Int32, Int32) => MulInt32 => Int32, 514;
4519            params!(Int64, Int64) => MulInt64 => Int64, 686;
4520            params!(UInt16, UInt16) => MulUInt16 => UInt16, oid::FUNC_MUL_UINT16;
4521            params!(UInt32, UInt32) => MulUInt32 => UInt32, oid::FUNC_MUL_UINT32;
4522            params!(UInt64, UInt64) => MulUInt64 => UInt64, oid::FUNC_MUL_UINT64;
4523            params!(Float32, Float32) => MulFloat32 => Float32, 589;
4524            params!(Float64, Float64) => MulFloat64 => Float64, 594;
4525            params!(Interval, Float64) => MulInterval => Interval, 1583;
4526            params!(Float64, Interval) => {
4527                Operation::binary(|_ecx, lhs, rhs| Ok(rhs.call_binary(lhs, MulInterval)))
4528            } => Interval, 1584;
4529            params!(Numeric, Numeric) => MulNumeric => Numeric, 1760;
4530            params!(RangeAny, RangeAny) => RangeIntersection => RangeAny, 3900;
4531        },
4532        "/" => Scalar {
4533            params!(Int16, Int16) => DivInt16 => Int16, 527;
4534            params!(Int32, Int32) => DivInt32 => Int32, 528;
4535            params!(Int64, Int64) => DivInt64 => Int64, 687;
4536            params!(UInt16, UInt16) => DivUInt16 => UInt16, oid::FUNC_DIV_UINT16;
4537            params!(UInt32, UInt32) => DivUInt32 => UInt32, oid::FUNC_DIV_UINT32;
4538            params!(UInt64, UInt64) => DivUInt64 => UInt64, oid::FUNC_DIV_UINT64;
4539            params!(Float32, Float32) => DivFloat32 => Float32, 588;
4540            params!(Float64, Float64) => DivFloat64 => Float64, 593;
4541            params!(Interval, Float64) => DivInterval => Interval, 1585;
4542            params!(Numeric, Numeric) => DivNumeric => Numeric, 1761;
4543        },
4544        "%" => Scalar {
4545            params!(Int16, Int16) => ModInt16 => Int16, 529;
4546            params!(Int32, Int32) => ModInt32 => Int32, 530;
4547            params!(Int64, Int64) => ModInt64 => Int64, 439;
4548            params!(UInt16, UInt16) => ModUInt16 => UInt16, oid::FUNC_MOD_UINT16;
4549            params!(UInt32, UInt32) => ModUInt32 => UInt32, oid::FUNC_MOD_UINT32;
4550            params!(UInt64, UInt64) => ModUInt64 => UInt64, oid::FUNC_MOD_UINT64;
4551            params!(Float32, Float32) => ModFloat32 => Float32, oid::OP_MOD_F32_OID;
4552            params!(Float64, Float64) => ModFloat64 => Float64, oid::OP_MOD_F64_OID;
4553            params!(Numeric, Numeric) => ModNumeric => Numeric, 1762;
4554        },
4555        "&" => Scalar {
4556            params!(Int16, Int16) => BitAndInt16 => Int16, 1874;
4557            params!(Int32, Int32) => BitAndInt32 => Int32, 1880;
4558            params!(Int64, Int64) => BitAndInt64 => Int64, 1886;
4559            params!(UInt16, UInt16) => BitAndUInt16 => UInt16, oid::FUNC_AND_UINT16;
4560            params!(UInt32, UInt32) => BitAndUInt32 => UInt32, oid::FUNC_AND_UINT32;
4561            params!(UInt64, UInt64) => BitAndUInt64 => UInt64, oid::FUNC_AND_UINT64;
4562        },
4563        "|" => Scalar {
4564            params!(Int16, Int16) => BitOrInt16 => Int16, 1875;
4565            params!(Int32, Int32) => BitOrInt32 => Int32, 1881;
4566            params!(Int64, Int64) => BitOrInt64 => Int64, 1887;
4567            params!(UInt16, UInt16) => BitOrUInt16 => UInt16, oid::FUNC_OR_UINT16;
4568            params!(UInt32, UInt32) => BitOrUInt32 => UInt32, oid::FUNC_OR_UINT32;
4569            params!(UInt64, UInt64) => BitOrUInt64 => UInt64, oid::FUNC_OR_UINT64;
4570        },
4571        "#" => Scalar {
4572            params!(Int16, Int16) => BitXorInt16 => Int16, 1876;
4573            params!(Int32, Int32) => BitXorInt32 => Int32, 1882;
4574            params!(Int64, Int64) => BitXorInt64 => Int64, 1888;
4575            params!(UInt16, UInt16) => BitXorUInt16 => UInt16, oid::FUNC_XOR_UINT16;
4576            params!(UInt32, UInt32) => BitXorUInt32 => UInt32, oid::FUNC_XOR_UINT32;
4577            params!(UInt64, UInt64) => BitXorUInt64 => UInt64, oid::FUNC_XOR_UINT64;
4578        },
4579        "<<" => Scalar {
4580            params!(Int16, Int32) => BitShiftLeftInt16 => Int16, 1878;
4581            params!(Int32, Int32) => BitShiftLeftInt32 => Int32, 1884;
4582            params!(Int64, Int32) => BitShiftLeftInt64 => Int64, 1890;
4583            params!(UInt16, UInt32) => BitShiftLeftUInt16 => UInt16, oid::FUNC_SHIFT_LEFT_UINT16;
4584            params!(UInt32, UInt32) => BitShiftLeftUInt32 => UInt32, oid::FUNC_SHIFT_LEFT_UINT32;
4585            params!(UInt64, UInt32) => BitShiftLeftUInt64 => UInt64, oid::FUNC_SHIFT_LEFT_UINT64;
4586            params!(RangeAny, RangeAny) => RangeBefore => Bool, 3893;
4587        },
4588        ">>" => Scalar {
4589            params!(Int16, Int32) => BitShiftRightInt16 => Int16, 1879;
4590            params!(Int32, Int32) => BitShiftRightInt32 => Int32, 1885;
4591            params!(Int64, Int32) => BitShiftRightInt64 => Int64, 1891;
4592            params!(UInt16, UInt32) => BitShiftRightUInt16 => UInt16, oid::FUNC_SHIFT_RIGHT_UINT16;
4593            params!(UInt32, UInt32) => BitShiftRightUInt32 => UInt32, oid::FUNC_SHIFT_RIGHT_UINT32;
4594            params!(UInt64, UInt32) => BitShiftRightUInt64 => UInt64, oid::FUNC_SHIFT_RIGHT_UINT64;
4595            params!(RangeAny, RangeAny) => RangeAfter => Bool, 3894;
4596        },
4597
4598        // ILIKE
4599        "~~*" => Scalar {
4600            params!(String, String) => IsLikeMatch { case_insensitive: true } => Bool, 1627;
4601            params!(Char, String) => Operation::binary(|ecx, lhs, rhs| {
4602                let length = ecx.scalar_type(&lhs).unwrap_char_length();
4603                Ok(lhs.call_unary(UnaryFunc::PadChar(func::PadChar { length }))
4604                    .call_binary(rhs, IsLikeMatch { case_insensitive: true })
4605                )
4606            }) => Bool, 1629;
4607        },
4608        "!~~*" => Scalar {
4609            params!(String, String) => Operation::binary(|_ecx, lhs, rhs| {
4610                Ok(lhs
4611                    .call_binary(rhs, IsLikeMatch { case_insensitive: true })
4612                    .call_unary(UnaryFunc::Not(func::Not)))
4613            }) => Bool, 1628;
4614            params!(Char, String) => Operation::binary(|ecx, lhs, rhs| {
4615                let length = ecx.scalar_type(&lhs).unwrap_char_length();
4616                Ok(lhs.call_unary(UnaryFunc::PadChar(func::PadChar { length }))
4617                    .call_binary(rhs, IsLikeMatch { case_insensitive: true })
4618                    .call_unary(UnaryFunc::Not(func::Not))
4619                )
4620            }) => Bool, 1630;
4621        },
4622
4623
4624        // LIKE
4625        "~~" => Scalar {
4626            params!(String, String) => IsLikeMatch { case_insensitive: false } => Bool, 1209;
4627            params!(Char, String) => Operation::binary(|ecx, lhs, rhs| {
4628                let length = ecx.scalar_type(&lhs).unwrap_char_length();
4629                Ok(lhs.call_unary(UnaryFunc::PadChar(func::PadChar { length }))
4630                    .call_binary(rhs, IsLikeMatch { case_insensitive: false })
4631                )
4632            }) => Bool, 1211;
4633        },
4634        "!~~" => Scalar {
4635            params!(String, String) => Operation::binary(|_ecx, lhs, rhs| {
4636                Ok(lhs
4637                    .call_binary(rhs, IsLikeMatch { case_insensitive: false })
4638                    .call_unary(UnaryFunc::Not(func::Not)))
4639            }) => Bool, 1210;
4640            params!(Char, String) => Operation::binary(|ecx, lhs, rhs| {
4641                let length = ecx.scalar_type(&lhs).unwrap_char_length();
4642                Ok(lhs.call_unary(UnaryFunc::PadChar(func::PadChar { length }))
4643                    .call_binary(rhs, IsLikeMatch { case_insensitive: false })
4644                    .call_unary(UnaryFunc::Not(func::Not))
4645                )
4646            }) => Bool, 1212;
4647        },
4648
4649        // REGEX
4650        "~" => Scalar {
4651            params!(Int16) => UnaryFunc::BitNotInt16(func::BitNotInt16) => Int16, 1877;
4652            params!(Int32) => UnaryFunc::BitNotInt32(func::BitNotInt32) => Int32, 1883;
4653            params!(Int64) => UnaryFunc::BitNotInt64(func::BitNotInt64) => Int64, 1889;
4654            params!(UInt16) => UnaryFunc::BitNotUint16(func::BitNotUint16) => UInt16, oid::FUNC_BIT_NOT_UINT16_OID;
4655            params!(UInt32) => UnaryFunc::BitNotUint32(func::BitNotUint32) => UInt32, oid::FUNC_BIT_NOT_UINT32_OID;
4656            params!(UInt64) => UnaryFunc::BitNotUint64(func::BitNotUint64) => UInt64, oid::FUNC_BIT_NOT_UINT64_OID;
4657            params!(String, String) => IsRegexpMatch { case_insensitive: false } => Bool, 641;
4658            params!(Char, String) => Operation::binary(|ecx, lhs, rhs| {
4659                let length = ecx.scalar_type(&lhs).unwrap_char_length();
4660                Ok(lhs.call_unary(UnaryFunc::PadChar(func::PadChar { length }))
4661                    .call_binary(rhs, IsRegexpMatch { case_insensitive: false })
4662                )
4663            }) => Bool, 1055;
4664        },
4665        "~*" => Scalar {
4666            params!(String, String) => Operation::binary(|_ecx, lhs, rhs| {
4667                Ok(lhs.call_binary(rhs, IsRegexpMatch { case_insensitive: true }))
4668            }) => Bool, 1228;
4669            params!(Char, String) => Operation::binary(|ecx, lhs, rhs| {
4670                let length = ecx.scalar_type(&lhs).unwrap_char_length();
4671                Ok(lhs.call_unary(UnaryFunc::PadChar(func::PadChar { length }))
4672                    .call_binary(rhs, IsRegexpMatch { case_insensitive: true })
4673                )
4674            }) => Bool, 1234;
4675        },
4676        "!~" => Scalar {
4677            params!(String, String) => Operation::binary(|_ecx, lhs, rhs| {
4678                Ok(lhs
4679                    .call_binary(rhs, IsRegexpMatch { case_insensitive: false })
4680                    .call_unary(UnaryFunc::Not(func::Not)))
4681            }) => Bool, 642;
4682            params!(Char, String) => Operation::binary(|ecx, lhs, rhs| {
4683                let length = ecx.scalar_type(&lhs).unwrap_char_length();
4684                Ok(lhs.call_unary(UnaryFunc::PadChar(func::PadChar { length }))
4685                    .call_binary(rhs, IsRegexpMatch { case_insensitive: false })
4686                    .call_unary(UnaryFunc::Not(func::Not))
4687                )
4688            }) => Bool, 1056;
4689        },
4690        "!~*" => Scalar {
4691            params!(String, String) => Operation::binary(|_ecx, lhs, rhs| {
4692                Ok(lhs
4693                    .call_binary(rhs, IsRegexpMatch { case_insensitive: true })
4694                    .call_unary(UnaryFunc::Not(func::Not)))
4695            }) => Bool, 1229;
4696            params!(Char, String) => Operation::binary(|ecx, lhs, rhs| {
4697                let length = ecx.scalar_type(&lhs).unwrap_char_length();
4698                Ok(lhs.call_unary(UnaryFunc::PadChar(func::PadChar { length }))
4699                    .call_binary(rhs, IsRegexpMatch { case_insensitive: true })
4700                    .call_unary(UnaryFunc::Not(func::Not))
4701                )
4702            }) => Bool, 1235;
4703        },
4704
4705        // CONCAT
4706        "||" => Scalar {
4707            params!(String, NonVecAny) => Operation::binary(|ecx, lhs, rhs| {
4708                let rhs = typeconv::plan_cast(
4709                    ecx,
4710                    CastContext::Explicit,
4711                    rhs,
4712                    &ScalarType::String,
4713                )?;
4714                Ok(lhs.call_binary(rhs, TextConcat))
4715            }) => String, 2779;
4716            params!(NonVecAny, String) => Operation::binary(|ecx, lhs, rhs| {
4717                let lhs = typeconv::plan_cast(
4718                    ecx,
4719                    CastContext::Explicit,
4720                    lhs,
4721                    &ScalarType::String,
4722                )?;
4723                Ok(lhs.call_binary(rhs, TextConcat))
4724            }) => String, 2780;
4725            params!(String, String) => TextConcat => String, 654;
4726            params!(Jsonb, Jsonb) => JsonbConcat => Jsonb, 3284;
4727            params!(ArrayAnyCompatible, ArrayAnyCompatible) => ArrayArrayConcat => ArrayAnyCompatible, 375;
4728            params!(ListAnyCompatible, ListAnyCompatible) => ListListConcat => ListAnyCompatible, oid::OP_CONCAT_LIST_LIST_OID;
4729            params!(ListAnyCompatible, ListElementAnyCompatible) => ListElementConcat => ListAnyCompatible, oid::OP_CONCAT_LIST_ELEMENT_OID;
4730            params!(ListElementAnyCompatible, ListAnyCompatible) => ElementListConcat => ListAnyCompatible, oid::OP_CONCAT_ELEMENY_LIST_OID;
4731        },
4732
4733        // JSON, MAP, RANGE, LIST, ARRAY
4734        "->" => Scalar {
4735            params!(Jsonb, Int64) => JsonbGetInt64 => Jsonb, 3212;
4736            params!(Jsonb, String) => JsonbGetString => Jsonb, 3211;
4737            params!(MapAny, String) => MapGetValue => Any, oid::OP_GET_VALUE_MAP_OID;
4738        },
4739        "->>" => Scalar {
4740            params!(Jsonb, Int64) => JsonbGetInt64Stringify => String, 3481;
4741            params!(Jsonb, String) => JsonbGetStringStringify => String, 3477;
4742        },
4743        "#>" => Scalar {
4744            params!(Jsonb, ScalarType::Array(Box::new(ScalarType::String))) => JsonbGetPath => Jsonb, 3213;
4745        },
4746        "#>>" => Scalar {
4747            params!(Jsonb, ScalarType::Array(Box::new(ScalarType::String))) => JsonbGetPathStringify => String, 3206;
4748        },
4749        "@>" => Scalar {
4750            params!(Jsonb, Jsonb) => JsonbContainsJsonb => Bool, 3246;
4751            params!(Jsonb, String) => Operation::binary(|_ecx, lhs, rhs| {
4752                Ok(lhs.call_binary(
4753                    rhs.call_unary(UnaryFunc::CastStringToJsonb(func::CastStringToJsonb)),
4754                    JsonbContainsJsonb,
4755                ))
4756            }) => Bool, oid::OP_CONTAINS_JSONB_STRING_OID;
4757            params!(String, Jsonb) => Operation::binary(|_ecx, lhs, rhs| {
4758                Ok(lhs.call_unary(UnaryFunc::CastStringToJsonb(func::CastStringToJsonb))
4759                      .call_binary(rhs, JsonbContainsJsonb))
4760            }) => Bool, oid::OP_CONTAINS_STRING_JSONB_OID;
4761            params!(MapAnyCompatible, MapAnyCompatible) => MapContainsMap => Bool, oid::OP_CONTAINS_MAP_MAP_OID;
4762            params!(RangeAny, AnyElement) => Operation::binary(|ecx, lhs, rhs| {
4763                let elem_type = ecx.scalar_type(&lhs).unwrap_range_element_type().clone();
4764                Ok(lhs.call_binary(rhs, BinaryFunc::RangeContainsElem { elem_type, rev: false }))
4765            }) => Bool, 3889;
4766            params!(RangeAny, RangeAny) => Operation::binary(|_ecx, lhs, rhs| {
4767                Ok(lhs.call_binary(rhs, BinaryFunc::RangeContainsRange { rev: false }))
4768            }) => Bool, 3890;
4769            params!(ArrayAny, ArrayAny) => Operation::binary(|_ecx, lhs, rhs| {
4770                Ok(lhs.call_binary(rhs, BinaryFunc::ArrayContainsArray { rev: false }))
4771            }) => Bool, 2751;
4772            params!(ListAny, ListAny) => Operation::binary(|_ecx, lhs, rhs| {
4773                Ok(lhs.call_binary(rhs, BinaryFunc::ListContainsList { rev: false }))
4774            }) => Bool, oid::OP_CONTAINS_LIST_LIST_OID;
4775        },
4776        "<@" => Scalar {
4777            params!(Jsonb, Jsonb) => Operation::binary(|_ecx, lhs, rhs| {
4778                Ok(rhs.call_binary(
4779                    lhs,
4780                    JsonbContainsJsonb
4781                ))
4782            }) => Bool, 3250;
4783            params!(Jsonb, String) => Operation::binary(|_ecx, lhs, rhs| {
4784                Ok(rhs.call_unary(UnaryFunc::CastStringToJsonb(func::CastStringToJsonb))
4785                      .call_binary(lhs, BinaryFunc::JsonbContainsJsonb))
4786            }) => Bool, oid::OP_CONTAINED_JSONB_STRING_OID;
4787            params!(String, Jsonb) => Operation::binary(|_ecx, lhs, rhs| {
4788                Ok(rhs.call_binary(
4789                    lhs.call_unary(UnaryFunc::CastStringToJsonb(func::CastStringToJsonb)),
4790                    BinaryFunc::JsonbContainsJsonb,
4791                ))
4792            }) => Bool, oid::OP_CONTAINED_STRING_JSONB_OID;
4793            params!(MapAnyCompatible, MapAnyCompatible) => Operation::binary(|_ecx, lhs, rhs| {
4794                Ok(rhs.call_binary(lhs, MapContainsMap))
4795            }) => Bool, oid::OP_CONTAINED_MAP_MAP_OID;
4796            params!(AnyElement, RangeAny) => Operation::binary(|ecx, lhs, rhs| {
4797                let elem_type = ecx.scalar_type(&rhs).unwrap_range_element_type().clone();
4798                Ok(rhs.call_binary(lhs, BinaryFunc::RangeContainsElem { elem_type, rev: true }))
4799            }) => Bool, 3891;
4800            params!(RangeAny, RangeAny) => Operation::binary(|_ecx, lhs, rhs| {
4801                Ok(rhs.call_binary(lhs, BinaryFunc::RangeContainsRange { rev: true }))
4802            }) => Bool, 3892;
4803            params!(ArrayAny, ArrayAny) => Operation::binary(|_ecx, lhs, rhs| {
4804                Ok(lhs.call_binary(rhs, BinaryFunc::ArrayContainsArray { rev: true }))
4805            }) => Bool, 2752;
4806            params!(ListAny, ListAny) => Operation::binary(|_ecx, lhs, rhs| {
4807                Ok(lhs.call_binary(rhs, BinaryFunc::ListContainsList { rev: true }))
4808            }) => Bool, oid::OP_IS_CONTAINED_LIST_LIST_OID;
4809        },
4810        "?" => Scalar {
4811            params!(Jsonb, String) => JsonbContainsString => Bool, 3247;
4812            params!(MapAny, String) => MapContainsKey => Bool, oid::OP_CONTAINS_KEY_MAP_OID;
4813        },
4814        "?&" => Scalar {
4815            params!(MapAny, ScalarType::Array(Box::new(ScalarType::String))) => MapContainsAllKeys => Bool, oid::OP_CONTAINS_ALL_KEYS_MAP_OID;
4816        },
4817        "?|" => Scalar {
4818            params!(MapAny, ScalarType::Array(Box::new(ScalarType::String))) => MapContainsAnyKeys => Bool, oid::OP_CONTAINS_ANY_KEYS_MAP_OID;
4819        },
4820        "&&" => Scalar {
4821            params!(RangeAny, RangeAny) => BinaryFunc::RangeOverlaps => Bool, 3888;
4822        },
4823        "&<" => Scalar {
4824            params!(RangeAny, RangeAny) => BinaryFunc::RangeOverleft => Bool, 3895;
4825        },
4826        "&>" => Scalar {
4827            params!(RangeAny, RangeAny) => BinaryFunc::RangeOverright => Bool, 3896;
4828        },
4829        "-|-" => Scalar {
4830            params!(RangeAny, RangeAny) => BinaryFunc::RangeAdjacent => Bool, 3897;
4831        },
4832
4833        // COMPARISON OPS
4834        "<" => Scalar {
4835            params!(Numeric, Numeric) => BinaryFunc::Lt => Bool, 1754;
4836            params!(Bool, Bool) => BinaryFunc::Lt => Bool, 58;
4837            params!(Int16, Int16) => BinaryFunc::Lt => Bool, 95;
4838            params!(Int32, Int32) => BinaryFunc::Lt => Bool, 97;
4839            params!(Int64, Int64) => BinaryFunc::Lt => Bool, 412;
4840            params!(UInt16, UInt16) => BinaryFunc::Lt => Bool, oid::FUNC_LT_UINT16_OID;
4841            params!(UInt32, UInt32) => BinaryFunc::Lt => Bool, oid::FUNC_LT_UINT32_OID;
4842            params!(UInt64, UInt64) => BinaryFunc::Lt => Bool, oid::FUNC_LT_UINT64_OID;
4843            params!(Float32, Float32) => BinaryFunc::Lt => Bool, 622;
4844            params!(Float64, Float64) => BinaryFunc::Lt => Bool, 672;
4845            params!(Oid, Oid) => BinaryFunc::Lt => Bool, 609;
4846            params!(Date, Date) => BinaryFunc::Lt => Bool, 1095;
4847            params!(Time, Time) => BinaryFunc::Lt => Bool, 1110;
4848            params!(Timestamp, Timestamp) => BinaryFunc::Lt => Bool, 2062;
4849            params!(TimestampTz, TimestampTz) => BinaryFunc::Lt => Bool, 1322;
4850            params!(Uuid, Uuid) => BinaryFunc::Lt => Bool, 2974;
4851            params!(Interval, Interval) => BinaryFunc::Lt => Bool, 1332;
4852            params!(Bytes, Bytes) => BinaryFunc::Lt => Bool, 1957;
4853            params!(String, String) => BinaryFunc::Lt => Bool, 664;
4854            params!(Char, Char) => BinaryFunc::Lt => Bool, 1058;
4855            params!(PgLegacyChar, PgLegacyChar) => BinaryFunc::Lt => Bool, 631;
4856            params!(PgLegacyName, PgLegacyName) => BinaryFunc::Lt => Bool, 660;
4857            params!(Jsonb, Jsonb) => BinaryFunc::Lt => Bool, 3242;
4858            params!(ArrayAny, ArrayAny) => BinaryFunc::Lt => Bool, 1072;
4859            params!(RecordAny, RecordAny) => BinaryFunc::Lt => Bool, 2990;
4860            params!(MzTimestamp, MzTimestamp)=>BinaryFunc::Lt =>Bool, oid::FUNC_MZ_TIMESTAMP_LT_MZ_TIMESTAMP_OID;
4861            params!(RangeAny, RangeAny) => BinaryFunc::Lt => Bool, 3884;
4862        },
4863        "<=" => Scalar {
4864            params!(Numeric, Numeric) => BinaryFunc::Lte => Bool, 1755;
4865            params!(Bool, Bool) => BinaryFunc::Lte => Bool, 1694;
4866            params!(Int16, Int16) => BinaryFunc::Lte => Bool, 522;
4867            params!(Int32, Int32) => BinaryFunc::Lte => Bool, 523;
4868            params!(Int64, Int64) => BinaryFunc::Lte => Bool, 414;
4869            params!(UInt16, UInt16) => BinaryFunc::Lte => Bool, oid::FUNC_LTE_UINT16_OID;
4870            params!(UInt32, UInt32) => BinaryFunc::Lte => Bool, oid::FUNC_LTE_UINT32_OID;
4871            params!(UInt64, UInt64) => BinaryFunc::Lte => Bool, oid::FUNC_LTE_UINT64_OID;
4872            params!(Float32, Float32) => BinaryFunc::Lte => Bool, 624;
4873            params!(Float64, Float64) => BinaryFunc::Lte => Bool, 673;
4874            params!(Oid, Oid) => BinaryFunc::Lte => Bool, 611;
4875            params!(Date, Date) => BinaryFunc::Lte => Bool, 1096;
4876            params!(Time, Time) => BinaryFunc::Lte => Bool, 1111;
4877            params!(Timestamp, Timestamp) => BinaryFunc::Lte => Bool, 2063;
4878            params!(TimestampTz, TimestampTz) => BinaryFunc::Lte => Bool, 1323;
4879            params!(Uuid, Uuid) => BinaryFunc::Lte => Bool, 2976;
4880            params!(Interval, Interval) => BinaryFunc::Lte => Bool, 1333;
4881            params!(Bytes, Bytes) => BinaryFunc::Lte => Bool, 1958;
4882            params!(String, String) => BinaryFunc::Lte => Bool, 665;
4883            params!(Char, Char) => BinaryFunc::Lte => Bool, 1059;
4884            params!(PgLegacyChar, PgLegacyChar) => BinaryFunc::Lte => Bool, 632;
4885            params!(PgLegacyName, PgLegacyName) => BinaryFunc::Lte => Bool, 661;
4886            params!(Jsonb, Jsonb) => BinaryFunc::Lte => Bool, 3244;
4887            params!(ArrayAny, ArrayAny) => BinaryFunc::Lte => Bool, 1074;
4888            params!(RecordAny, RecordAny) => BinaryFunc::Lte => Bool, 2992;
4889            params!(MzTimestamp, MzTimestamp)=>BinaryFunc::Lte =>Bool, oid::FUNC_MZ_TIMESTAMP_LTE_MZ_TIMESTAMP_OID;
4890            params!(RangeAny, RangeAny) => BinaryFunc::Lte => Bool, 3885;
4891        },
4892        ">" => Scalar {
4893            params!(Numeric, Numeric) => BinaryFunc::Gt => Bool, 1756;
4894            params!(Bool, Bool) => BinaryFunc::Gt => Bool, 59;
4895            params!(Int16, Int16) => BinaryFunc::Gt => Bool, 520;
4896            params!(Int32, Int32) => BinaryFunc::Gt => Bool, 521;
4897            params!(Int64, Int64) => BinaryFunc::Gt => Bool, 413;
4898            params!(UInt16, UInt16) => BinaryFunc::Gt => Bool, oid::FUNC_GT_UINT16_OID;
4899            params!(UInt32, UInt32) => BinaryFunc::Gt => Bool, oid::FUNC_GT_UINT32_OID;
4900            params!(UInt64, UInt64) => BinaryFunc::Gt => Bool, oid::FUNC_GT_UINT64_OID;
4901            params!(Float32, Float32) => BinaryFunc::Gt => Bool, 623;
4902            params!(Float64, Float64) => BinaryFunc::Gt => Bool, 674;
4903            params!(Oid, Oid) => BinaryFunc::Gt => Bool, 610;
4904            params!(Date, Date) => BinaryFunc::Gt => Bool, 1097;
4905            params!(Time, Time) => BinaryFunc::Gt => Bool, 1112;
4906            params!(Timestamp, Timestamp) => BinaryFunc::Gt => Bool, 2064;
4907            params!(TimestampTz, TimestampTz) => BinaryFunc::Gt => Bool, 1324;
4908            params!(Uuid, Uuid) => BinaryFunc::Gt => Bool, 2975;
4909            params!(Interval, Interval) => BinaryFunc::Gt => Bool, 1334;
4910            params!(Bytes, Bytes) => BinaryFunc::Gt => Bool, 1959;
4911            params!(String, String) => BinaryFunc::Gt => Bool, 666;
4912            params!(Char, Char) => BinaryFunc::Gt => Bool, 1060;
4913            params!(PgLegacyChar, PgLegacyChar) => BinaryFunc::Gt => Bool, 633;
4914            params!(PgLegacyName, PgLegacyName) => BinaryFunc::Gt => Bool, 662;
4915            params!(Jsonb, Jsonb) => BinaryFunc::Gt => Bool, 3243;
4916            params!(ArrayAny, ArrayAny) => BinaryFunc::Gt => Bool, 1073;
4917            params!(RecordAny, RecordAny) => BinaryFunc::Gt => Bool, 2991;
4918            params!(MzTimestamp, MzTimestamp)=>BinaryFunc::Gt =>Bool, oid::FUNC_MZ_TIMESTAMP_GT_MZ_TIMESTAMP_OID;
4919            params!(RangeAny, RangeAny) => BinaryFunc::Gt => Bool, 3887;
4920        },
4921        ">=" => Scalar {
4922            params!(Numeric, Numeric) => BinaryFunc::Gte => Bool, 1757;
4923            params!(Bool, Bool) => BinaryFunc::Gte => Bool, 1695;
4924            params!(Int16, Int16) => BinaryFunc::Gte => Bool, 524;
4925            params!(Int32, Int32) => BinaryFunc::Gte => Bool, 525;
4926            params!(Int64, Int64) => BinaryFunc::Gte => Bool, 415;
4927            params!(UInt16, UInt16) => BinaryFunc::Gte => Bool, oid::FUNC_GTE_UINT16_OID;
4928            params!(UInt32, UInt32) => BinaryFunc::Gte => Bool, oid::FUNC_GTE_UINT32_OID;
4929            params!(UInt64, UInt64) => BinaryFunc::Gte => Bool, oid::FUNC_GTE_UINT64_OID;
4930            params!(Float32, Float32) => BinaryFunc::Gte => Bool, 625;
4931            params!(Float64, Float64) => BinaryFunc::Gte => Bool, 675;
4932            params!(Oid, Oid) => BinaryFunc::Gte => Bool, 612;
4933            params!(Date, Date) => BinaryFunc::Gte => Bool, 1098;
4934            params!(Time, Time) => BinaryFunc::Gte => Bool, 1113;
4935            params!(Timestamp, Timestamp) => BinaryFunc::Gte => Bool, 2065;
4936            params!(TimestampTz, TimestampTz) => BinaryFunc::Gte => Bool, 1325;
4937            params!(Uuid, Uuid) => BinaryFunc::Gte => Bool, 2977;
4938            params!(Interval, Interval) => BinaryFunc::Gte => Bool, 1335;
4939            params!(Bytes, Bytes) => BinaryFunc::Gte => Bool, 1960;
4940            params!(String, String) => BinaryFunc::Gte => Bool, 667;
4941            params!(Char, Char) => BinaryFunc::Gte => Bool, 1061;
4942            params!(PgLegacyChar, PgLegacyChar) => BinaryFunc::Gte => Bool, 634;
4943            params!(PgLegacyName, PgLegacyName) => BinaryFunc::Gte => Bool, 663;
4944            params!(Jsonb, Jsonb) => BinaryFunc::Gte => Bool, 3245;
4945            params!(ArrayAny, ArrayAny) => BinaryFunc::Gte => Bool, 1075;
4946            params!(RecordAny, RecordAny) => BinaryFunc::Gte => Bool, 2993;
4947            params!(MzTimestamp, MzTimestamp)=>BinaryFunc::Gte =>Bool, oid::FUNC_MZ_TIMESTAMP_GTE_MZ_TIMESTAMP_OID;
4948            params!(RangeAny, RangeAny) => BinaryFunc::Gte => Bool, 3886;
4949        },
4950        // Warning!
4951        // - If you are writing functions here that do not simply use
4952        //   `BinaryFunc::Eq`, you will break row equality (used in e.g.
4953        //   DISTINCT operations and JOINs). In short, this is totally verboten.
4954        // - The implementation of `BinaryFunc::Eq` is byte equality on two
4955        //   datums, and we enforce that both inputs to the function are of the
4956        //   same type in planning. However, it's possible that we will perform
4957        //   equality on types not listed here (e.g. `Varchar`) due to decisions
4958        //   made in the optimizer.
4959        "=" => Scalar {
4960            params!(Numeric, Numeric) => BinaryFunc::Eq => Bool, 1752;
4961            params!(Bool, Bool) => BinaryFunc::Eq => Bool, 91;
4962            params!(Int16, Int16) => BinaryFunc::Eq => Bool, 94;
4963            params!(Int32, Int32) => BinaryFunc::Eq => Bool, 96;
4964            params!(Int64, Int64) => BinaryFunc::Eq => Bool, 410;
4965            params!(UInt16, UInt16) => BinaryFunc::Eq => Bool, oid::FUNC_EQ_UINT16_OID;
4966            params!(UInt32, UInt32) => BinaryFunc::Eq => Bool, oid::FUNC_EQ_UINT32_OID;
4967            params!(UInt64, UInt64) => BinaryFunc::Eq => Bool, oid::FUNC_EQ_UINT64_OID;
4968            params!(Float32, Float32) => BinaryFunc::Eq => Bool, 620;
4969            params!(Float64, Float64) => BinaryFunc::Eq => Bool, 670;
4970            params!(Oid, Oid) => BinaryFunc::Eq => Bool, 607;
4971            params!(Date, Date) => BinaryFunc::Eq => Bool, 1093;
4972            params!(Time, Time) => BinaryFunc::Eq => Bool, 1108;
4973            params!(Timestamp, Timestamp) => BinaryFunc::Eq => Bool, 2060;
4974            params!(TimestampTz, TimestampTz) => BinaryFunc::Eq => Bool, 1320;
4975            params!(Uuid, Uuid) => BinaryFunc::Eq => Bool, 2972;
4976            params!(Interval, Interval) => BinaryFunc::Eq => Bool, 1330;
4977            params!(Bytes, Bytes) => BinaryFunc::Eq => Bool, 1955;
4978            params!(String, String) => BinaryFunc::Eq => Bool, 98;
4979            params!(Char, Char) => BinaryFunc::Eq => Bool, 1054;
4980            params!(PgLegacyChar, PgLegacyChar) => BinaryFunc::Eq => Bool, 92;
4981            params!(PgLegacyName, PgLegacyName) => BinaryFunc::Eq => Bool, 93;
4982            params!(Jsonb, Jsonb) => BinaryFunc::Eq => Bool, 3240;
4983            params!(ListAny, ListAny) => BinaryFunc::Eq => Bool, oid::FUNC_LIST_EQ_OID;
4984            params!(ArrayAny, ArrayAny) => BinaryFunc::Eq => Bool, 1070;
4985            params!(RecordAny, RecordAny) => BinaryFunc::Eq => Bool, 2988;
4986            params!(MzTimestamp, MzTimestamp) => BinaryFunc::Eq => Bool, oid::FUNC_MZ_TIMESTAMP_EQ_MZ_TIMESTAMP_OID;
4987            params!(RangeAny, RangeAny) => BinaryFunc::Eq => Bool, 3882;
4988            params!(MzAclItem, MzAclItem) => BinaryFunc::Eq => Bool, oid::FUNC_MZ_ACL_ITEM_EQ_MZ_ACL_ITEM_OID;
4989            params!(AclItem, AclItem) => BinaryFunc::Eq => Bool, 974;
4990        },
4991        "<>" => Scalar {
4992            params!(Numeric, Numeric) => BinaryFunc::NotEq => Bool, 1753;
4993            params!(Bool, Bool) => BinaryFunc::NotEq => Bool, 85;
4994            params!(Int16, Int16) => BinaryFunc::NotEq => Bool, 519;
4995            params!(Int32, Int32) => BinaryFunc::NotEq => Bool, 518;
4996            params!(Int64, Int64) => BinaryFunc::NotEq => Bool, 411;
4997            params!(UInt16, UInt16) => BinaryFunc::NotEq => Bool, oid::FUNC_NOT_EQ_UINT16_OID;
4998            params!(UInt32, UInt32) => BinaryFunc::NotEq => Bool, oid::FUNC_NOT_EQ_UINT32_OID;
4999            params!(UInt64, UInt64) => BinaryFunc::NotEq => Bool, oid::FUNC_NOT_EQ_UINT64_OID;
5000            params!(Float32, Float32) => BinaryFunc::NotEq => Bool, 621;
5001            params!(Float64, Float64) => BinaryFunc::NotEq => Bool, 671;
5002            params!(Oid, Oid) => BinaryFunc::NotEq => Bool, 608;
5003            params!(Date, Date) => BinaryFunc::NotEq => Bool, 1094;
5004            params!(Time, Time) => BinaryFunc::NotEq => Bool, 1109;
5005            params!(Timestamp, Timestamp) => BinaryFunc::NotEq => Bool, 2061;
5006            params!(TimestampTz, TimestampTz) => BinaryFunc::NotEq => Bool, 1321;
5007            params!(Uuid, Uuid) => BinaryFunc::NotEq => Bool, 2973;
5008            params!(Interval, Interval) => BinaryFunc::NotEq => Bool, 1331;
5009            params!(Bytes, Bytes) => BinaryFunc::NotEq => Bool, 1956;
5010            params!(String, String) => BinaryFunc::NotEq => Bool, 531;
5011            params!(Char, Char) => BinaryFunc::NotEq => Bool, 1057;
5012            params!(PgLegacyChar, PgLegacyChar) => BinaryFunc::NotEq => Bool, 630;
5013            params!(PgLegacyName, PgLegacyName) => BinaryFunc::NotEq => Bool, 643;
5014            params!(Jsonb, Jsonb) => BinaryFunc::NotEq => Bool, 3241;
5015            params!(ArrayAny, ArrayAny) => BinaryFunc::NotEq => Bool, 1071;
5016            params!(RecordAny, RecordAny) => BinaryFunc::NotEq => Bool, 2989;
5017            params!(MzTimestamp, MzTimestamp) => BinaryFunc::NotEq => Bool, oid::FUNC_MZ_TIMESTAMP_NOT_EQ_MZ_TIMESTAMP_OID;
5018            params!(RangeAny, RangeAny) => BinaryFunc::NotEq => Bool, 3883;
5019            params!(MzAclItem, MzAclItem) => BinaryFunc::NotEq => Bool, oid::FUNC_MZ_ACL_ITEM_NOT_EQ_MZ_ACL_ITEM_OID;
5020        }
5021    }
5022});
5023
5024/// Resolves the operator to a set of function implementations.
5025pub fn resolve_op(op: &str) -> Result<&'static [FuncImpl<HirScalarExpr>], PlanError> {
5026    match OP_IMPLS.get(op) {
5027        Some(Func::Scalar(impls)) => Ok(impls),
5028        Some(_) => unreachable!("all operators must be scalar functions"),
5029        // TODO: these require sql arrays
5030        // JsonContainsAnyFields
5031        // JsonContainsAllFields
5032        // TODO: these require json paths
5033        // JsonGetPath
5034        // JsonGetPathAsText
5035        // JsonDeletePath
5036        // JsonContainsPath
5037        // JsonApplyPathPredicate
5038        None => bail_unsupported!(format!("[{}]", op)),
5039    }
5040}
5041
5042// Since ViewableVariables is unmaterializeable (which can't be eval'd) that
5043// depend on their arguments, implement directly with Hir.
5044fn current_settings(
5045    name: HirScalarExpr,
5046    missing_ok: HirScalarExpr,
5047) -> Result<HirScalarExpr, PlanError> {
5048    // MapGetValue returns Null if the key doesn't exist in the map.
5049    let expr = HirScalarExpr::call_binary(
5050        HirScalarExpr::call_unmaterializable(UnmaterializableFunc::ViewableVariables),
5051        HirScalarExpr::call_unary(name, UnaryFunc::Lower(func::Lower)),
5052        BinaryFunc::MapGetValue,
5053    );
5054    let expr = HirScalarExpr::if_then_else(
5055        missing_ok,
5056        expr.clone(),
5057        HirScalarExpr::call_variadic(
5058            VariadicFunc::ErrorIfNull,
5059            vec![
5060                expr,
5061                HirScalarExpr::literal(
5062                    Datum::String("unrecognized configuration parameter"),
5063                    ScalarType::String,
5064                ),
5065            ],
5066        ),
5067    );
5068    Ok(expr)
5069}