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