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