Skip to main content

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