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