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