Skip to main content

mz_sql/plan/
typeconv.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//! Maintains a catalog of valid casts between [`mz_repr::SqlScalarType`]s, as well as
11//! other cast-related functions.
12
13use std::cell::RefCell;
14use std::collections::BTreeMap;
15use std::sync::LazyLock;
16
17use dynfmt::{Format, SimpleCurlyFormat};
18use itertools::Itertools;
19use mz_expr::func;
20use mz_expr::func::variadic::{JsonbBuildObject, RecordCreate};
21use mz_expr::func::{CastArrayToJsonb, CastListToJsonb};
22use mz_repr::{
23    ColumnName, Datum, SqlColumnType, SqlRelationType, SqlScalarBaseType, SqlScalarType,
24};
25
26use crate::catalog::TypeCategory;
27use crate::plan::error::PlanError;
28use crate::plan::hir::{
29    AbstractColumnType, CoercibleScalarExpr, CoercibleScalarType, HirScalarExpr, UnaryFunc,
30};
31use crate::plan::query::{ExprContext, QueryContext};
32use crate::plan::scope::Scope;
33
34/// Like func::sql_impl_func, but for casts.
35fn sql_impl_cast(expr: &'static str) -> CastTemplate {
36    let invoke = crate::func::sql_impl(expr);
37    CastTemplate::new(move |ecx, _ccx, from_type, _to_type| {
38        // Oddly, this needs to be able to gracefully fail so we can detect unmet dependencies.
39        let mut out = invoke(ecx, vec![from_type.clone()]).ok()?;
40        Some(move |e| {
41            out.splice_parameters(&[e], 0);
42            out
43        })
44    })
45}
46
47fn sql_impl_cast_per_context(casts: &[(CastContext, &'static str)]) -> CastTemplate {
48    let casts: BTreeMap<CastContext, _> = casts
49        .iter()
50        .map(|(ccx, expr)| (ccx.clone(), crate::func::sql_impl(expr)))
51        .collect();
52    CastTemplate::new(move |ecx, ccx, from_type, _to_type| {
53        let invoke = &casts[&ccx];
54        let r = invoke(ecx, vec![from_type.clone()]);
55        let mut out = r.ok()?;
56        Some(move |e| {
57            out.splice_parameters(&[e], 0);
58            out
59        })
60    })
61}
62
63/// A cast is a function that takes a `ScalarExpr` to another `ScalarExpr`.
64type Cast = Box<dyn FnOnce(HirScalarExpr) -> HirScalarExpr>;
65
66/// A cast template is a function that produces a `Cast` given a concrete input
67/// and output type. A template can return `None` to indicate that it is
68/// incapable of producing a cast for the specified types.
69///
70/// Cast templates are used to share code for similar casts, where the input or
71/// output type is of one "category" of type. For example, a single cast
72/// template handles converting from strings to any list type. Without cast
73/// templates, we'd have to enumerate every possible list -> list conversion,
74/// which is impractical.
75struct CastTemplate(
76    Box<
77        dyn Fn(&ExprContext, CastContext, &SqlScalarType, &SqlScalarType) -> Option<Cast>
78            + Send
79            + Sync,
80    >,
81);
82
83impl CastTemplate {
84    fn new<T, C>(t: T) -> CastTemplate
85    where
86        T: Fn(&ExprContext, CastContext, &SqlScalarType, &SqlScalarType) -> Option<C>
87            + Send
88            + Sync
89            + 'static,
90        C: FnOnce(HirScalarExpr) -> HirScalarExpr + 'static,
91    {
92        CastTemplate(Box::new(move |ecx, ccx, from_ty, to_ty| {
93            Some(Box::new(t(ecx, ccx, from_ty, to_ty)?))
94        }))
95    }
96}
97
98impl From<UnaryFunc> for CastTemplate {
99    fn from(u: UnaryFunc) -> CastTemplate {
100        CastTemplate::new(move |_ecx, _ccx, _from, _to| {
101            let u = u.clone();
102            Some(move |expr: HirScalarExpr| expr.call_unary(u))
103        })
104    }
105}
106
107impl<const N: usize> From<[UnaryFunc; N]> for CastTemplate {
108    fn from(funcs: [UnaryFunc; N]) -> CastTemplate {
109        CastTemplate::new(move |_ecx, _ccx, _from, _to| {
110            let funcs = funcs.clone();
111            Some(move |mut expr: HirScalarExpr| {
112                for func in funcs {
113                    expr = expr.call_unary(func.clone());
114                }
115                expr
116            })
117        })
118    }
119}
120
121/// STRING to REG*
122///
123/// A reg* type represents a specific type of object by oid.
124///
125/// Casting from a string to a reg*:
126/// - Accepts `-`, the absent reference, as OID 0 in all cases, matching
127///   PostgreSQL's `parseDashOrOid`.
128/// - Accepts a string that looks like an OID and converts the value to the
129///   specified reg* type. This is available in all cases except explicitly
130///   casting text values to regclass (e.g. `SELECT '2'::text::regclass`)
131/// - Resolves non-OID-appearing strings to objects. If this string resolves to
132///   more than one OID (e.g. functions), it errors.
133///
134/// The below code provides a template to accomplish this for various reg*
135/// types. Arguments in order are:
136/// - 0: type catalog name this is casting to
137/// - 1: the category of this reg for the error message
138/// - 2: Whether or not to permit passing through numeric values as OIDs
139const STRING_REG_CAST_TEMPLATE: &str = "
140(SELECT
141CASE
142    WHEN $1 IS NULL THEN NULL
143-- Handle the absent reference, which the reg* text output spells `-`. PostgreSQL's
144-- `parseDashOrOid` accepts it for every reg* type, including text to regclass.
145    WHEN $1 = '-' THEN 0::pg_catalog.oid::pg_catalog.{0}
146-- Handle OID-like input, if available via {2}
147    WHEN {2} AND pg_catalog.substring($1, 1, 1) BETWEEN '0' AND '9' THEN
148        $1::pg_catalog.oid::pg_catalog.{0}
149    ELSE (
150    -- String case; look up that the item exists
151        SELECT o.oid
152        FROM mz_unsafe.mz_error_if_null(
153            (
154                -- We need to ensure a distinct here in the case of e.g. functions,
155                -- where multiple items share a GlobalId.
156                SELECT DISTINCT id AS name_id
157                FROM mz_internal.mz_resolve_object_name('{0}', $1)
158            ),
159            -- TODO: Support the correct error code for does not exist (42883).
160            '{1} \"' || $1 || '\" does not exist'
161        ) AS i (name_id),
162        -- Lateral lets us error separately from DNE case
163        LATERAL (
164            SELECT
165                CASE
166            -- Handle too many OIDs
167                WHEN mz_catalog.list_length(mz_catalog.list_agg(oid)) > 1 THEN
168                    mz_unsafe.mz_error_if_null(
169                        NULL::pg_catalog.{0},
170                        'more than one {1} named \"' || $1 || '\"'
171                    )
172            -- Resolve object name's OID if we know there is only one
173                ELSE
174                    CAST(mz_catalog.list_agg(oid)[1] AS pg_catalog.{0})
175                END
176            FROM mz_catalog.mz_objects
177            WHERE id = name_id
178            GROUP BY id
179        ) AS o (oid)
180    )
181END)";
182
183static STRING_TO_REGCLASS_EXPLICIT: LazyLock<String> = LazyLock::new(|| {
184    SimpleCurlyFormat
185        .format(STRING_REG_CAST_TEMPLATE, ["regclass", "relation", "false"])
186        .unwrap()
187        .to_string()
188});
189
190static STRING_TO_REGCLASS_COERCED: LazyLock<String> = LazyLock::new(|| {
191    SimpleCurlyFormat
192        .format(STRING_REG_CAST_TEMPLATE, ["regclass", "relation", "true"])
193        .unwrap()
194        .to_string()
195});
196
197static STRING_TO_REGPROC: LazyLock<String> = LazyLock::new(|| {
198    SimpleCurlyFormat
199        .format(STRING_REG_CAST_TEMPLATE, ["regproc", "function", "true"])
200        .unwrap()
201        .to_string()
202});
203
204static STRING_TO_REGTYPE: LazyLock<String> = LazyLock::new(|| {
205    SimpleCurlyFormat
206        .format(STRING_REG_CAST_TEMPLATE, ["regtype", "type", "true"])
207        .unwrap()
208        .to_string()
209});
210
211// OID 0 renders as `-` to match PostgreSQL's `regprocout`, `regclassout` and
212// `regtypeout`, which all spell the absent reference that way. A nonzero OID
213// that names nothing still renders as its digits, also as PostgreSQL does.
214const REG_STRING_CAST_TEMPLATE: &str = "(
215SELECT
216    CASE
217      WHEN CAST($1 AS pg_catalog.oid) = 0::pg_catalog.oid THEN '-'
218      ELSE
219        COALESCE(mz_internal.mz_global_id_to_name(o.id), CAST($1 AS pg_catalog.oid)::pg_catalog.text)
220    END
221    AS text
222FROM
223  (
224        SELECT
225          (
226            SELECT DISTINCT id
227            FROM
228              mz_catalog.mz_objects AS o
229                JOIN
230                  mz_internal.mz_object_oid_alias AS a
231                  ON o.type = a.object_type
232            WHERE
233              oid = CAST($1 AS pg_catalog.oid)
234                AND
235              a.oid_alias = '{0}'
236          )
237      )
238      AS o
239)";
240
241static REGCLASS_TO_STRING: LazyLock<String> = LazyLock::new(|| {
242    SimpleCurlyFormat
243        .format(REG_STRING_CAST_TEMPLATE, ["regclass"])
244        .unwrap()
245        .to_string()
246});
247
248static REGPROC_TO_STRING: LazyLock<String> = LazyLock::new(|| {
249    SimpleCurlyFormat
250        .format(REG_STRING_CAST_TEMPLATE, ["regproc"])
251        .unwrap()
252        .to_string()
253});
254
255static REGTYPE_TO_STRING: LazyLock<String> = LazyLock::new(|| {
256    SimpleCurlyFormat
257        .format(REG_STRING_CAST_TEMPLATE, ["regtype"])
258        .unwrap()
259        .to_string()
260});
261
262/// Describes the context of a cast.
263///
264/// n.b. this type derived `Ord, PartialOrd` and the ordering of these values
265/// has semantics meaning; casts are only permitted when the caller's cast
266/// context is geq the ccx we store, which is the minimum required context.
267#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
268pub enum CastContext {
269    /// Implicit casts are "no-brainer" casts that apply automatically in
270    /// expressions. They are typically lossless, such as `SqlScalarType::Int32` to
271    /// `SqlScalarType::Int64`.
272    Implicit,
273    /// Assignment casts are "reasonable" casts that make sense to apply
274    /// automatically in `INSERT` statements, but are surprising enough that
275    /// they don't apply implicitly in expressions.
276    Assignment,
277    /// Explicit casts are casts that are possible but may be surprising, like
278    /// casting `SqlScalarType::Json` to `SqlScalarType::Int32`, and therefore they do
279    /// not happen unless explicitly requested by the user with a cast operator.
280    Explicit,
281    /// Coerced casts permit different behavior when a type is coerced from a
282    /// string literal vs. a value of type `pg_catalog::text`.
283    ///
284    /// The only call site that should pass this value in to this module is
285    /// string coercion.
286    Coerced,
287}
288
289/// The implementation of a cast.
290struct CastImpl {
291    template: CastTemplate,
292    context: CastContext,
293}
294
295macro_rules! casts(
296    {
297        $(
298            $from_to:expr => $cast_context:ident: $cast_template:expr
299        ),+
300    } => {{
301        let mut m = BTreeMap::new();
302        $(
303            m.insert($from_to, CastImpl {
304                template: $cast_template.into(),
305                context: CastContext::$cast_context,
306            });
307        )+
308        m
309    }};
310);
311
312static VALID_CASTS: LazyLock<BTreeMap<(SqlScalarBaseType, SqlScalarBaseType), CastImpl>> =
313    LazyLock::new(|| {
314        use SqlScalarBaseType::*;
315        use UnaryFunc::*;
316
317        casts! {
318            // BOOL
319            (Bool, Int32) => Explicit: CastBoolToInt32(func::CastBoolToInt32),
320            (Bool, Int64) => Explicit: CastBoolToInt64(func::CastBoolToInt64),
321            (Bool, String) => Assignment: CastBoolToString(func::CastBoolToString),
322
323            //INT16
324            (Int16, Int32) => Implicit: CastInt16ToInt32(func::CastInt16ToInt32),
325            (Int16, Int64) => Implicit: CastInt16ToInt64(func::CastInt16ToInt64),
326            (Int16, UInt16) => Assignment: CastInt16ToUint16(func::CastInt16ToUint16),
327            (Int16, UInt32) => Assignment: CastInt16ToUint32(func::CastInt16ToUint32),
328            (Int16, UInt64) => Assignment: CastInt16ToUint64(func::CastInt16ToUint64),
329            (Int16, Float32) => Implicit: CastInt16ToFloat32(func::CastInt16ToFloat32),
330            (Int16, Float64) => Implicit: CastInt16ToFloat64(func::CastInt16ToFloat64),
331            (Int16, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
332                let s = to_type.unwrap_numeric_max_scale();
333                let f = CastInt16ToNumeric(func::CastInt16ToNumeric(s));
334                Some(move |e: HirScalarExpr| e.call_unary(f))
335            }),
336            (Int16, Oid) => Implicit: [
337                CastInt16ToInt32(func::CastInt16ToInt32),
338                CastInt32ToOid(func::CastInt32ToOid),
339            ],
340            (Int16, RegClass) => Implicit: [
341                CastInt16ToInt32(func::CastInt16ToInt32),
342                CastInt32ToOid(func::CastInt32ToOid),
343                CastOidToRegClass(func::CastOidToRegClass),
344            ],
345            (Int16, RegProc) => Implicit: [
346                CastInt16ToInt32(func::CastInt16ToInt32),
347                CastInt32ToOid(func::CastInt32ToOid),
348                CastOidToRegProc(func::CastOidToRegProc),
349            ],
350            (Int16, RegType) => Implicit: [
351                CastInt16ToInt32(func::CastInt16ToInt32),
352                CastInt32ToOid(func::CastInt32ToOid),
353                CastOidToRegType(func::CastOidToRegType),
354            ],
355            (Int16, String) => Assignment: CastInt16ToString(func::CastInt16ToString),
356
357            //INT32
358            (Int32, Bool) => Explicit: CastInt32ToBool(func::CastInt32ToBool),
359            (Int32, Oid) => Implicit: CastInt32ToOid(func::CastInt32ToOid),
360            (Int32, RegClass) => Implicit: [
361                CastInt32ToOid(func::CastInt32ToOid),
362                CastOidToRegClass(func::CastOidToRegClass),
363            ],
364            (Int32, RegProc) => Implicit: [
365                CastInt32ToOid(func::CastInt32ToOid),
366                CastOidToRegProc(func::CastOidToRegProc),
367            ],
368            (Int32, RegType) => Implicit: [
369                CastInt32ToOid(func::CastInt32ToOid),
370                CastOidToRegType(func::CastOidToRegType),
371            ],
372            (Int32, PgLegacyChar) => Explicit:
373                CastInt32ToPgLegacyChar(func::CastInt32ToPgLegacyChar),
374            (Int32, Int16) => Assignment: CastInt32ToInt16(func::CastInt32ToInt16),
375            (Int32, Int64) => Implicit: CastInt32ToInt64(func::CastInt32ToInt64),
376            (Int32, UInt16) => Assignment: CastInt32ToUint16(func::CastInt32ToUint16),
377            (Int32, UInt32) => Assignment: CastInt32ToUint32(func::CastInt32ToUint32),
378            (Int32, UInt64) => Assignment: CastInt32ToUint64(func::CastInt32ToUint64),
379            (Int32, Float32) => Implicit: CastInt32ToFloat32(func::CastInt32ToFloat32),
380            (Int32, Float64) => Implicit: CastInt32ToFloat64(func::CastInt32ToFloat64),
381            (Int32, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
382                let s = to_type.unwrap_numeric_max_scale();
383                let f = CastInt32ToNumeric(func::CastInt32ToNumeric(s));
384                Some(move |e: HirScalarExpr| e.call_unary(f))
385            }),
386            (Int32, String) => Assignment: CastInt32ToString(func::CastInt32ToString),
387
388            // INT64
389            (Int64, Bool) => Explicit: CastInt64ToBool(func::CastInt64ToBool),
390            (Int64, Int16) => Assignment: CastInt64ToInt16(func::CastInt64ToInt16),
391            (Int64, Int32) => Assignment: CastInt64ToInt32(func::CastInt64ToInt32),
392            (Int64, UInt16) => Assignment: CastInt64ToUint16(func::CastInt64ToUint16),
393            (Int64, UInt32) => Assignment: CastInt64ToUint32(func::CastInt64ToUint32),
394            (Int64, UInt64) => Assignment: CastInt64ToUint64(func::CastInt64ToUint64),
395            (Int64, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
396                let s = to_type.unwrap_numeric_max_scale();
397                let f = CastInt64ToNumeric(func::CastInt64ToNumeric(s));
398                Some(move |e: HirScalarExpr| e.call_unary(f))
399            }),
400            (Int64, Float32) => Implicit: CastInt64ToFloat32(func::CastInt64ToFloat32),
401            (Int64, Float64) => Implicit: CastInt64ToFloat64(func::CastInt64ToFloat64),
402            (Int64, Oid) => Implicit: CastInt64ToOid(func::CastInt64ToOid),
403            (Int64, RegClass) => Implicit: [
404                CastInt64ToOid(func::CastInt64ToOid),
405                CastOidToRegClass(func::CastOidToRegClass),
406            ],
407            (Int64, RegProc) => Implicit: [
408                CastInt64ToOid(func::CastInt64ToOid),
409                CastOidToRegProc(func::CastOidToRegProc),
410            ],
411            (Int64, RegType) => Implicit: [
412                CastInt64ToOid(func::CastInt64ToOid),
413                CastOidToRegType(func::CastOidToRegType),
414            ],
415            (Int64, String) => Assignment: CastInt64ToString(func::CastInt64ToString),
416
417            // UINT16
418            (UInt16, UInt32) => Implicit: CastUint16ToUint32(func::CastUint16ToUint32),
419            (UInt16, UInt64) => Implicit: CastUint16ToUint64(func::CastUint16ToUint64),
420            (UInt16, Int16) => Assignment: CastUint16ToInt16(func::CastUint16ToInt16),
421            (UInt16, Int32) => Implicit: CastUint16ToInt32(func::CastUint16ToInt32),
422            (UInt16, Int64) => Implicit: CastUint16ToInt64(func::CastUint16ToInt64),
423            (UInt16, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
424                let s = to_type.unwrap_numeric_max_scale();
425                let f = CastUint16ToNumeric(func::CastUint16ToNumeric(s));
426                Some(move |e: HirScalarExpr| e.call_unary(f))
427            }),
428            (UInt16, Float32) => Implicit: CastUint16ToFloat32(func::CastUint16ToFloat32),
429            (UInt16, Float64) => Implicit: CastUint16ToFloat64(func::CastUint16ToFloat64),
430            (UInt16, String) => Assignment: CastUint16ToString(func::CastUint16ToString),
431
432            // UINT32
433            (UInt32, UInt16) => Assignment: CastUint32ToUint16(func::CastUint32ToUint16),
434            (UInt32, UInt64) => Implicit: CastUint32ToUint64(func::CastUint32ToUint64),
435            (UInt32, Int16) => Assignment: CastUint32ToInt16(func::CastUint32ToInt16),
436            (UInt32, Int32) => Assignment: CastUint32ToInt32(func::CastUint32ToInt32),
437            (UInt32, Int64) => Implicit: CastUint32ToInt64(func::CastUint32ToInt64),
438            (UInt32, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
439                let s = to_type.unwrap_numeric_max_scale();
440                let f = CastUint32ToNumeric(func::CastUint32ToNumeric(s));
441                Some(move |e: HirScalarExpr| e.call_unary(f))
442            }),
443            (UInt32, Float32) => Implicit: CastUint32ToFloat32(func::CastUint32ToFloat32),
444            (UInt32, Float64) => Implicit: CastUint32ToFloat64(func::CastUint32ToFloat64),
445            (UInt32, String) => Assignment: CastUint32ToString(func::CastUint32ToString),
446
447            // UINT64
448            (UInt64, UInt16) => Assignment: CastUint64ToUint16(func::CastUint64ToUint16),
449            (UInt64, UInt32) => Assignment: CastUint64ToUint32(func::CastUint64ToUint32),
450            (UInt64, Int16) => Assignment: CastUint64ToInt16(func::CastUint64ToInt16),
451            (UInt64, Int32) => Assignment: CastUint64ToInt32(func::CastUint64ToInt32),
452            (UInt64, Int64) => Assignment: CastUint64ToInt64(func::CastUint64ToInt64),
453            (UInt64, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
454                let s = to_type.unwrap_numeric_max_scale();
455                let f = CastUint64ToNumeric(func::CastUint64ToNumeric(s));
456                Some(move |e: HirScalarExpr| e.call_unary(f))
457            }),
458            (UInt64, Float32) => Implicit: CastUint64ToFloat32(func::CastUint64ToFloat32),
459            (UInt64, Float64) => Implicit: CastUint64ToFloat64(func::CastUint64ToFloat64),
460            (UInt64, String) => Assignment: CastUint64ToString(func::CastUint64ToString),
461
462            // MZ_TIMESTAMP
463            (MzTimestamp, String) => Assignment:
464                CastMzTimestampToString(func::CastMzTimestampToString),
465            (MzTimestamp, Timestamp) => Assignment:
466                CastMzTimestampToTimestamp(func::CastMzTimestampToTimestamp),
467            (MzTimestamp, TimestampTz) => Assignment:
468                CastMzTimestampToTimestampTz(
469                    func::CastMzTimestampToTimestampTz,
470                ),
471            (String, MzTimestamp) => Assignment:
472                CastStringToMzTimestamp(func::CastStringToMzTimestamp),
473            (UInt64, MzTimestamp) => Implicit:
474                CastUint64ToMzTimestamp(func::CastUint64ToMzTimestamp),
475            (UInt32, MzTimestamp) => Implicit:
476                CastUint32ToMzTimestamp(func::CastUint32ToMzTimestamp),
477            (Int64, MzTimestamp) => Implicit:
478                CastInt64ToMzTimestamp(func::CastInt64ToMzTimestamp),
479            (Int32, MzTimestamp) => Implicit:
480                CastInt32ToMzTimestamp(func::CastInt32ToMzTimestamp),
481            (Numeric, MzTimestamp) => Implicit:
482                CastNumericToMzTimestamp(func::CastNumericToMzTimestamp),
483            (Timestamp, MzTimestamp) => Implicit:
484                CastTimestampToMzTimestamp(func::CastTimestampToMzTimestamp),
485            (TimestampTz, MzTimestamp) => Implicit:
486                CastTimestampTzToMzTimestamp(
487                    func::CastTimestampTzToMzTimestamp,
488                ),
489            (Date, MzTimestamp) => Implicit:
490                CastDateToMzTimestamp(func::CastDateToMzTimestamp),
491
492            // OID
493            (Oid, Int32) => Assignment: CastOidToInt32(func::CastOidToInt32),
494            (Oid, Int64) => Assignment: CastOidToInt64(func::CastOidToInt64),
495            (Oid, String) => Explicit: CastOidToString(func::CastOidToString),
496            (Oid, RegClass) => Implicit: CastOidToRegClass(func::CastOidToRegClass),
497            (Oid, RegProc) => Implicit: CastOidToRegProc(func::CastOidToRegProc),
498            (Oid, RegType) => Implicit: CastOidToRegType(func::CastOidToRegType),
499
500            // REGCLASS
501            (RegClass, Oid) => Implicit: CastRegClassToOid(func::CastRegClassToOid),
502            (RegClass, String) => Explicit: sql_impl_cast(&REGCLASS_TO_STRING),
503
504            // REGPROC
505            (RegProc, Oid) => Implicit: CastRegProcToOid(func::CastRegProcToOid),
506            (RegProc, String) => Explicit: sql_impl_cast(&REGPROC_TO_STRING),
507
508            // REGTYPE
509            (RegType, Oid) => Implicit: CastRegTypeToOid(func::CastRegTypeToOid),
510            (RegType, String) => Explicit: sql_impl_cast(&REGTYPE_TO_STRING),
511
512            // FLOAT32
513            (Float32, Int16) => Assignment: CastFloat32ToInt16(func::CastFloat32ToInt16),
514            (Float32, Int32) => Assignment: CastFloat32ToInt32(func::CastFloat32ToInt32),
515            (Float32, Int64) => Assignment: CastFloat32ToInt64(func::CastFloat32ToInt64),
516            (Float32, UInt16) => Assignment: CastFloat32ToUint16(func::CastFloat32ToUint16),
517            (Float32, UInt32) => Assignment: CastFloat32ToUint32(func::CastFloat32ToUint32),
518            (Float32, UInt64) => Assignment: CastFloat32ToUint64(func::CastFloat32ToUint64),
519            (Float32, Float64) => Implicit: CastFloat32ToFloat64(func::CastFloat32ToFloat64),
520            (Float32, Numeric) => Assignment: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
521                let s = to_type.unwrap_numeric_max_scale();
522                let f = CastFloat32ToNumeric(func::CastFloat32ToNumeric(s));
523                Some(move |e: HirScalarExpr| e.call_unary(f))
524            }),
525            (Float32, String) => Assignment: CastFloat32ToString(func::CastFloat32ToString),
526
527            // FLOAT64
528            (Float64, Int16) => Assignment: CastFloat64ToInt16(func::CastFloat64ToInt16),
529            (Float64, Int32) => Assignment: CastFloat64ToInt32(func::CastFloat64ToInt32),
530            (Float64, Int64) => Assignment: CastFloat64ToInt64(func::CastFloat64ToInt64),
531            (Float64, UInt16) => Assignment: CastFloat64ToUint16(func::CastFloat64ToUint16),
532            (Float64, UInt32) => Assignment: CastFloat64ToUint32(func::CastFloat64ToUint32),
533            (Float64, UInt64) => Assignment: CastFloat64ToUint64(func::CastFloat64ToUint64),
534            (Float64, Float32) => Assignment: CastFloat64ToFloat32(func::CastFloat64ToFloat32),
535            (Float64, Numeric) => Assignment: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
536                let s = to_type.unwrap_numeric_max_scale();
537                let f = CastFloat64ToNumeric(func::CastFloat64ToNumeric(s));
538                Some(move |e: HirScalarExpr| e.call_unary(f))
539            }),
540            (Float64, String) => Assignment: CastFloat64ToString(func::CastFloat64ToString),
541
542            // DATE
543            (Date, Timestamp) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
544                let p = to_type.unwrap_timestamp_precision();
545                let f = CastDateToTimestamp(func::CastDateToTimestamp(p));
546                Some(move |e: HirScalarExpr| e.call_unary(f))
547            }),
548            (Date, TimestampTz) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
549                let p = to_type.unwrap_timestamp_precision();
550                let f = CastDateToTimestampTz(func::CastDateToTimestampTz(p));
551                Some(move |e: HirScalarExpr| e.call_unary(f))
552            }),
553            (Date, String) => Assignment: CastDateToString(func::CastDateToString),
554
555            // TIME
556            (Time, Interval) => Implicit: CastTimeToInterval(func::CastTimeToInterval),
557            (Time, String) => Assignment: CastTimeToString(func::CastTimeToString),
558
559            // TIMESTAMP
560            (Timestamp, Date) => Assignment: CastTimestampToDate(func::CastTimestampToDate),
561            (Timestamp, TimestampTz) => Implicit: CastTemplate::new(
562                |_ecx, _ccx, from_type, to_type|
563            {
564                let from = from_type.unwrap_timestamp_precision();
565                let to = to_type.unwrap_timestamp_precision();
566                let f = CastTimestampToTimestampTz(
567                    func::CastTimestampToTimestampTz { from, to },
568                );
569                Some(move |e: HirScalarExpr| e.call_unary(f))
570            }),
571            (Timestamp, Timestamp) => Assignment: CastTemplate::new(
572                |_ecx, _ccx, from_type, to_type|
573            {
574                let from = from_type.unwrap_timestamp_precision();
575                let to = to_type.unwrap_timestamp_precision();
576                let f = AdjustTimestampPrecision(
577                    func::AdjustTimestampPrecision { from, to },
578                );
579                Some(move |e: HirScalarExpr| e.call_unary(f))
580            }),
581            (Timestamp, Time) => Assignment: CastTimestampToTime(func::CastTimestampToTime),
582            (Timestamp, String) => Assignment: CastTimestampToString(func::CastTimestampToString),
583
584            // TIMESTAMPTZ
585            (TimestampTz, Date) => Assignment: CastTimestampTzToDate(func::CastTimestampTzToDate),
586            (TimestampTz, Timestamp) => Assignment: CastTemplate::new(
587                |_ecx, _ccx, from_type, to_type|
588            {
589                let from = from_type.unwrap_timestamp_precision();
590                let to = to_type.unwrap_timestamp_precision();
591                let f = CastTimestampTzToTimestamp(
592                    func::CastTimestampTzToTimestamp { from, to },
593                );
594                Some(move |e: HirScalarExpr| e.call_unary(f))
595            }),
596            (TimestampTz, TimestampTz) => Assignment: CastTemplate::new(
597                |_ecx, _ccx, from_type, to_type|
598            {
599                let from = from_type.unwrap_timestamp_precision();
600                let to = to_type.unwrap_timestamp_precision();
601                let f = AdjustTimestampTzPrecision(
602                    func::AdjustTimestampTzPrecision { from, to },
603                );
604                Some(move |e: HirScalarExpr| e.call_unary(f))
605            }),
606            (TimestampTz, Time) => Assignment:
607                CastTimestampTzToTime(func::CastTimestampTzToTime),
608            (TimestampTz, String) => Assignment:
609                CastTimestampTzToString(func::CastTimestampTzToString),
610
611            // INTERVAL
612            (Interval, Time) => Assignment: CastIntervalToTime(func::CastIntervalToTime),
613            (Interval, String) => Assignment: CastIntervalToString(func::CastIntervalToString),
614
615            // BYTES
616            (Bytes, String) => Assignment: CastBytesToString(func::CastBytesToString),
617
618            // STRING
619            (String, Bool) => Explicit: CastStringToBool(func::CastStringToBool),
620            (String, Int16) => Explicit: CastStringToInt16(func::CastStringToInt16),
621            (String, Int32) => Explicit: CastStringToInt32(func::CastStringToInt32),
622            (String, Int64) => Explicit: CastStringToInt64(func::CastStringToInt64),
623            (String, UInt16) => Explicit: CastStringToUint16(func::CastStringToUint16),
624            (String, UInt32) => Explicit: CastStringToUint32(func::CastStringToUint32),
625            (String, UInt64) => Explicit: CastStringToUint64(func::CastStringToUint64),
626            (String, Oid) => Explicit: CastStringToOid(func::CastStringToOid),
627
628            // STRING to REG*
629            // A reg* type represents a specific type of object by oid.
630            // Converting from string to reg* does a lookup of the object name
631            // in the corresponding mz_catalog table and expects exactly one object to match it.
632            // You can also specify (in postgres) a string that's a valid
633            // int4 and it'll happily cast it (without verifying that the int4 matches
634            // an object oid).
635            // TODO: Support the correct error code for does not exist (42883).
636            (String, RegClass) => Explicit: sql_impl_cast_per_context(
637                &[
638                    (CastContext::Explicit, &STRING_TO_REGCLASS_EXPLICIT),
639                    (CastContext::Coerced, &STRING_TO_REGCLASS_COERCED)
640                ]
641            ),
642            (String, RegProc) => Explicit: sql_impl_cast(&STRING_TO_REGPROC),
643            (String, RegType) => Explicit: sql_impl_cast(&STRING_TO_REGTYPE),
644
645            (String, Float32) => Explicit: CastStringToFloat32(func::CastStringToFloat32),
646            (String, Float64) => Explicit: CastStringToFloat64(func::CastStringToFloat64),
647            (String, Numeric) => Explicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
648                let s = to_type.unwrap_numeric_max_scale();
649                let f = CastStringToNumeric(func::CastStringToNumeric(s));
650                Some(move |e: HirScalarExpr| e.call_unary(f))
651            }),
652            (String, Date) => Explicit: CastStringToDate(func::CastStringToDate),
653            (String, Time) => Explicit: CastStringToTime(func::CastStringToTime),
654            (String, Timestamp) => Explicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
655                let p = to_type.unwrap_timestamp_precision();
656                let f = CastStringToTimestamp(func::CastStringToTimestamp(p));
657                Some(move |e: HirScalarExpr| e.call_unary(f))
658            }),
659            (String, TimestampTz) => Explicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
660                let p = to_type.unwrap_timestamp_precision();
661                let f = CastStringToTimestampTz(
662                    func::CastStringToTimestampTz(p),
663                );
664                Some(move |e: HirScalarExpr| e.call_unary(f))
665            }),
666            (String, Interval) => Explicit: CastStringToInterval(func::CastStringToInterval),
667            (String, Bytes) => Explicit: CastStringToBytes(func::CastStringToBytes),
668            (String, Jsonb) => Explicit: CastStringToJsonb(func::CastStringToJsonb),
669            (String, Uuid) => Explicit: CastStringToUuid(func::CastStringToUuid),
670            (String, Array) => Explicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
671                let return_ty = to_type.clone();
672                let to_el_type = to_type.unwrap_array_element_type();
673                let cast_expr = plan_hypothetical_cast(ecx, ccx, from_type, to_el_type)?;
674                Some(|e: HirScalarExpr| {
675                    e.call_unary(UnaryFunc::CastStringToArray(
676                        func::CastStringToArray {
677                            return_ty,
678                            cast_expr: Box::new(cast_expr),
679                        },
680                    ))
681                })
682            }),
683            (String, List) => Explicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
684                let return_ty = to_type.clone();
685                let to_el_type = to_type.unwrap_list_element_type();
686                let cast_expr = plan_hypothetical_cast(ecx, ccx, from_type, to_el_type)?;
687                Some(|e: HirScalarExpr| {
688                    e.call_unary(UnaryFunc::CastStringToList(
689                        func::CastStringToList {
690                            return_ty,
691                            cast_expr: Box::new(cast_expr),
692                        },
693                    ))
694                })
695            }),
696            (String, Map) => Explicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
697                let return_ty = to_type.clone();
698                let to_val_type = to_type.unwrap_map_value_type();
699                let cast_expr = plan_hypothetical_cast(ecx, ccx, from_type, to_val_type)?;
700                Some(|e: HirScalarExpr| {
701                    e.call_unary(UnaryFunc::CastStringToMap(
702                        func::CastStringToMap {
703                            return_ty,
704                            cast_expr: Box::new(cast_expr),
705                        },
706                    ))
707                })
708            }),
709            (String, Range) => Explicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
710                let return_ty = to_type.clone();
711                let to_el_type = to_type.unwrap_range_element_type();
712                let cast_expr = plan_hypothetical_cast(ecx, ccx, from_type, to_el_type)?;
713                Some(|e: HirScalarExpr| {
714                    e.call_unary(UnaryFunc::CastStringToRange(
715                        func::CastStringToRange {
716                            return_ty,
717                            cast_expr: Box::new(cast_expr),
718                        },
719                    ))
720                })
721            }),
722            (String, Int2Vector) => Explicit: CastStringToInt2Vector(func::CastStringToInt2Vector),
723            (String, Char) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
724                let length = to_type.unwrap_char_length();
725                let fail_on_len = ccx != CastContext::Explicit;
726                let f = CastStringToChar(func::CastStringToChar {
727                    length, fail_on_len,
728                });
729                Some(move |e: HirScalarExpr| e.call_unary(f))
730            }),
731            (String, VarChar) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
732                let length = to_type.unwrap_varchar_max_length();
733                let fail_on_len = ccx != CastContext::Explicit;
734                let f = CastStringToVarChar(func::CastStringToVarChar {
735                    length, fail_on_len,
736                });
737                Some(move |e: HirScalarExpr| e.call_unary(f))
738            }),
739            (String, PgLegacyChar) => Assignment:
740                CastStringToPgLegacyChar(func::CastStringToPgLegacyChar),
741            // CHAR
742            (Char, String) => Implicit: CastCharToString(func::CastCharToString),
743            (Char, Char) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
744                let length = to_type.unwrap_char_length();
745                let fail_on_len = ccx != CastContext::Explicit;
746                let f = CastStringToChar(func::CastStringToChar {
747                    length, fail_on_len,
748                });
749                Some(move |e: HirScalarExpr| e.call_unary(f))
750            }),
751            (Char, VarChar) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
752                let length = to_type.unwrap_varchar_max_length();
753                let fail_on_len = ccx != CastContext::Explicit;
754                let f = CastStringToVarChar(func::CastStringToVarChar {
755                    length, fail_on_len,
756                });
757                Some(move |e: HirScalarExpr| e.call_unary(f))
758            }),
759            (Char, PgLegacyChar) => Assignment:
760                CastStringToPgLegacyChar(func::CastStringToPgLegacyChar),
761
762            // VARCHAR
763            (VarChar, String) => Implicit: CastVarCharToString(func::CastVarCharToString),
764            (VarChar, Char) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
765                let length = to_type.unwrap_char_length();
766                let fail_on_len = ccx != CastContext::Explicit;
767                let f = CastStringToChar(func::CastStringToChar {
768                    length, fail_on_len,
769                });
770                Some(move |e: HirScalarExpr| e.call_unary(f))
771            }),
772            (VarChar, VarChar) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
773                let length = to_type.unwrap_varchar_max_length();
774                let fail_on_len = ccx != CastContext::Explicit;
775                let f = CastStringToVarChar(func::CastStringToVarChar {
776                    length, fail_on_len,
777                });
778                Some(move |e: HirScalarExpr| e.call_unary(f))
779            }),
780            (VarChar, PgLegacyChar) => Assignment:
781                CastStringToPgLegacyChar(func::CastStringToPgLegacyChar),
782
783            // PG LEGACY CHAR
784            (PgLegacyChar, String) => Implicit:
785                CastPgLegacyCharToString(func::CastPgLegacyCharToString),
786            (PgLegacyChar, Char) => Assignment:
787                CastPgLegacyCharToChar(func::CastPgLegacyCharToChar),
788            (PgLegacyChar, VarChar) => Assignment:
789                CastPgLegacyCharToVarChar(func::CastPgLegacyCharToVarChar),
790            (PgLegacyChar, Int32) => Explicit:
791                CastPgLegacyCharToInt32(func::CastPgLegacyCharToInt32),
792
793            // PG LEGACY NAME
794            // Under the hood VarChars and Name's are just Strings, so we
795            // can re-use existing methods
796            // on Strings and VarChars instead of defining new ones.
797            (PgLegacyName, String) => Implicit: CastVarCharToString(func::CastVarCharToString),
798            (PgLegacyName, Char) => Assignment: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
799                let length = to_type.unwrap_char_length();
800                let fail_on_len = ccx != CastContext::Explicit;
801                let f = CastStringToChar(func::CastStringToChar {
802                    length, fail_on_len,
803                });
804                Some(move |e: HirScalarExpr| e.call_unary(f))
805            }),
806            (PgLegacyName, VarChar) => Assignment: CastTemplate::new(
807                |_ecx, ccx, _from_type, to_type|
808            {
809                let length = to_type.unwrap_varchar_max_length();
810                let fail_on_len = ccx != CastContext::Explicit;
811                let f = CastStringToVarChar(func::CastStringToVarChar {
812                    length, fail_on_len,
813                });
814                Some(move |e: HirScalarExpr| e.call_unary(f))
815            }),
816            (String, PgLegacyName) => Implicit:
817                CastStringToPgLegacyName(func::CastStringToPgLegacyName),
818            (Char, PgLegacyName) => Implicit:
819                CastStringToPgLegacyName(func::CastStringToPgLegacyName),
820            (VarChar, PgLegacyName) => Implicit:
821                CastStringToPgLegacyName(func::CastStringToPgLegacyName),
822
823            // RECORD
824            (Record, String) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, _to_type| {
825                let ty = from_type.clone();
826                Some(|e: HirScalarExpr| {
827                    e.call_unary(CastRecordToString(
828                        func::CastRecordToString { ty },
829                    ))
830                })
831            }),
832            (Record, Record) => Implicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
833                let from_fields = from_type.unwrap_record_element_type();
834                let to_fields = to_type.unwrap_record_element_type();
835                if from_fields.len() != to_fields.len() {
836                    return None;
837                }
838
839                if let (
840                    l @ SqlScalarType::Record {
841                        custom_id: Some(..), ..
842                    },
843                    r,
844                ) = (from_type, to_type)
845                {
846                    // Changing `from`'s custom_id requires at least
847                    // Assignment context
848                    if ccx == CastContext::Implicit && l != r {
849                        return None;
850                    }
851                }
852
853                let cast_exprs = from_fields
854                    .iter()
855                    .zip_eq(to_fields)
856                    .map(|(f, t)| plan_hypothetical_cast(ecx, ccx, f, t))
857                    .collect::<Option<Box<_>>>()?;
858                let to = to_type.clone();
859                Some(|e: HirScalarExpr| {
860                    e.call_unary(CastRecord1ToRecord2(
861                        func::CastRecord1ToRecord2 {
862                            return_ty: to,
863                            cast_exprs,
864                        },
865                    ))
866                })
867            }),
868
869            // ARRAY
870            (Array, String) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, _to_type| {
871                let ty = from_type.clone();
872                Some(|e: HirScalarExpr| {
873                    e.call_unary(CastArrayToString(
874                        func::CastArrayToString { ty },
875                    ))
876                })
877            }),
878            (Array, List) => Explicit: CastArrayToListOneDim(func::CastArrayToListOneDim),
879            (Array, Array) => Explicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
880                let inner_from_type = from_type.unwrap_array_element_type();
881                let inner_to_type = to_type.unwrap_array_element_type();
882                let cast_expr = plan_hypothetical_cast(
883                    ecx, ccx, inner_from_type, inner_to_type,
884                )?;
885                let return_ty = to_type.clone();
886
887                Some(move |e: HirScalarExpr| {
888                    e.call_unary(CastArrayToArray(func::CastArrayToArray {
889                        return_ty,
890                        cast_expr: Box::new(cast_expr),
891                    }))
892                })
893            }),
894
895            // INT2VECTOR
896            (Int2Vector, Array) => Implicit: CastTemplate::new(|ecx, ccx, _from_type, to_type| {
897                let inner_to_type = to_type.unwrap_array_element_type();
898                // Int2Vector elements are Int16, so if the target element type
899                // differs we need to also cast the array elements after the
900                // Int2Vector -> Array(Int16) conversion. (Postgres seems to do the same.)
901                let element_cast = if inner_to_type != &SqlScalarType::Int16 {
902                    let cast_expr = plan_hypothetical_cast(
903                        ecx, ccx, &SqlScalarType::Int16, inner_to_type
904                    )?;
905                    Some((to_type.clone(), cast_expr))
906                } else {
907                    None
908                };
909                Some(move |e: HirScalarExpr| {
910                    let arr = e.call_unary(
911                        UnaryFunc::CastInt2VectorToArray(func::CastInt2VectorToArray)
912                    );
913                    match element_cast {
914                        Some((return_ty, cast_expr)) => {
915                            arr.call_unary(CastArrayToArray(
916                                func::CastArrayToArray { return_ty, cast_expr: Box::new(cast_expr) }
917                            ))
918                        }
919                        None => arr,
920                    }
921                })
922            }),
923            (Int2Vector, String) => Explicit: CastInt2VectorToString(func::CastInt2VectorToString),
924
925            // LIST
926            (List, String) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, _to_type| {
927                let ty = from_type.clone();
928                Some(|e: HirScalarExpr| {
929                    e.call_unary(CastListToString(
930                        func::CastListToString { ty },
931                    ))
932                })
933            }),
934            (List, List) => Implicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
935
936                if let (
937                    l @ SqlScalarType::List {
938                        custom_id: Some(..), ..
939                    },
940                    r,
941                ) = (from_type, to_type)
942                {
943                    // Changing `from`'s custom_id requires at least
944                    // Assignment context
945                    if ccx == CastContext::Implicit && !l.base_eq(r) {
946                        return None;
947                    }
948                }
949
950                let return_ty = to_type.clone();
951                let from_el_type = from_type.unwrap_list_element_type();
952                let to_el_type = to_type.unwrap_list_element_type();
953                let cast_expr = plan_hypothetical_cast(
954                    ecx, ccx, from_el_type, to_el_type,
955                )?;
956                Some(|e: HirScalarExpr| {
957                    e.call_unary(UnaryFunc::CastList1ToList2(
958                        func::CastList1ToList2 {
959                            return_ty,
960                            cast_expr: Box::new(cast_expr),
961                        },
962                    ))
963                })
964            }),
965
966            // MAP
967            (Map, String) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, _to_type| {
968                let ty = from_type.clone();
969                Some(|e: HirScalarExpr| e.call_unary(CastMapToString(func::CastMapToString { ty })))
970            }),
971
972            // JSONB
973            (Jsonb, Bool) => Explicit: CastJsonbToBool(func::CastJsonbToBool),
974            (Jsonb, Int16) => Explicit: CastJsonbToInt16(func::CastJsonbToInt16),
975            (Jsonb, Int32) => Explicit: CastJsonbToInt32(func::CastJsonbToInt32),
976            (Jsonb, Int64) => Explicit: CastJsonbToInt64(func::CastJsonbToInt64),
977            (Jsonb, Float32) => Explicit: CastJsonbToFloat32(func::CastJsonbToFloat32),
978            (Jsonb, Float64) => Explicit: CastJsonbToFloat64(func::CastJsonbToFloat64),
979            (Jsonb, Numeric) => Explicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
980                let s = to_type.unwrap_numeric_max_scale();
981                let f = CastJsonbToNumeric(func::CastJsonbToNumeric(s));
982                Some(move |e: HirScalarExpr| e.call_unary(f))
983            }),
984            (Jsonb, String) => Assignment: CastJsonbToString(func::CastJsonbToString),
985
986            // UUID
987            (Uuid, String) => Assignment: CastUuidToString(func::CastUuidToString),
988
989            // Numeric
990            (Numeric, Numeric) => Assignment: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
991                let scale = to_type.unwrap_numeric_max_scale();
992                Some(move |e: HirScalarExpr| match scale {
993                    None => e,
994                    Some(scale) => e.call_unary(
995                        UnaryFunc::AdjustNumericScale(
996                            func::AdjustNumericScale(scale),
997                        ),
998                    ),
999                })
1000            }),
1001            (Numeric, Float32) => Implicit: CastNumericToFloat32(func::CastNumericToFloat32),
1002            (Numeric, Float64) => Implicit: CastNumericToFloat64(func::CastNumericToFloat64),
1003            (Numeric, Int16) => Assignment: CastNumericToInt16(func::CastNumericToInt16),
1004            (Numeric, Int32) => Assignment: CastNumericToInt32(func::CastNumericToInt32),
1005            (Numeric, Int64) => Assignment: CastNumericToInt64(func::CastNumericToInt64),
1006            (Numeric, UInt16) => Assignment: CastNumericToUint16(func::CastNumericToUint16),
1007            (Numeric, UInt32) => Assignment: CastNumericToUint32(func::CastNumericToUint32),
1008            (Numeric, UInt64) => Assignment: CastNumericToUint64(func::CastNumericToUint64),
1009            (Numeric, String) => Assignment: CastNumericToString(func::CastNumericToString),
1010
1011            // Range
1012            (Range, String) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, _to_type| {
1013                let ty = from_type.clone();
1014                Some(|e: HirScalarExpr| {
1015                    e.call_unary(CastRangeToString(
1016                        func::CastRangeToString { ty },
1017                    ))
1018                })
1019            }),
1020
1021            // MzAclItem
1022            (MzAclItem, String) => Explicit: sql_impl_cast("(
1023                SELECT
1024                    (CASE
1025                        WHEN grantee_role_id = 'p' THEN ''
1026                        ELSE COALESCE(grantee_role.name, grantee_role_id)
1027                    END)
1028                    || '='
1029                    || mz_internal.mz_aclitem_privileges($1)
1030                    || '/'
1031                    || COALESCE(grantor_role.name, grantor_role_id)
1032                FROM
1033                    (SELECT mz_internal.mz_aclitem_grantee($1) AS grantee_role_id),
1034                    (SELECT mz_internal.mz_aclitem_grantor($1) AS grantor_role_id)
1035                LEFT JOIN mz_catalog.mz_roles AS grantee_role ON grantee_role_id = grantee_role.id
1036                LEFT JOIN mz_catalog.mz_roles AS grantor_role ON grantor_role_id = grantor_role.id
1037            )"),
1038            (MzAclItem, AclItem) => Explicit: sql_impl_cast("(
1039                SELECT makeaclitem(
1040                    (CASE mz_internal.mz_aclitem_grantee($1)
1041                        WHEN 'p' THEN 0
1042                        ELSE (SELECT oid FROM mz_catalog.mz_roles
1043                            WHERE id = mz_internal.mz_aclitem_grantee($1))
1044                    END),
1045                    (SELECT oid FROM mz_catalog.mz_roles
1046                        WHERE id = mz_internal.mz_aclitem_grantor($1)),
1047                    (SELECT array_to_string(
1048                        mz_internal.mz_format_privileges(
1049                            mz_internal.mz_aclitem_privileges($1)
1050                        ), ',')),
1051                    -- GRANT OPTION isn't implemented so we hardcode false.
1052                    false
1053                )
1054            )"),
1055
1056            // AclItem
1057            (AclItem, String) => Explicit: sql_impl_cast("(
1058                SELECT
1059                    (CASE grantee_oid
1060                        WHEN 0 THEN ''
1061                        ELSE COALESCE(grantee_role.name, grantee_oid::text)
1062                    END)
1063                    || '='
1064                    || mz_internal.aclitem_privileges($1)
1065                    || '/'
1066                    || COALESCE(grantor_role.name, grantor_oid::text)
1067                FROM
1068                    (SELECT mz_internal.aclitem_grantee($1) AS grantee_oid),
1069                    (SELECT mz_internal.aclitem_grantor($1) AS grantor_oid)
1070                LEFT JOIN mz_catalog.mz_roles AS grantee_role ON grantee_oid = grantee_role.oid
1071                LEFT JOIN mz_catalog.mz_roles AS grantor_role ON grantor_oid = grantor_role.oid
1072            )"),
1073            (AclItem, MzAclItem) => Explicit: sql_impl_cast("(
1074                SELECT mz_internal.make_mz_aclitem(
1075                    (CASE mz_internal.aclitem_grantee($1)
1076                        WHEN 0 THEN 'p'
1077                        ELSE (SELECT id FROM mz_catalog.mz_roles
1078                            WHERE oid = mz_internal.aclitem_grantee($1))
1079                    END),
1080                    (SELECT id FROM mz_catalog.mz_roles
1081                        WHERE oid = mz_internal.aclitem_grantor($1)),
1082                    (SELECT array_to_string(
1083                        mz_internal.mz_format_privileges(
1084                            mz_internal.aclitem_privileges($1)
1085                        ), ','))
1086                )
1087            )")
1088        }
1089    });
1090
1091/// Error returned when a cast is not available or not allowed.
1092#[derive(Debug)]
1093pub enum CastError {
1094    /// No cast exists between the given types (or not in this cast context).
1095    InvalidCast {
1096        ccx: CastContext,
1097        from: String,
1098        to: String,
1099    },
1100    /// Cast would apply but is disallowed (e.g. range over an unsupported element type).
1101    UnsupportedRangeElementType { element_type_name: String },
1102}
1103
1104impl CastError {
1105    /// Convert to a [`PlanError`] for use in planning; requires the expression name for context.
1106    pub fn into_plan_error(self, name: String) -> PlanError {
1107        match self {
1108            CastError::InvalidCast { ccx, from, to } => PlanError::InvalidCast {
1109                name,
1110                ccx,
1111                from,
1112                to,
1113            },
1114            CastError::UnsupportedRangeElementType { element_type_name } => {
1115                PlanError::UnsupportedRangeElementType { element_type_name }
1116            }
1117        }
1118    }
1119}
1120
1121/// Get casts directly between two [`SqlScalarType`]s, with control over the
1122/// allowed [`CastContext`].
1123///
1124/// Returns `Err` when the cast is not supported: either no cast exists
1125/// ([`CastError::InvalidCast`]) or the cast is disallowed
1126/// ([`CastError::UnsupportedRangeElementType`], e.g. range over float/uint).
1127fn get_cast(
1128    ecx: &ExprContext,
1129    ccx: CastContext,
1130    from: &SqlScalarType,
1131    to: &SqlScalarType,
1132) -> Result<Cast, CastError> {
1133    use CastContext::*;
1134
1135    if from == to || (ccx == Implicit && from.base_eq(to)) {
1136        return Ok(Box::new(|expr| expr));
1137    }
1138
1139    // Reject casts to range types with unsupported element types at plan time.
1140    if let SqlScalarType::Range { element_type } = to {
1141        validate_range_element_type(ecx, element_type)?;
1142    }
1143
1144    let imp = match VALID_CASTS.get(&(from.into(), to.into())) {
1145        Some(imp) => imp,
1146        None => {
1147            return Err(CastError::InvalidCast {
1148                ccx,
1149                from: ecx.humanize_sql_scalar_type(from, false),
1150                to: ecx.humanize_sql_scalar_type(to, false),
1151            });
1152        }
1153    };
1154    let template = if ccx >= imp.context {
1155        Some(&imp.template)
1156    } else {
1157        None
1158    };
1159    match template.and_then(|template| (template.0)(ecx, ccx, from, to)) {
1160        Some(cast) => Ok(cast),
1161        None => Err(CastError::InvalidCast {
1162            ccx,
1163            from: ecx.humanize_sql_scalar_type(from, false),
1164            to: ecx.humanize_sql_scalar_type(to, false),
1165        }),
1166    }
1167}
1168
1169/// Converts an expression to `SqlScalarType::String`.
1170///
1171/// Most types have an explicit cast to string, but a few do not: `mz_aclitem`
1172/// has no text cast at all, and the `reg*` casts are implemented as subqueries
1173/// that `plan_cast` rejects in contexts that disallow subqueries (e.g. a
1174/// `RETURNING` clause or a `to_jsonb` element cast). We surface those as a
1175/// planning error rather than panicking.
1176pub fn to_string(ecx: &ExprContext, expr: HirScalarExpr) -> Result<HirScalarExpr, PlanError> {
1177    plan_cast(ecx, CastContext::Explicit, expr, &SqlScalarType::String)
1178}
1179
1180/// Converts an expression to `SqlScalarType::Jsonb`.
1181///
1182/// The rules are as follows:
1183///   * `SqlScalarType::Boolean`s become JSON booleans.
1184///   * All numeric types are converted to `Float64`s, then become JSON numbers.
1185///   * Records are converted to a JSON object where the record's field names
1186///     are the keys of the object, and the record's fields are recursively
1187///     converted to JSON by `to_jsonb`.
1188///   * Other types are converted to strings by their usual cast function and
1189///     become JSON strings. That cast can fail (see `to_string`), in which case
1190///     we propagate the planning error instead of panicking.
1191pub fn to_jsonb(ecx: &ExprContext, expr: HirScalarExpr) -> Result<HirScalarExpr, PlanError> {
1192    use SqlScalarType::*;
1193
1194    Ok(match ecx.scalar_type(&expr) {
1195        Bool | Jsonb | Numeric { .. } => {
1196            expr.call_unary(UnaryFunc::CastJsonbableToJsonb(func::CastJsonbableToJsonb))
1197        }
1198        Int16 | Int32 | Int64 | UInt16 | UInt32 | UInt64 | Float32 | Float64 => plan_cast(
1199            ecx,
1200            CastContext::Explicit,
1201            expr,
1202            &Numeric { max_scale: None },
1203        )
1204        .expect("cast known to exist")
1205        .call_unary(UnaryFunc::CastJsonbableToJsonb(func::CastJsonbableToJsonb)),
1206        Record { fields, .. } => {
1207            let mut exprs = vec![];
1208            for (i, (name, _ty)) in fields.iter().enumerate() {
1209                exprs.push(HirScalarExpr::literal(
1210                    Datum::String(name),
1211                    SqlScalarType::String,
1212                ));
1213                exprs.push(to_jsonb(
1214                    ecx,
1215                    expr.clone()
1216                        .call_unary(UnaryFunc::RecordGet(func::RecordGet(i))),
1217                )?);
1218            }
1219            HirScalarExpr::call_variadic(JsonbBuildObject, exprs)
1220        }
1221        ref ty @ List {
1222            ref element_type, ..
1223        }
1224        | ref ty @ Array(ref element_type) => {
1225            // Construct a new expression context with one column whose type
1226            // is the container's element type.
1227            let qcx = QueryContext::root(ecx.qcx.scx, ecx.qcx.lifetime);
1228            let ecx = ExprContext {
1229                qcx: &qcx,
1230                name: "to_jsonb",
1231                scope: &Scope::empty(),
1232                relation_type: &SqlRelationType::new(vec![element_type.clone().nullable(true)]),
1233                allow_aggregates: false,
1234                allow_subqueries: false,
1235                allow_parameters: false,
1236                allow_windows: false,
1237            };
1238
1239            // Create an element-casting expression by calling `to_jsonb` on
1240            // an expression that references the first column in a row.
1241            let cast_element = to_jsonb(&ecx, HirScalarExpr::column(0))?;
1242            let cast_element = cast_element
1243                .lower_uncorrelated(ecx.catalog().system_vars())
1244                .expect("to_jsonb does not produce correlated expressions on uncorrelated input");
1245
1246            // The `Cast{Array|List}ToJsonb` functions take the element-casting
1247            // expression as an argument and evaluate the expression against
1248            // each element of the container at runtime.
1249            let func = match ty {
1250                List { .. } => UnaryFunc::CastListToJsonb(CastListToJsonb {
1251                    cast_element: Box::new(cast_element),
1252                }),
1253                Array { .. } => UnaryFunc::CastArrayToJsonb(CastArrayToJsonb {
1254                    cast_element: Box::new(cast_element),
1255                }),
1256                _ => unreachable!("validated above"),
1257            };
1258
1259            expr.call_unary(func)
1260        }
1261        Date
1262        | Time
1263        | Timestamp { .. }
1264        | TimestampTz { .. }
1265        | Interval
1266        | PgLegacyChar
1267        | PgLegacyName
1268        | Bytes
1269        | String
1270        | Char { .. }
1271        | VarChar { .. }
1272        | Uuid
1273        | Oid
1274        | Map { .. }
1275        | RegProc
1276        | RegType
1277        | RegClass
1278        | Int2Vector
1279        | MzTimestamp
1280        | Range { .. }
1281        | MzAclItem
1282        | AclItem => to_string(ecx, expr)?
1283            .call_unary(UnaryFunc::CastJsonbableToJsonb(func::CastJsonbableToJsonb)),
1284    })
1285}
1286
1287/// Guesses the most-common type among a set of [`SqlScalarType`]s that all members
1288/// can be cast to. Returns `None` if a common type cannot be deduced.
1289///
1290/// Note that this function implements the type-determination components of
1291/// Postgres' ["`UNION`, `CASE`, and Related Constructs"][union-type-conv] type
1292/// conversion.
1293///
1294/// [union-type-conv]: https://www.postgresql.org/docs/12/typeconv-union-case.html
1295pub fn guess_best_common_type(
1296    ecx: &ExprContext,
1297    types: &[CoercibleScalarType],
1298) -> Result<SqlScalarType, PlanError> {
1299    // This function is a translation of `select_common_type` in PostgreSQL with
1300    // the addition of our near match logic, which supports Materialize
1301    // non-linear type promotions.
1302    // https://github.com/postgres/postgres/blob/d1b307eef/
1303    //   src/backend/parser/parse_coerce.c#L1288-L1308
1304
1305    // If every type is a literal record with the same number of fields, the
1306    // best common type is a record with that number of fields. We recursively
1307    // guess the best type for each field.
1308    if let Some(CoercibleScalarType::Record(field_tys)) = types.first() {
1309        if types
1310            .iter()
1311            .all(|t| matches!(t, CoercibleScalarType::Record(fts) if field_tys.len() == fts.len()))
1312        {
1313            let mut fields = vec![];
1314            for i in 0..field_tys.len() {
1315                let name = ColumnName::from(format!("f{}", fields.len() + 1));
1316                let mut guesses = vec![];
1317                let mut nullable = false;
1318                for ty in types {
1319                    let field_ty = match ty {
1320                        CoercibleScalarType::Record(fts) => fts[i].clone(),
1321                        _ => unreachable!(),
1322                    };
1323                    if field_ty.nullable() {
1324                        nullable = true;
1325                    }
1326                    guesses.push(field_ty.scalar_type());
1327                }
1328                let guess = guess_best_common_type(ecx, &guesses)?;
1329                fields.push((name, guess.nullable(nullable)));
1330            }
1331            return Ok(SqlScalarType::Record {
1332                fields: fields.into(),
1333                custom_id: None,
1334            });
1335        }
1336    }
1337
1338    // Remove unknown types, and collect them.
1339    let mut types: Vec<_> = types.into_iter().filter_map(|v| v.as_coerced()).collect();
1340
1341    // In the case of mixed ints and uints, replace uints with their near match
1342    let contains_int = types.iter().any(|t| {
1343        matches!(
1344            t,
1345            SqlScalarType::Int16 | SqlScalarType::Int32 | SqlScalarType::Int64
1346        )
1347    });
1348
1349    for t in types.iter_mut() {
1350        if contains_int
1351            && matches!(
1352                t,
1353                SqlScalarType::UInt16 | SqlScalarType::UInt32 | SqlScalarType::UInt64
1354            )
1355        {
1356            *t = t.near_match().expect("unsigned ints have near matches")
1357        }
1358    }
1359
1360    let mut types = types.iter();
1361
1362    let mut candidate = match types.next() {
1363        // If no known types, fall back to `String`.
1364        None => return Ok(SqlScalarType::String),
1365        // Start by guessing the first type.
1366        Some(t) => t,
1367    };
1368
1369    let preferred_type = TypeCategory::from_type(candidate).preferred_type();
1370
1371    for typ in types {
1372        if TypeCategory::from_type(candidate) != TypeCategory::from_type(typ) {
1373            // The next type is in a different category; give up.
1374            sql_bail!(
1375                "{} types {} and {} cannot be matched",
1376                ecx.name,
1377                ecx.humanize_sql_scalar_type(candidate, false),
1378                ecx.humanize_sql_scalar_type(typ, false),
1379            );
1380        };
1381
1382        // If this type is the preferred type, make it the candidate.
1383        if preferred_type.as_ref() != Some(candidate)
1384            && can_cast(ecx, CastContext::Implicit, candidate, typ)
1385            && !can_cast(ecx, CastContext::Implicit, typ, candidate)
1386        {
1387            // The current candidate is not the preferred type for its category
1388            // and the next type is implicitly convertible to the current
1389            // candidate, but not vice-versa, so take the next type as the new
1390            // candidate.
1391            candidate = typ;
1392        }
1393    }
1394    Ok(candidate.without_modifiers())
1395}
1396
1397pub fn plan_coerce<'a>(
1398    ecx: &'a ExprContext,
1399    e: CoercibleScalarExpr,
1400    coerce_to: &SqlScalarType,
1401) -> Result<HirScalarExpr, PlanError> {
1402    use CoercibleScalarExpr::*;
1403
1404    Ok(match e {
1405        Coerced(e) => e,
1406
1407        LiteralNull => HirScalarExpr::literal_null(coerce_to.clone()),
1408
1409        LiteralString(s) => {
1410            let lit = HirScalarExpr::literal(Datum::String(&s), SqlScalarType::String);
1411            // Per PostgreSQL, string literal explicitly casts to the base type.
1412            // The caller is responsible for applying any desired modifiers
1413            // (with either implicit or explicit semantics) via a separate call
1414            // to `plan_cast`.
1415            let coerce_to_base = &coerce_to.without_modifiers();
1416            plan_cast(ecx, CastContext::Coerced, lit, coerce_to_base)?
1417        }
1418
1419        LiteralRecord(exprs) => {
1420            let arity = exprs.len();
1421            let coercions = match coerce_to {
1422                SqlScalarType::Record { fields, .. } if fields.len() == arity => fields
1423                    .iter()
1424                    .map(|(_name, ty)| &ty.scalar_type)
1425                    .cloned()
1426                    .collect(),
1427                _ => vec![SqlScalarType::String; exprs.len()],
1428            };
1429            let mut out = vec![];
1430            for (e, coerce_to) in exprs.into_iter().zip_eq(coercions) {
1431                out.push(plan_coerce(ecx, e, &coerce_to)?);
1432            }
1433            HirScalarExpr::call_variadic(
1434                RecordCreate {
1435                    field_names: (0..arity)
1436                        .map(|i| ColumnName::from(format!("f{}", i + 1)))
1437                        .collect(),
1438                },
1439                out,
1440            )
1441        }
1442
1443        Parameter(n) => {
1444            let prev = ecx.param_types().borrow_mut().insert(n, coerce_to.clone());
1445            if let Some(prev) = prev {
1446                if prev != *coerce_to {
1447                    sql_bail!(
1448                        "there are contradicting constraints for \
1449                         the type of parameter ${}: \
1450                         should be both {} and {}",
1451                        n,
1452                        ecx.humanize_sql_scalar_type(&prev, false),
1453                        ecx.humanize_sql_scalar_type(coerce_to, false),
1454                    );
1455                }
1456            }
1457            HirScalarExpr::parameter(n)
1458        }
1459    })
1460}
1461
1462/// Returns an error if the given type is not a supported range element type
1463/// (PostgreSQL built-in range types: int32, int64, date, numeric, timestamp,
1464/// timestamptz).
1465fn validate_range_element_type(
1466    ecx: &ExprContext,
1467    element_type: &SqlScalarType,
1468) -> Result<(), CastError> {
1469    let allowed = matches!(
1470        element_type,
1471        SqlScalarType::Int32
1472            | SqlScalarType::Int64
1473            | SqlScalarType::Date
1474            | SqlScalarType::Numeric { .. }
1475            | SqlScalarType::Timestamp { .. }
1476            | SqlScalarType::TimestampTz { .. }
1477    );
1478    if allowed {
1479        Ok(())
1480    } else {
1481        Err(CastError::UnsupportedRangeElementType {
1482            element_type_name: ecx.humanize_sql_scalar_type(element_type, false),
1483        })
1484    }
1485}
1486
1487/// Similar to `plan_cast`, but for situations where you only know the type of
1488/// the input expression (`from`) and not the expression itself. The returned
1489/// expression refers to the first column of some imaginary row, where the first
1490/// column is assumed to have type `from`.
1491///
1492/// If casting from `from` to `to` is not possible, returns `None`.
1493pub fn plan_hypothetical_cast(
1494    ecx: &ExprContext,
1495    ccx: CastContext,
1496    from: &SqlScalarType,
1497    to: &SqlScalarType,
1498) -> Option<mz_expr::MirScalarExpr> {
1499    // Reconstruct an expression context where the expression is evaluated on
1500    // the "first column" of some imaginary row.
1501    let mut scx = ecx.qcx.scx.clone();
1502    scx.param_types = RefCell::new(BTreeMap::new());
1503    let qcx = QueryContext::root(&scx, ecx.qcx.lifetime);
1504    let relation_type = SqlRelationType {
1505        column_types: vec![SqlColumnType {
1506            nullable: true,
1507            scalar_type: from.clone(),
1508        }],
1509        keys: vec![vec![0]],
1510    };
1511    let ecx = ExprContext {
1512        qcx: &qcx,
1513        name: "plan_hypothetical_cast",
1514        scope: &Scope::empty(),
1515        relation_type: &relation_type,
1516        allow_aggregates: false,
1517        allow_subqueries: true,
1518        allow_parameters: true,
1519        allow_windows: false,
1520    };
1521
1522    let col_expr = HirScalarExpr::column(0);
1523
1524    // Determine the `ScalarExpr` required to cast our column to the target
1525    // component type.
1526    plan_cast(&ecx, ccx, col_expr, to)
1527        .ok()?
1528        // TODO(jkosh44) Support casts that have correlated implementations.
1529        .lower_uncorrelated(ecx.catalog().system_vars())
1530        .ok()
1531}
1532
1533/// Plans a cast between [`SqlScalarType`]s, specifying which types of casts are
1534/// permitted using [`CastContext`].
1535///
1536/// # Errors
1537///
1538/// If a cast between the `ScalarExpr`'s base type and the specified type is:
1539/// - Not possible, e.g. `Bytes` to `Interval`
1540/// - Not permitted, e.g. implicitly casting from `Float64` to `Float32`.
1541/// - Not implemented yet
1542pub fn plan_cast(
1543    ecx: &ExprContext,
1544    ccx: CastContext,
1545    expr: HirScalarExpr,
1546    to: &SqlScalarType,
1547) -> Result<HirScalarExpr, PlanError> {
1548    let from = ecx.scalar_type(&expr);
1549
1550    // Close over `ccx`, `from`, and `to` to simplify error messages in the
1551    // face of intermediate expressions.
1552    let cast_inner = |from, to, expr| {
1553        get_cast(ecx, ccx, from, to)
1554            .map(|cast| cast(expr))
1555            .map_err(|e| e.into_plan_error(ecx.name.into()))
1556    };
1557
1558    // Get cast which might include parameter rewrites + generating intermediate
1559    // expressions.
1560    //
1561    // String-like types get special handling to match PostgreSQL.
1562    // See: https://github.com/postgres/postgres/blob/6b04abdfc/
1563    //   src/backend/parser/parse_coerce.c#L3205-L3223
1564    let from_category = TypeCategory::from_type(&from);
1565    let to_category = TypeCategory::from_type(to);
1566    if from_category == TypeCategory::String && to_category != TypeCategory::String {
1567        // Converting from stringlike to something non-stringlike. Handle as if
1568        // `from` were a `SqlScalarType::String.
1569        cast_inner(&SqlScalarType::String, to, expr)
1570    } else if from_category != TypeCategory::String && to_category == TypeCategory::String {
1571        // Converting from non-stringlike to something stringlike. Convert to a
1572        // `SqlScalarType::String` and then to the desired type.
1573        let expr = cast_inner(&from, &SqlScalarType::String, expr)?;
1574        cast_inner(&SqlScalarType::String, to, expr)
1575    } else {
1576        // Standard cast.
1577        cast_inner(&from, to, expr)
1578    }
1579}
1580
1581/// Reports whether it is possible to perform a cast from the specified types.
1582pub fn can_cast(
1583    ecx: &ExprContext,
1584    ccx: CastContext,
1585    cast_from: &SqlScalarType,
1586    cast_to: &SqlScalarType,
1587) -> bool {
1588    get_cast(ecx, ccx, cast_from, cast_to).is_ok()
1589}