1use 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_ore::soft_panic_or_log;
23use mz_repr::{
24 ColumnName, Datum, Row, SqlColumnType, SqlRelationType, SqlScalarBaseType, SqlScalarType,
25};
26use mz_sql_parser::ast::CastFailureMode;
27
28use crate::catalog::TypeCategory;
29use crate::plan::error::PlanError;
30use crate::plan::hir::{
31 AbstractColumnType, CoercibleScalarExpr, CoercibleScalarType, HirScalarExpr, NameMetadata,
32 UnaryFunc,
33};
34use crate::plan::query::{ExprContext, QueryContext};
35use crate::plan::scope::Scope;
36
37fn sql_impl_cast(expr: &'static str) -> CastTemplate {
39 let invoke = crate::func::sql_impl(expr);
40 CastTemplate::strict_only(move |ecx, _ccx, from_type, _to_type| {
41 let mut out = invoke(ecx, vec![from_type.clone()]).ok()?;
43 Some(move |e| {
44 out.splice_parameters(&[e], 0);
45 out
46 })
47 })
48}
49
50fn sql_impl_cast_per_context(casts: &[(CastContext, &'static str)]) -> CastTemplate {
51 let casts: BTreeMap<CastContext, _> = casts
52 .iter()
53 .map(|(ccx, expr)| (ccx.clone(), crate::func::sql_impl(expr)))
54 .collect();
55 CastTemplate::strict_only(move |ecx, ccx, from_type, _to_type| {
56 let invoke = &casts[&ccx];
57 let r = invoke(ecx, vec![from_type.clone()]);
58 let mut out = r.ok()?;
59 Some(move |e| {
60 out.splice_parameters(&[e], 0);
61 out
62 })
63 })
64}
65
66type Cast = Box<dyn FnOnce(HirScalarExpr) -> HirScalarExpr>;
68
69struct CastTemplate(
80 Box<
81 dyn Fn(
82 &ExprContext,
83 CastContext,
84 CastFailureMode,
85 &SqlScalarType,
86 &SqlScalarType,
87 ) -> Option<Cast>
88 + Send
89 + Sync,
90 >,
91);
92
93impl CastTemplate {
94 fn new<T, C>(t: T) -> CastTemplate
111 where
112 T: Fn(&ExprContext, CastContext, &SqlScalarType, &SqlScalarType) -> Option<C>
113 + Send
114 + Sync
115 + 'static,
116 C: FnOnce(HirScalarExpr) -> HirScalarExpr + 'static,
117 {
118 CastTemplate(Box::new(move |ecx, ccx, mode, from_ty, to_ty| {
119 let cast = t(ecx, ccx, from_ty, to_ty)?;
120 match mode {
121 CastFailureMode::Error => Some(Box::new(cast)),
122 CastFailureMode::NullFallback => {
123 let stages = extract_unary_stages_for_try_cast(from_ty, cast)?;
124 Some(Box::new(move |mut expr: HirScalarExpr| {
125 for stage in stages {
126 expr = expr.call_unary(UnaryFunc::try_cast_if_fallible(stage));
127 }
128 expr
129 }))
130 }
131 }
132 }))
133 }
134
135 fn strict_only<T, C>(t: T) -> CastTemplate
141 where
142 T: Fn(&ExprContext, CastContext, &SqlScalarType, &SqlScalarType) -> Option<C>
143 + Send
144 + Sync
145 + 'static,
146 C: FnOnce(HirScalarExpr) -> HirScalarExpr + 'static,
147 {
148 CastTemplate(Box::new(move |ecx, ccx, mode, from_ty, to_ty| match mode {
149 CastFailureMode::Error => Some(Box::new(t(ecx, ccx, from_ty, to_ty)?)),
150 CastFailureMode::NullFallback => None,
151 }))
152 }
153}
154
155fn extract_unary_stages_for_try_cast(
166 from: &SqlScalarType,
167 cast: impl FnOnce(HirScalarExpr) -> HirScalarExpr,
168) -> Option<Vec<UnaryFunc>> {
169 let sentinel = HirScalarExpr::Literal(
174 Row::pack([Datum::Dummy]),
175 from.clone().nullable(false),
176 NameMetadata::default(),
177 );
178 let mut expr = cast(sentinel.clone());
179 let mut stages = vec![];
180 loop {
181 match expr {
182 HirScalarExpr::CallUnary {
183 func, expr: inner, ..
184 } => {
185 stages.push(func);
186 expr = *inner;
187 }
188 other if other == sentinel => break,
189 _ => return None,
190 }
191 }
192 stages.reverse();
193 if let Some(stage) = stages
194 .iter()
195 .skip(1)
196 .find(|stage| !stage.propagates_nulls())
197 {
198 soft_panic_or_log!("cast stage {stage} does not propagate NULLs; TRY_CAST cannot wrap it");
199 return None;
200 }
201 Some(stages)
202}
203
204impl From<UnaryFunc> for CastTemplate {
205 fn from(u: UnaryFunc) -> CastTemplate {
206 CastTemplate::new(move |_ecx, _ccx, _from, _to| {
207 let u = u.clone();
208 Some(move |expr: HirScalarExpr| expr.call_unary(u))
209 })
210 }
211}
212
213impl<const N: usize> From<[UnaryFunc; N]> for CastTemplate {
214 fn from(funcs: [UnaryFunc; N]) -> CastTemplate {
215 CastTemplate::new(move |_ecx, _ccx, _from, _to| {
216 let funcs = funcs.clone();
217 Some(move |mut expr: HirScalarExpr| {
218 for func in funcs {
219 expr = expr.call_unary(func.clone());
220 }
221 expr
222 })
223 })
224 }
225}
226
227const STRING_REG_CAST_TEMPLATE: &str = "
246(SELECT
247CASE
248 WHEN $1 IS NULL THEN NULL
249-- Handle the absent reference, which the reg* text output spells `-`. PostgreSQL's
250-- `parseDashOrOid` accepts it for every reg* type, including text to regclass.
251 WHEN $1 = '-' THEN 0::pg_catalog.oid::pg_catalog.{0}
252-- Handle OID-like input, if available via {2}
253 WHEN {2} AND pg_catalog.substring($1, 1, 1) BETWEEN '0' AND '9' THEN
254 $1::pg_catalog.oid::pg_catalog.{0}
255 ELSE (
256 -- String case; look up that the item exists
257 SELECT o.oid
258 FROM mz_unsafe.mz_error_if_null(
259 (
260 -- We need to ensure a distinct here in the case of e.g. functions,
261 -- where multiple items share a GlobalId.
262 SELECT DISTINCT id AS name_id
263 FROM mz_internal.mz_resolve_object_name('{0}', $1)
264 ),
265 -- TODO: Support the correct error code for does not exist (42883).
266 '{1} \"' || $1 || '\" does not exist'
267 ) AS i (name_id),
268 -- Lateral lets us error separately from DNE case
269 LATERAL (
270 SELECT
271 CASE
272 -- Handle too many OIDs
273 WHEN mz_catalog.list_length(mz_catalog.list_agg(oid)) > 1 THEN
274 mz_unsafe.mz_error_if_null(
275 NULL::pg_catalog.{0},
276 'more than one {1} named \"' || $1 || '\"'
277 )
278 -- Resolve object name's OID if we know there is only one
279 ELSE
280 CAST(mz_catalog.list_agg(oid)[1] AS pg_catalog.{0})
281 END
282 FROM mz_catalog.mz_objects
283 WHERE id = name_id
284 GROUP BY id
285 ) AS o (oid)
286 )
287END)";
288
289static STRING_TO_REGCLASS_EXPLICIT: LazyLock<String> = LazyLock::new(|| {
290 SimpleCurlyFormat
291 .format(STRING_REG_CAST_TEMPLATE, ["regclass", "relation", "false"])
292 .unwrap()
293 .to_string()
294});
295
296static STRING_TO_REGCLASS_COERCED: LazyLock<String> = LazyLock::new(|| {
297 SimpleCurlyFormat
298 .format(STRING_REG_CAST_TEMPLATE, ["regclass", "relation", "true"])
299 .unwrap()
300 .to_string()
301});
302
303static STRING_TO_REGPROC: LazyLock<String> = LazyLock::new(|| {
304 SimpleCurlyFormat
305 .format(STRING_REG_CAST_TEMPLATE, ["regproc", "function", "true"])
306 .unwrap()
307 .to_string()
308});
309
310static STRING_TO_REGTYPE: LazyLock<String> = LazyLock::new(|| {
311 SimpleCurlyFormat
312 .format(STRING_REG_CAST_TEMPLATE, ["regtype", "type", "true"])
313 .unwrap()
314 .to_string()
315});
316
317const REG_STRING_CAST_TEMPLATE: &str = "(
321SELECT
322 CASE
323 WHEN CAST($1 AS pg_catalog.oid) = 0::pg_catalog.oid THEN '-'
324 ELSE
325 COALESCE(mz_internal.mz_global_id_to_name(o.id), CAST($1 AS pg_catalog.oid)::pg_catalog.text)
326 END
327 AS text
328FROM
329 (
330 SELECT
331 (
332 SELECT DISTINCT id
333 FROM
334 mz_catalog.mz_objects AS o
335 JOIN
336 mz_internal.mz_object_oid_alias AS a
337 ON o.type = a.object_type
338 WHERE
339 oid = CAST($1 AS pg_catalog.oid)
340 AND
341 a.oid_alias = '{0}'
342 )
343 )
344 AS o
345)";
346
347static REGCLASS_TO_STRING: LazyLock<String> = LazyLock::new(|| {
348 SimpleCurlyFormat
349 .format(REG_STRING_CAST_TEMPLATE, ["regclass"])
350 .unwrap()
351 .to_string()
352});
353
354static REGPROC_TO_STRING: LazyLock<String> = LazyLock::new(|| {
355 SimpleCurlyFormat
356 .format(REG_STRING_CAST_TEMPLATE, ["regproc"])
357 .unwrap()
358 .to_string()
359});
360
361static REGTYPE_TO_STRING: LazyLock<String> = LazyLock::new(|| {
362 SimpleCurlyFormat
363 .format(REG_STRING_CAST_TEMPLATE, ["regtype"])
364 .unwrap()
365 .to_string()
366});
367
368#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
374pub enum CastContext {
375 Implicit,
379 Assignment,
383 Explicit,
387 Coerced,
393}
394
395struct CastImpl {
397 template: CastTemplate,
398 context: CastContext,
399}
400
401macro_rules! casts(
402 {
403 $(
404 $from_to:expr => $cast_context:ident: $cast_template:expr
405 ),+
406 } => {{
407 let mut m = BTreeMap::new();
408 $(
409 m.insert($from_to, CastImpl {
410 template: $cast_template.into(),
411 context: CastContext::$cast_context,
412 });
413 )+
414 m
415 }};
416);
417
418static VALID_CASTS: LazyLock<BTreeMap<(SqlScalarBaseType, SqlScalarBaseType), CastImpl>> =
419 LazyLock::new(|| {
420 use SqlScalarBaseType::*;
421 use UnaryFunc::*;
422
423 casts! {
424 (Bool, Int32) => Explicit: CastBoolToInt32(func::CastBoolToInt32),
426 (Bool, Int64) => Explicit: CastBoolToInt64(func::CastBoolToInt64),
427 (Bool, String) => Assignment: CastBoolToString(func::CastBoolToString),
428
429 (Int16, Int32) => Implicit: CastInt16ToInt32(func::CastInt16ToInt32),
431 (Int16, Int64) => Implicit: CastInt16ToInt64(func::CastInt16ToInt64),
432 (Int16, UInt16) => Assignment: CastInt16ToUint16(func::CastInt16ToUint16),
433 (Int16, UInt32) => Assignment: CastInt16ToUint32(func::CastInt16ToUint32),
434 (Int16, UInt64) => Assignment: CastInt16ToUint64(func::CastInt16ToUint64),
435 (Int16, Float32) => Implicit: CastInt16ToFloat32(func::CastInt16ToFloat32),
436 (Int16, Float64) => Implicit: CastInt16ToFloat64(func::CastInt16ToFloat64),
437 (Int16, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
438 let s = to_type.unwrap_numeric_max_scale();
439 let f = CastInt16ToNumeric(func::CastInt16ToNumeric(s));
440 Some(move |e: HirScalarExpr| e.call_unary(f))
441 }),
442 (Int16, Oid) => Implicit: [
443 CastInt16ToInt32(func::CastInt16ToInt32),
444 CastInt32ToOid(func::CastInt32ToOid),
445 ],
446 (Int16, RegClass) => Implicit: [
447 CastInt16ToInt32(func::CastInt16ToInt32),
448 CastInt32ToOid(func::CastInt32ToOid),
449 CastOidToRegClass(func::CastOidToRegClass),
450 ],
451 (Int16, RegProc) => Implicit: [
452 CastInt16ToInt32(func::CastInt16ToInt32),
453 CastInt32ToOid(func::CastInt32ToOid),
454 CastOidToRegProc(func::CastOidToRegProc),
455 ],
456 (Int16, RegType) => Implicit: [
457 CastInt16ToInt32(func::CastInt16ToInt32),
458 CastInt32ToOid(func::CastInt32ToOid),
459 CastOidToRegType(func::CastOidToRegType),
460 ],
461 (Int16, String) => Assignment: CastInt16ToString(func::CastInt16ToString),
462
463 (Int32, Bool) => Explicit: CastInt32ToBool(func::CastInt32ToBool),
465 (Int32, Oid) => Implicit: CastInt32ToOid(func::CastInt32ToOid),
466 (Int32, RegClass) => Implicit: [
467 CastInt32ToOid(func::CastInt32ToOid),
468 CastOidToRegClass(func::CastOidToRegClass),
469 ],
470 (Int32, RegProc) => Implicit: [
471 CastInt32ToOid(func::CastInt32ToOid),
472 CastOidToRegProc(func::CastOidToRegProc),
473 ],
474 (Int32, RegType) => Implicit: [
475 CastInt32ToOid(func::CastInt32ToOid),
476 CastOidToRegType(func::CastOidToRegType),
477 ],
478 (Int32, PgLegacyChar) => Explicit:
479 CastInt32ToPgLegacyChar(func::CastInt32ToPgLegacyChar),
480 (Int32, Int16) => Assignment: CastInt32ToInt16(func::CastInt32ToInt16),
481 (Int32, Int64) => Implicit: CastInt32ToInt64(func::CastInt32ToInt64),
482 (Int32, UInt16) => Assignment: CastInt32ToUint16(func::CastInt32ToUint16),
483 (Int32, UInt32) => Assignment: CastInt32ToUint32(func::CastInt32ToUint32),
484 (Int32, UInt64) => Assignment: CastInt32ToUint64(func::CastInt32ToUint64),
485 (Int32, Float32) => Implicit: CastInt32ToFloat32(func::CastInt32ToFloat32),
486 (Int32, Float64) => Implicit: CastInt32ToFloat64(func::CastInt32ToFloat64),
487 (Int32, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
488 let s = to_type.unwrap_numeric_max_scale();
489 let f = CastInt32ToNumeric(func::CastInt32ToNumeric(s));
490 Some(move |e: HirScalarExpr| e.call_unary(f))
491 }),
492 (Int32, String) => Assignment: CastInt32ToString(func::CastInt32ToString),
493
494 (Int64, Bool) => Explicit: CastInt64ToBool(func::CastInt64ToBool),
496 (Int64, Int16) => Assignment: CastInt64ToInt16(func::CastInt64ToInt16),
497 (Int64, Int32) => Assignment: CastInt64ToInt32(func::CastInt64ToInt32),
498 (Int64, UInt16) => Assignment: CastInt64ToUint16(func::CastInt64ToUint16),
499 (Int64, UInt32) => Assignment: CastInt64ToUint32(func::CastInt64ToUint32),
500 (Int64, UInt64) => Assignment: CastInt64ToUint64(func::CastInt64ToUint64),
501 (Int64, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
502 let s = to_type.unwrap_numeric_max_scale();
503 let f = CastInt64ToNumeric(func::CastInt64ToNumeric(s));
504 Some(move |e: HirScalarExpr| e.call_unary(f))
505 }),
506 (Int64, Float32) => Implicit: CastInt64ToFloat32(func::CastInt64ToFloat32),
507 (Int64, Float64) => Implicit: CastInt64ToFloat64(func::CastInt64ToFloat64),
508 (Int64, Oid) => Implicit: CastInt64ToOid(func::CastInt64ToOid),
509 (Int64, RegClass) => Implicit: [
510 CastInt64ToOid(func::CastInt64ToOid),
511 CastOidToRegClass(func::CastOidToRegClass),
512 ],
513 (Int64, RegProc) => Implicit: [
514 CastInt64ToOid(func::CastInt64ToOid),
515 CastOidToRegProc(func::CastOidToRegProc),
516 ],
517 (Int64, RegType) => Implicit: [
518 CastInt64ToOid(func::CastInt64ToOid),
519 CastOidToRegType(func::CastOidToRegType),
520 ],
521 (Int64, String) => Assignment: CastInt64ToString(func::CastInt64ToString),
522
523 (UInt16, UInt32) => Implicit: CastUint16ToUint32(func::CastUint16ToUint32),
525 (UInt16, UInt64) => Implicit: CastUint16ToUint64(func::CastUint16ToUint64),
526 (UInt16, Int16) => Assignment: CastUint16ToInt16(func::CastUint16ToInt16),
527 (UInt16, Int32) => Implicit: CastUint16ToInt32(func::CastUint16ToInt32),
528 (UInt16, Int64) => Implicit: CastUint16ToInt64(func::CastUint16ToInt64),
529 (UInt16, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
530 let s = to_type.unwrap_numeric_max_scale();
531 let f = CastUint16ToNumeric(func::CastUint16ToNumeric(s));
532 Some(move |e: HirScalarExpr| e.call_unary(f))
533 }),
534 (UInt16, Float32) => Implicit: CastUint16ToFloat32(func::CastUint16ToFloat32),
535 (UInt16, Float64) => Implicit: CastUint16ToFloat64(func::CastUint16ToFloat64),
536 (UInt16, String) => Assignment: CastUint16ToString(func::CastUint16ToString),
537
538 (UInt32, UInt16) => Assignment: CastUint32ToUint16(func::CastUint32ToUint16),
540 (UInt32, UInt64) => Implicit: CastUint32ToUint64(func::CastUint32ToUint64),
541 (UInt32, Int16) => Assignment: CastUint32ToInt16(func::CastUint32ToInt16),
542 (UInt32, Int32) => Assignment: CastUint32ToInt32(func::CastUint32ToInt32),
543 (UInt32, Int64) => Implicit: CastUint32ToInt64(func::CastUint32ToInt64),
544 (UInt32, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
545 let s = to_type.unwrap_numeric_max_scale();
546 let f = CastUint32ToNumeric(func::CastUint32ToNumeric(s));
547 Some(move |e: HirScalarExpr| e.call_unary(f))
548 }),
549 (UInt32, Float32) => Implicit: CastUint32ToFloat32(func::CastUint32ToFloat32),
550 (UInt32, Float64) => Implicit: CastUint32ToFloat64(func::CastUint32ToFloat64),
551 (UInt32, String) => Assignment: CastUint32ToString(func::CastUint32ToString),
552
553 (UInt64, UInt16) => Assignment: CastUint64ToUint16(func::CastUint64ToUint16),
555 (UInt64, UInt32) => Assignment: CastUint64ToUint32(func::CastUint64ToUint32),
556 (UInt64, Int16) => Assignment: CastUint64ToInt16(func::CastUint64ToInt16),
557 (UInt64, Int32) => Assignment: CastUint64ToInt32(func::CastUint64ToInt32),
558 (UInt64, Int64) => Assignment: CastUint64ToInt64(func::CastUint64ToInt64),
559 (UInt64, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
560 let s = to_type.unwrap_numeric_max_scale();
561 let f = CastUint64ToNumeric(func::CastUint64ToNumeric(s));
562 Some(move |e: HirScalarExpr| e.call_unary(f))
563 }),
564 (UInt64, Float32) => Implicit: CastUint64ToFloat32(func::CastUint64ToFloat32),
565 (UInt64, Float64) => Implicit: CastUint64ToFloat64(func::CastUint64ToFloat64),
566 (UInt64, String) => Assignment: CastUint64ToString(func::CastUint64ToString),
567
568 (MzTimestamp, String) => Assignment:
570 CastMzTimestampToString(func::CastMzTimestampToString),
571 (MzTimestamp, Timestamp) => Assignment:
572 CastMzTimestampToTimestamp(func::CastMzTimestampToTimestamp),
573 (MzTimestamp, TimestampTz) => Assignment:
574 CastMzTimestampToTimestampTz(
575 func::CastMzTimestampToTimestampTz,
576 ),
577 (String, MzTimestamp) => Assignment:
578 CastStringToMzTimestamp(func::CastStringToMzTimestamp),
579 (UInt64, MzTimestamp) => Implicit:
580 CastUint64ToMzTimestamp(func::CastUint64ToMzTimestamp),
581 (UInt32, MzTimestamp) => Implicit:
582 CastUint32ToMzTimestamp(func::CastUint32ToMzTimestamp),
583 (Int64, MzTimestamp) => Implicit:
584 CastInt64ToMzTimestamp(func::CastInt64ToMzTimestamp),
585 (Int32, MzTimestamp) => Implicit:
586 CastInt32ToMzTimestamp(func::CastInt32ToMzTimestamp),
587 (Numeric, MzTimestamp) => Implicit:
588 CastNumericToMzTimestamp(func::CastNumericToMzTimestamp),
589 (Timestamp, MzTimestamp) => Implicit:
590 CastTimestampToMzTimestamp(func::CastTimestampToMzTimestamp),
591 (TimestampTz, MzTimestamp) => Implicit:
592 CastTimestampTzToMzTimestamp(
593 func::CastTimestampTzToMzTimestamp,
594 ),
595 (Date, MzTimestamp) => Implicit:
596 CastDateToMzTimestamp(func::CastDateToMzTimestamp),
597
598 (Oid, Int32) => Assignment: CastOidToInt32(func::CastOidToInt32),
600 (Oid, Int64) => Assignment: CastOidToInt64(func::CastOidToInt64),
601 (Oid, String) => Explicit: CastOidToString(func::CastOidToString),
602 (Oid, RegClass) => Implicit: CastOidToRegClass(func::CastOidToRegClass),
603 (Oid, RegProc) => Implicit: CastOidToRegProc(func::CastOidToRegProc),
604 (Oid, RegType) => Implicit: CastOidToRegType(func::CastOidToRegType),
605
606 (RegClass, Oid) => Implicit: CastRegClassToOid(func::CastRegClassToOid),
608 (RegClass, String) => Explicit: sql_impl_cast(®CLASS_TO_STRING),
609
610 (RegProc, Oid) => Implicit: CastRegProcToOid(func::CastRegProcToOid),
612 (RegProc, String) => Explicit: sql_impl_cast(®PROC_TO_STRING),
613
614 (RegType, Oid) => Implicit: CastRegTypeToOid(func::CastRegTypeToOid),
616 (RegType, String) => Explicit: sql_impl_cast(®TYPE_TO_STRING),
617
618 (Float32, Int16) => Assignment: CastFloat32ToInt16(func::CastFloat32ToInt16),
620 (Float32, Int32) => Assignment: CastFloat32ToInt32(func::CastFloat32ToInt32),
621 (Float32, Int64) => Assignment: CastFloat32ToInt64(func::CastFloat32ToInt64),
622 (Float32, UInt16) => Assignment: CastFloat32ToUint16(func::CastFloat32ToUint16),
623 (Float32, UInt32) => Assignment: CastFloat32ToUint32(func::CastFloat32ToUint32),
624 (Float32, UInt64) => Assignment: CastFloat32ToUint64(func::CastFloat32ToUint64),
625 (Float32, Float64) => Implicit: CastFloat32ToFloat64(func::CastFloat32ToFloat64),
626 (Float32, Numeric) => Assignment: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
627 let s = to_type.unwrap_numeric_max_scale();
628 let f = CastFloat32ToNumeric(func::CastFloat32ToNumeric(s));
629 Some(move |e: HirScalarExpr| e.call_unary(f))
630 }),
631 (Float32, String) => Assignment: CastFloat32ToString(func::CastFloat32ToString),
632
633 (Float64, Int16) => Assignment: CastFloat64ToInt16(func::CastFloat64ToInt16),
635 (Float64, Int32) => Assignment: CastFloat64ToInt32(func::CastFloat64ToInt32),
636 (Float64, Int64) => Assignment: CastFloat64ToInt64(func::CastFloat64ToInt64),
637 (Float64, UInt16) => Assignment: CastFloat64ToUint16(func::CastFloat64ToUint16),
638 (Float64, UInt32) => Assignment: CastFloat64ToUint32(func::CastFloat64ToUint32),
639 (Float64, UInt64) => Assignment: CastFloat64ToUint64(func::CastFloat64ToUint64),
640 (Float64, Float32) => Assignment: CastFloat64ToFloat32(func::CastFloat64ToFloat32),
641 (Float64, Numeric) => Assignment: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
642 let s = to_type.unwrap_numeric_max_scale();
643 let f = CastFloat64ToNumeric(func::CastFloat64ToNumeric(s));
644 Some(move |e: HirScalarExpr| e.call_unary(f))
645 }),
646 (Float64, String) => Assignment: CastFloat64ToString(func::CastFloat64ToString),
647
648 (Date, Timestamp) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
650 let p = to_type.unwrap_timestamp_precision();
651 let f = CastDateToTimestamp(func::CastDateToTimestamp(p));
652 Some(move |e: HirScalarExpr| e.call_unary(f))
653 }),
654 (Date, TimestampTz) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
655 let p = to_type.unwrap_timestamp_precision();
656 let f = CastDateToTimestampTz(func::CastDateToTimestampTz(p));
657 Some(move |e: HirScalarExpr| e.call_unary(f))
658 }),
659 (Date, String) => Assignment: CastDateToString(func::CastDateToString),
660
661 (Time, Interval) => Implicit: CastTimeToInterval(func::CastTimeToInterval),
663 (Time, String) => Assignment: CastTimeToString(func::CastTimeToString),
664
665 (Timestamp, Date) => Assignment: CastTimestampToDate(func::CastTimestampToDate),
667 (Timestamp, TimestampTz) => Implicit: CastTemplate::new(
668 |_ecx, _ccx, from_type, to_type|
669 {
670 let from = from_type.unwrap_timestamp_precision();
671 let to = to_type.unwrap_timestamp_precision();
672 let f = CastTimestampToTimestampTz(
673 func::CastTimestampToTimestampTz { from, to },
674 );
675 Some(move |e: HirScalarExpr| e.call_unary(f))
676 }),
677 (Timestamp, Timestamp) => Assignment: CastTemplate::new(
678 |_ecx, _ccx, from_type, to_type|
679 {
680 let from = from_type.unwrap_timestamp_precision();
681 let to = to_type.unwrap_timestamp_precision();
682 let f = AdjustTimestampPrecision(
683 func::AdjustTimestampPrecision { from, to },
684 );
685 Some(move |e: HirScalarExpr| e.call_unary(f))
686 }),
687 (Timestamp, Time) => Assignment: CastTimestampToTime(func::CastTimestampToTime),
688 (Timestamp, String) => Assignment: CastTimestampToString(func::CastTimestampToString),
689
690 (TimestampTz, Date) => Assignment: CastTimestampTzToDate(func::CastTimestampTzToDate),
692 (TimestampTz, Timestamp) => Assignment: CastTemplate::new(
693 |_ecx, _ccx, from_type, to_type|
694 {
695 let from = from_type.unwrap_timestamp_precision();
696 let to = to_type.unwrap_timestamp_precision();
697 let f = CastTimestampTzToTimestamp(
698 func::CastTimestampTzToTimestamp { from, to },
699 );
700 Some(move |e: HirScalarExpr| e.call_unary(f))
701 }),
702 (TimestampTz, TimestampTz) => Assignment: CastTemplate::new(
703 |_ecx, _ccx, from_type, to_type|
704 {
705 let from = from_type.unwrap_timestamp_precision();
706 let to = to_type.unwrap_timestamp_precision();
707 let f = AdjustTimestampTzPrecision(
708 func::AdjustTimestampTzPrecision { from, to },
709 );
710 Some(move |e: HirScalarExpr| e.call_unary(f))
711 }),
712 (TimestampTz, Time) => Assignment:
713 CastTimestampTzToTime(func::CastTimestampTzToTime),
714 (TimestampTz, String) => Assignment:
715 CastTimestampTzToString(func::CastTimestampTzToString),
716
717 (Interval, Time) => Assignment: CastIntervalToTime(func::CastIntervalToTime),
719 (Interval, String) => Assignment: CastIntervalToString(func::CastIntervalToString),
720
721 (Bytes, String) => Assignment: CastBytesToString(func::CastBytesToString),
723
724 (String, Bool) => Explicit: CastStringToBool(func::CastStringToBool),
726 (String, Int16) => Explicit: CastStringToInt16(func::CastStringToInt16),
727 (String, Int32) => Explicit: CastStringToInt32(func::CastStringToInt32),
728 (String, Int64) => Explicit: CastStringToInt64(func::CastStringToInt64),
729 (String, UInt16) => Explicit: CastStringToUint16(func::CastStringToUint16),
730 (String, UInt32) => Explicit: CastStringToUint32(func::CastStringToUint32),
731 (String, UInt64) => Explicit: CastStringToUint64(func::CastStringToUint64),
732 (String, Oid) => Explicit: CastStringToOid(func::CastStringToOid),
733
734 (String, RegClass) => Explicit: sql_impl_cast_per_context(
743 &[
744 (CastContext::Explicit, &STRING_TO_REGCLASS_EXPLICIT),
745 (CastContext::Coerced, &STRING_TO_REGCLASS_COERCED)
746 ]
747 ),
748 (String, RegProc) => Explicit: sql_impl_cast(&STRING_TO_REGPROC),
749 (String, RegType) => Explicit: sql_impl_cast(&STRING_TO_REGTYPE),
750
751 (String, Float32) => Explicit: CastStringToFloat32(func::CastStringToFloat32),
752 (String, Float64) => Explicit: CastStringToFloat64(func::CastStringToFloat64),
753 (String, Numeric) => Explicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
754 let s = to_type.unwrap_numeric_max_scale();
755 let f = CastStringToNumeric(func::CastStringToNumeric(s));
756 Some(move |e: HirScalarExpr| e.call_unary(f))
757 }),
758 (String, Date) => Explicit: CastStringToDate(func::CastStringToDate),
759 (String, Time) => Explicit: CastStringToTime(func::CastStringToTime),
760 (String, Timestamp) => Explicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
761 let p = to_type.unwrap_timestamp_precision();
762 let f = CastStringToTimestamp(func::CastStringToTimestamp(p));
763 Some(move |e: HirScalarExpr| e.call_unary(f))
764 }),
765 (String, TimestampTz) => Explicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
766 let p = to_type.unwrap_timestamp_precision();
767 let f = CastStringToTimestampTz(
768 func::CastStringToTimestampTz(p),
769 );
770 Some(move |e: HirScalarExpr| e.call_unary(f))
771 }),
772 (String, Interval) => Explicit: CastStringToInterval(func::CastStringToInterval),
773 (String, Bytes) => Explicit: CastStringToBytes(func::CastStringToBytes),
774 (String, Jsonb) => Explicit: CastStringToJsonb(func::CastStringToJsonb),
775 (String, Uuid) => Explicit: CastStringToUuid(func::CastStringToUuid),
776 (String, Array) => Explicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
777 let return_ty = to_type.clone();
778 let to_el_type = to_type.unwrap_array_element_type();
779 let cast_expr = plan_hypothetical_cast(ecx, ccx, from_type, to_el_type)?;
780 Some(|e: HirScalarExpr| {
781 e.call_unary(UnaryFunc::CastStringToArray(
782 func::CastStringToArray {
783 return_ty,
784 cast_expr: Box::new(cast_expr),
785 },
786 ))
787 })
788 }),
789 (String, List) => Explicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
790 let return_ty = to_type.clone();
791 let to_el_type = to_type.unwrap_list_element_type();
792 let cast_expr = plan_hypothetical_cast(ecx, ccx, from_type, to_el_type)?;
793 Some(|e: HirScalarExpr| {
794 e.call_unary(UnaryFunc::CastStringToList(
795 func::CastStringToList {
796 return_ty,
797 cast_expr: Box::new(cast_expr),
798 },
799 ))
800 })
801 }),
802 (String, Map) => Explicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
803 let return_ty = to_type.clone();
804 let to_val_type = to_type.unwrap_map_value_type();
805 let cast_expr = plan_hypothetical_cast(ecx, ccx, from_type, to_val_type)?;
806 Some(|e: HirScalarExpr| {
807 e.call_unary(UnaryFunc::CastStringToMap(
808 func::CastStringToMap {
809 return_ty,
810 cast_expr: Box::new(cast_expr),
811 },
812 ))
813 })
814 }),
815 (String, Range) => Explicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
816 let return_ty = to_type.clone();
817 let to_el_type = to_type.unwrap_range_element_type();
818 let cast_expr = plan_hypothetical_cast(ecx, ccx, from_type, to_el_type)?;
819 Some(|e: HirScalarExpr| {
820 e.call_unary(UnaryFunc::CastStringToRange(
821 func::CastStringToRange {
822 return_ty,
823 cast_expr: Box::new(cast_expr),
824 },
825 ))
826 })
827 }),
828 (String, Int2Vector) => Explicit: CastStringToInt2Vector(func::CastStringToInt2Vector),
829 (String, Char) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
830 let length = to_type.unwrap_char_length();
831 let fail_on_len = ccx != CastContext::Explicit;
832 let f = CastStringToChar(func::CastStringToChar {
833 length, fail_on_len,
834 });
835 Some(move |e: HirScalarExpr| e.call_unary(f))
836 }),
837 (String, VarChar) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
838 let length = to_type.unwrap_varchar_max_length();
839 let fail_on_len = ccx != CastContext::Explicit;
840 let f = CastStringToVarChar(func::CastStringToVarChar {
841 length, fail_on_len,
842 });
843 Some(move |e: HirScalarExpr| e.call_unary(f))
844 }),
845 (String, PgLegacyChar) => Assignment:
846 CastStringToPgLegacyChar(func::CastStringToPgLegacyChar),
847 (Char, String) => Implicit: CastCharToString(func::CastCharToString),
849 (Char, Char) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
850 let length = to_type.unwrap_char_length();
851 let fail_on_len = ccx != CastContext::Explicit;
852 let f = CastStringToChar(func::CastStringToChar {
853 length, fail_on_len,
854 });
855 Some(move |e: HirScalarExpr| e.call_unary(f))
856 }),
857 (Char, VarChar) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
858 let length = to_type.unwrap_varchar_max_length();
859 let fail_on_len = ccx != CastContext::Explicit;
860 let f = CastStringToVarChar(func::CastStringToVarChar {
861 length, fail_on_len,
862 });
863 Some(move |e: HirScalarExpr| e.call_unary(f))
864 }),
865 (Char, PgLegacyChar) => Assignment:
866 CastStringToPgLegacyChar(func::CastStringToPgLegacyChar),
867
868 (VarChar, String) => Implicit: CastVarCharToString(func::CastVarCharToString),
870 (VarChar, Char) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
871 let length = to_type.unwrap_char_length();
872 let fail_on_len = ccx != CastContext::Explicit;
873 let f = CastStringToChar(func::CastStringToChar {
874 length, fail_on_len,
875 });
876 Some(move |e: HirScalarExpr| e.call_unary(f))
877 }),
878 (VarChar, VarChar) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
879 let length = to_type.unwrap_varchar_max_length();
880 let fail_on_len = ccx != CastContext::Explicit;
881 let f = CastStringToVarChar(func::CastStringToVarChar {
882 length, fail_on_len,
883 });
884 Some(move |e: HirScalarExpr| e.call_unary(f))
885 }),
886 (VarChar, PgLegacyChar) => Assignment:
887 CastStringToPgLegacyChar(func::CastStringToPgLegacyChar),
888
889 (PgLegacyChar, String) => Implicit:
891 CastPgLegacyCharToString(func::CastPgLegacyCharToString),
892 (PgLegacyChar, Char) => Assignment:
893 CastPgLegacyCharToChar(func::CastPgLegacyCharToChar),
894 (PgLegacyChar, VarChar) => Assignment:
895 CastPgLegacyCharToVarChar(func::CastPgLegacyCharToVarChar),
896 (PgLegacyChar, Int32) => Explicit:
897 CastPgLegacyCharToInt32(func::CastPgLegacyCharToInt32),
898
899 (PgLegacyName, String) => Implicit: CastVarCharToString(func::CastVarCharToString),
904 (PgLegacyName, Char) => Assignment: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
905 let length = to_type.unwrap_char_length();
906 let fail_on_len = ccx != CastContext::Explicit;
907 let f = CastStringToChar(func::CastStringToChar {
908 length, fail_on_len,
909 });
910 Some(move |e: HirScalarExpr| e.call_unary(f))
911 }),
912 (PgLegacyName, VarChar) => Assignment: CastTemplate::new(
913 |_ecx, ccx, _from_type, to_type|
914 {
915 let length = to_type.unwrap_varchar_max_length();
916 let fail_on_len = ccx != CastContext::Explicit;
917 let f = CastStringToVarChar(func::CastStringToVarChar {
918 length, fail_on_len,
919 });
920 Some(move |e: HirScalarExpr| e.call_unary(f))
921 }),
922 (String, PgLegacyName) => Implicit:
923 CastStringToPgLegacyName(func::CastStringToPgLegacyName),
924 (Char, PgLegacyName) => Implicit:
925 CastStringToPgLegacyName(func::CastStringToPgLegacyName),
926 (VarChar, PgLegacyName) => Implicit:
927 CastStringToPgLegacyName(func::CastStringToPgLegacyName),
928
929 (Record, String) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, _to_type| {
931 let ty = from_type.clone();
932 Some(|e: HirScalarExpr| {
933 e.call_unary(CastRecordToString(
934 func::CastRecordToString { ty },
935 ))
936 })
937 }),
938 (Record, Record) => Implicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
939 let from_fields = from_type.unwrap_record_element_type();
940 let to_fields = to_type.unwrap_record_element_type();
941 if from_fields.len() != to_fields.len() {
942 return None;
943 }
944
945 if let (
946 l @ SqlScalarType::Record {
947 custom_id: Some(..), ..
948 },
949 r,
950 ) = (from_type, to_type)
951 {
952 if ccx == CastContext::Implicit && l != r {
955 return None;
956 }
957 }
958
959 let cast_exprs = from_fields
960 .iter()
961 .zip_eq(to_fields)
962 .map(|(f, t)| plan_hypothetical_cast(ecx, ccx, f, t))
963 .collect::<Option<Box<_>>>()?;
964 let to = to_type.clone();
965 Some(|e: HirScalarExpr| {
966 e.call_unary(CastRecord1ToRecord2(
967 func::CastRecord1ToRecord2 {
968 return_ty: to,
969 cast_exprs,
970 },
971 ))
972 })
973 }),
974
975 (Array, String) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, _to_type| {
977 let ty = from_type.clone();
978 Some(|e: HirScalarExpr| {
979 e.call_unary(CastArrayToString(
980 func::CastArrayToString { ty },
981 ))
982 })
983 }),
984 (Array, List) => Explicit: CastArrayToListOneDim(func::CastArrayToListOneDim),
985 (Array, Array) => Explicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
986 let inner_from_type = from_type.unwrap_array_element_type();
987 let inner_to_type = to_type.unwrap_array_element_type();
988 let cast_expr = plan_hypothetical_cast(
989 ecx, ccx, inner_from_type, inner_to_type,
990 )?;
991 let return_ty = to_type.clone();
992
993 Some(move |e: HirScalarExpr| {
994 e.call_unary(CastArrayToArray(func::CastArrayToArray {
995 return_ty,
996 cast_expr: Box::new(cast_expr),
997 }))
998 })
999 }),
1000
1001 (Int2Vector, Array) => Implicit: CastTemplate::new(|ecx, ccx, _from_type, to_type| {
1003 let inner_to_type = to_type.unwrap_array_element_type();
1004 let element_cast = if inner_to_type != &SqlScalarType::Int16 {
1008 let cast_expr = plan_hypothetical_cast(
1009 ecx, ccx, &SqlScalarType::Int16, inner_to_type
1010 )?;
1011 Some((to_type.clone(), cast_expr))
1012 } else {
1013 None
1014 };
1015 Some(move |e: HirScalarExpr| {
1016 let arr = e.call_unary(
1017 UnaryFunc::CastInt2VectorToArray(func::CastInt2VectorToArray)
1018 );
1019 match element_cast {
1020 Some((return_ty, cast_expr)) => {
1021 arr.call_unary(CastArrayToArray(
1022 func::CastArrayToArray { return_ty, cast_expr: Box::new(cast_expr) }
1023 ))
1024 }
1025 None => arr,
1026 }
1027 })
1028 }),
1029 (Int2Vector, String) => Explicit: CastInt2VectorToString(func::CastInt2VectorToString),
1030
1031 (List, String) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, _to_type| {
1033 let ty = from_type.clone();
1034 Some(|e: HirScalarExpr| {
1035 e.call_unary(CastListToString(
1036 func::CastListToString { ty },
1037 ))
1038 })
1039 }),
1040 (List, List) => Implicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
1041
1042 if let (
1043 l @ SqlScalarType::List {
1044 custom_id: Some(..), ..
1045 },
1046 r,
1047 ) = (from_type, to_type)
1048 {
1049 if ccx == CastContext::Implicit && !l.base_eq(r) {
1052 return None;
1053 }
1054 }
1055
1056 let return_ty = to_type.clone();
1057 let from_el_type = from_type.unwrap_list_element_type();
1058 let to_el_type = to_type.unwrap_list_element_type();
1059 let cast_expr = plan_hypothetical_cast(
1060 ecx, ccx, from_el_type, to_el_type,
1061 )?;
1062 Some(|e: HirScalarExpr| {
1063 e.call_unary(UnaryFunc::CastList1ToList2(
1064 func::CastList1ToList2 {
1065 return_ty,
1066 cast_expr: Box::new(cast_expr),
1067 },
1068 ))
1069 })
1070 }),
1071
1072 (Map, String) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, _to_type| {
1074 let ty = from_type.clone();
1075 Some(|e: HirScalarExpr| e.call_unary(CastMapToString(func::CastMapToString { ty })))
1076 }),
1077
1078 (Jsonb, Bool) => Explicit: CastJsonbToBool(func::CastJsonbToBool),
1080 (Jsonb, Int16) => Explicit: CastJsonbToInt16(func::CastJsonbToInt16),
1081 (Jsonb, Int32) => Explicit: CastJsonbToInt32(func::CastJsonbToInt32),
1082 (Jsonb, Int64) => Explicit: CastJsonbToInt64(func::CastJsonbToInt64),
1083 (Jsonb, Float32) => Explicit: CastJsonbToFloat32(func::CastJsonbToFloat32),
1084 (Jsonb, Float64) => Explicit: CastJsonbToFloat64(func::CastJsonbToFloat64),
1085 (Jsonb, Numeric) => Explicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
1086 let s = to_type.unwrap_numeric_max_scale();
1087 let f = CastJsonbToNumeric(func::CastJsonbToNumeric(s));
1088 Some(move |e: HirScalarExpr| e.call_unary(f))
1089 }),
1090 (Jsonb, String) => Assignment: CastJsonbToString(func::CastJsonbToString),
1091
1092 (Uuid, String) => Assignment: CastUuidToString(func::CastUuidToString),
1094
1095 (Numeric, Numeric) => Assignment: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
1097 let scale = to_type.unwrap_numeric_max_scale();
1098 Some(move |e: HirScalarExpr| match scale {
1099 None => e,
1100 Some(scale) => e.call_unary(
1101 UnaryFunc::AdjustNumericScale(
1102 func::AdjustNumericScale(scale),
1103 ),
1104 ),
1105 })
1106 }),
1107 (Numeric, Float32) => Implicit: CastNumericToFloat32(func::CastNumericToFloat32),
1108 (Numeric, Float64) => Implicit: CastNumericToFloat64(func::CastNumericToFloat64),
1109 (Numeric, Int16) => Assignment: CastNumericToInt16(func::CastNumericToInt16),
1110 (Numeric, Int32) => Assignment: CastNumericToInt32(func::CastNumericToInt32),
1111 (Numeric, Int64) => Assignment: CastNumericToInt64(func::CastNumericToInt64),
1112 (Numeric, UInt16) => Assignment: CastNumericToUint16(func::CastNumericToUint16),
1113 (Numeric, UInt32) => Assignment: CastNumericToUint32(func::CastNumericToUint32),
1114 (Numeric, UInt64) => Assignment: CastNumericToUint64(func::CastNumericToUint64),
1115 (Numeric, String) => Assignment: CastNumericToString(func::CastNumericToString),
1116
1117 (Range, String) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, _to_type| {
1119 let ty = from_type.clone();
1120 Some(|e: HirScalarExpr| {
1121 e.call_unary(CastRangeToString(
1122 func::CastRangeToString { ty },
1123 ))
1124 })
1125 }),
1126
1127 (MzAclItem, String) => Explicit: sql_impl_cast("(
1129 SELECT
1130 (CASE
1131 WHEN grantee_role_id = 'p' THEN ''
1132 ELSE COALESCE(grantee_role.name, grantee_role_id)
1133 END)
1134 || '='
1135 || mz_internal.mz_aclitem_privileges($1)
1136 || '/'
1137 || COALESCE(grantor_role.name, grantor_role_id)
1138 FROM
1139 (SELECT mz_internal.mz_aclitem_grantee($1) AS grantee_role_id),
1140 (SELECT mz_internal.mz_aclitem_grantor($1) AS grantor_role_id)
1141 LEFT JOIN mz_catalog.mz_roles AS grantee_role ON grantee_role_id = grantee_role.id
1142 LEFT JOIN mz_catalog.mz_roles AS grantor_role ON grantor_role_id = grantor_role.id
1143 )"),
1144 (MzAclItem, AclItem) => Explicit: sql_impl_cast("(
1145 SELECT makeaclitem(
1146 (CASE mz_internal.mz_aclitem_grantee($1)
1147 WHEN 'p' THEN 0
1148 ELSE (SELECT oid FROM mz_catalog.mz_roles
1149 WHERE id = mz_internal.mz_aclitem_grantee($1))
1150 END),
1151 (SELECT oid FROM mz_catalog.mz_roles
1152 WHERE id = mz_internal.mz_aclitem_grantor($1)),
1153 (SELECT array_to_string(
1154 mz_internal.mz_format_privileges(
1155 mz_internal.mz_aclitem_privileges($1)
1156 ), ',')),
1157 -- GRANT OPTION isn't implemented so we hardcode false.
1158 false
1159 )
1160 )"),
1161
1162 (AclItem, String) => Explicit: sql_impl_cast("(
1164 SELECT
1165 (CASE grantee_oid
1166 WHEN 0 THEN ''
1167 ELSE COALESCE(grantee_role.name, grantee_oid::text)
1168 END)
1169 || '='
1170 || mz_internal.aclitem_privileges($1)
1171 || '/'
1172 || COALESCE(grantor_role.name, grantor_oid::text)
1173 FROM
1174 (SELECT mz_internal.aclitem_grantee($1) AS grantee_oid),
1175 (SELECT mz_internal.aclitem_grantor($1) AS grantor_oid)
1176 LEFT JOIN mz_catalog.mz_roles AS grantee_role ON grantee_oid = grantee_role.oid
1177 LEFT JOIN mz_catalog.mz_roles AS grantor_role ON grantor_oid = grantor_role.oid
1178 )"),
1179 (AclItem, MzAclItem) => Explicit: sql_impl_cast("(
1180 SELECT mz_internal.make_mz_aclitem(
1181 (CASE mz_internal.aclitem_grantee($1)
1182 WHEN 0 THEN 'p'
1183 ELSE (SELECT id FROM mz_catalog.mz_roles
1184 WHERE oid = mz_internal.aclitem_grantee($1))
1185 END),
1186 (SELECT id FROM mz_catalog.mz_roles
1187 WHERE oid = mz_internal.aclitem_grantor($1)),
1188 (SELECT array_to_string(
1189 mz_internal.mz_format_privileges(
1190 mz_internal.aclitem_privileges($1)
1191 ), ','))
1192 )
1193 )")
1194 }
1195 });
1196
1197#[derive(Debug)]
1199pub enum CastError {
1200 InvalidCast {
1202 ccx: CastContext,
1203 from: String,
1204 to: String,
1205 },
1206 UnsupportedRangeElementType { element_type_name: String },
1208 NullFallbackUnsupported { from: String, to: String },
1210}
1211
1212impl CastError {
1213 pub fn into_plan_error(self, name: String) -> PlanError {
1215 match self {
1216 CastError::InvalidCast { ccx, from, to } => PlanError::InvalidCast {
1217 name,
1218 ccx,
1219 from,
1220 to,
1221 },
1222 CastError::UnsupportedRangeElementType { element_type_name } => {
1223 PlanError::UnsupportedRangeElementType { element_type_name }
1224 }
1225 CastError::NullFallbackUnsupported { from, to } => {
1226 PlanError::TryCastUnsupported { from, to }
1227 }
1228 }
1229 }
1230}
1231
1232fn get_cast(
1239 ecx: &ExprContext,
1240 ccx: CastContext,
1241 mode: CastFailureMode,
1242 from: &SqlScalarType,
1243 to: &SqlScalarType,
1244) -> Result<Cast, CastError> {
1245 use CastContext::*;
1246
1247 if from == to || (ccx == Implicit && from.base_eq(to)) {
1249 return Ok(Box::new(|expr| expr));
1250 }
1251
1252 if let SqlScalarType::Range { element_type } = to {
1254 validate_range_element_type(ecx, element_type)?;
1255 }
1256
1257 let imp = match VALID_CASTS.get(&(from.into(), to.into())) {
1258 Some(imp) => imp,
1259 None => {
1260 return Err(CastError::InvalidCast {
1261 ccx,
1262 from: ecx.humanize_sql_scalar_type(from, false),
1263 to: ecx.humanize_sql_scalar_type(to, false),
1264 });
1265 }
1266 };
1267 let template = if ccx >= imp.context {
1268 Some(&imp.template)
1269 } else {
1270 None
1271 };
1272 let build = |mode| template.and_then(|template| (template.0)(ecx, ccx, mode, from, to));
1273 match build(mode) {
1274 Some(cast) => Ok(cast),
1275 None if mode == CastFailureMode::NullFallback
1279 && build(CastFailureMode::Error).is_some() =>
1280 {
1281 Err(CastError::NullFallbackUnsupported {
1282 from: ecx.humanize_sql_scalar_type(from, false),
1283 to: ecx.humanize_sql_scalar_type(to, false),
1284 })
1285 }
1286 None => Err(CastError::InvalidCast {
1287 ccx,
1288 from: ecx.humanize_sql_scalar_type(from, false),
1289 to: ecx.humanize_sql_scalar_type(to, false),
1290 }),
1291 }
1292}
1293
1294pub fn valid_cast_base_type_pairs() -> impl Iterator<Item = (SqlScalarBaseType, SqlScalarBaseType)>
1297{
1298 VALID_CASTS.keys().copied()
1299}
1300
1301pub fn to_string(ecx: &ExprContext, expr: HirScalarExpr) -> Result<HirScalarExpr, PlanError> {
1309 plan_cast(ecx, CastContext::Explicit, expr, &SqlScalarType::String)
1310}
1311
1312pub fn to_jsonb(ecx: &ExprContext, expr: HirScalarExpr) -> Result<HirScalarExpr, PlanError> {
1324 use SqlScalarType::*;
1325
1326 Ok(match ecx.scalar_type(&expr) {
1327 Bool | Jsonb | Numeric { .. } => {
1328 expr.call_unary(UnaryFunc::CastJsonbableToJsonb(func::CastJsonbableToJsonb))
1329 }
1330 Int16 | Int32 | Int64 | UInt16 | UInt32 | UInt64 | Float32 | Float64 => plan_cast(
1331 ecx,
1332 CastContext::Explicit,
1333 expr,
1334 &Numeric { max_scale: None },
1335 )
1336 .expect("cast known to exist")
1337 .call_unary(UnaryFunc::CastJsonbableToJsonb(func::CastJsonbableToJsonb)),
1338 Record { fields, .. } => {
1339 let mut exprs = vec![];
1340 for (i, (name, _ty)) in fields.iter().enumerate() {
1341 exprs.push(HirScalarExpr::literal(
1342 Datum::String(name),
1343 SqlScalarType::String,
1344 ));
1345 exprs.push(to_jsonb(
1346 ecx,
1347 expr.clone()
1348 .call_unary(UnaryFunc::RecordGet(func::RecordGet(i))),
1349 )?);
1350 }
1351 HirScalarExpr::call_variadic(JsonbBuildObject, exprs)
1352 }
1353 ref ty @ List {
1354 ref element_type, ..
1355 }
1356 | ref ty @ Array(ref element_type) => {
1357 let qcx = QueryContext::root(ecx.qcx.scx, ecx.qcx.lifetime);
1360 let ecx = ExprContext {
1361 qcx: &qcx,
1362 name: "to_jsonb",
1363 scope: &Scope::empty(),
1364 relation_type: &SqlRelationType::new(vec![element_type.clone().nullable(true)]),
1365 allow_aggregates: false,
1366 allow_subqueries: false,
1367 allow_parameters: false,
1368 allow_windows: false,
1369 };
1370
1371 let cast_element = to_jsonb(&ecx, HirScalarExpr::column(0))?;
1374 let cast_element = cast_element
1375 .lower_uncorrelated(ecx.catalog().system_vars())
1376 .expect("to_jsonb does not produce correlated expressions on uncorrelated input");
1377
1378 let func = match ty {
1382 List { .. } => UnaryFunc::CastListToJsonb(CastListToJsonb {
1383 cast_element: Box::new(cast_element),
1384 }),
1385 Array { .. } => UnaryFunc::CastArrayToJsonb(CastArrayToJsonb {
1386 cast_element: Box::new(cast_element),
1387 }),
1388 _ => unreachable!("validated above"),
1389 };
1390
1391 expr.call_unary(func)
1392 }
1393 Date
1394 | Time
1395 | Timestamp { .. }
1396 | TimestampTz { .. }
1397 | Interval
1398 | PgLegacyChar
1399 | PgLegacyName
1400 | Bytes
1401 | String
1402 | Char { .. }
1403 | VarChar { .. }
1404 | Uuid
1405 | Oid
1406 | Map { .. }
1407 | RegProc
1408 | RegType
1409 | RegClass
1410 | Int2Vector
1411 | MzTimestamp
1412 | Range { .. }
1413 | MzAclItem
1414 | AclItem => to_string(ecx, expr)?
1415 .call_unary(UnaryFunc::CastJsonbableToJsonb(func::CastJsonbableToJsonb)),
1416 })
1417}
1418
1419pub fn guess_best_common_type(
1428 ecx: &ExprContext,
1429 types: &[CoercibleScalarType],
1430) -> Result<SqlScalarType, PlanError> {
1431 if let Some(CoercibleScalarType::Record(field_tys)) = types.first() {
1441 if types
1442 .iter()
1443 .all(|t| matches!(t, CoercibleScalarType::Record(fts) if field_tys.len() == fts.len()))
1444 {
1445 let mut fields = vec![];
1446 for i in 0..field_tys.len() {
1447 let name = ColumnName::from(format!("f{}", fields.len() + 1));
1448 let mut guesses = vec![];
1449 let mut nullable = false;
1450 for ty in types {
1451 let field_ty = match ty {
1452 CoercibleScalarType::Record(fts) => fts[i].clone(),
1453 _ => unreachable!(),
1454 };
1455 if field_ty.nullable() {
1456 nullable = true;
1457 }
1458 guesses.push(field_ty.scalar_type());
1459 }
1460 let guess = guess_best_common_type(ecx, &guesses)?;
1461 fields.push((name, guess.nullable(nullable)));
1462 }
1463 return Ok(SqlScalarType::Record {
1464 fields: fields.into(),
1465 custom_id: None,
1466 });
1467 }
1468 }
1469
1470 let mut types: Vec<_> = types.into_iter().filter_map(|v| v.as_coerced()).collect();
1472
1473 let contains_int = types.iter().any(|t| {
1475 matches!(
1476 t,
1477 SqlScalarType::Int16 | SqlScalarType::Int32 | SqlScalarType::Int64
1478 )
1479 });
1480
1481 for t in types.iter_mut() {
1482 if contains_int
1483 && matches!(
1484 t,
1485 SqlScalarType::UInt16 | SqlScalarType::UInt32 | SqlScalarType::UInt64
1486 )
1487 {
1488 *t = t.near_match().expect("unsigned ints have near matches")
1489 }
1490 }
1491
1492 let mut types = types.iter();
1493
1494 let mut candidate = match types.next() {
1495 None => return Ok(SqlScalarType::String),
1497 Some(t) => t,
1499 };
1500
1501 let preferred_type = TypeCategory::from_type(candidate).preferred_type();
1502
1503 for typ in types {
1504 if TypeCategory::from_type(candidate) != TypeCategory::from_type(typ) {
1505 sql_bail!(
1507 "{} types {} and {} cannot be matched",
1508 ecx.name,
1509 ecx.humanize_sql_scalar_type(candidate, false),
1510 ecx.humanize_sql_scalar_type(typ, false),
1511 );
1512 };
1513
1514 if preferred_type.as_ref() != Some(candidate)
1516 && can_cast(ecx, CastContext::Implicit, candidate, typ)
1517 && !can_cast(ecx, CastContext::Implicit, typ, candidate)
1518 {
1519 candidate = typ;
1524 }
1525 }
1526 Ok(candidate.without_modifiers())
1527}
1528
1529pub fn plan_coerce<'a>(
1530 ecx: &'a ExprContext,
1531 e: CoercibleScalarExpr,
1532 coerce_to: &SqlScalarType,
1533) -> Result<HirScalarExpr, PlanError> {
1534 plan_coerce_with_failure_mode(ecx, e, coerce_to, CastFailureMode::Error)
1535}
1536
1537pub fn plan_coerce_with_failure_mode<'a>(
1541 ecx: &'a ExprContext,
1542 e: CoercibleScalarExpr,
1543 coerce_to: &SqlScalarType,
1544 mode: CastFailureMode,
1545) -> Result<HirScalarExpr, PlanError> {
1546 use CoercibleScalarExpr::*;
1547
1548 Ok(match e {
1549 Coerced(e) => e,
1550
1551 LiteralNull => HirScalarExpr::literal_null(coerce_to.clone()),
1552
1553 LiteralString(s) => {
1554 let lit = HirScalarExpr::literal(Datum::String(&s), SqlScalarType::String);
1555 let coerce_to_base = &coerce_to.without_modifiers();
1560 plan_cast_with_failure_mode(ecx, CastContext::Coerced, mode, lit, coerce_to_base)?
1561 }
1562
1563 LiteralRecord(exprs) => {
1564 let arity = exprs.len();
1565 let coercions = match (coerce_to, mode) {
1572 (SqlScalarType::Record { fields, .. }, CastFailureMode::Error)
1573 if fields.len() == arity =>
1574 {
1575 fields
1576 .iter()
1577 .map(|(_name, ty)| &ty.scalar_type)
1578 .cloned()
1579 .collect()
1580 }
1581 _ => vec![SqlScalarType::String; exprs.len()],
1582 };
1583 let mut out = vec![];
1584 for (e, coerce_to) in exprs.into_iter().zip_eq(coercions) {
1585 out.push(plan_coerce_with_failure_mode(ecx, e, &coerce_to, mode)?);
1586 }
1587 HirScalarExpr::call_variadic(
1588 RecordCreate {
1589 field_names: (0..arity)
1590 .map(|i| ColumnName::from(format!("f{}", i + 1)))
1591 .collect(),
1592 },
1593 out,
1594 )
1595 }
1596
1597 Parameter(n) => {
1598 let prev = ecx.param_types().borrow_mut().insert(n, coerce_to.clone());
1599 if let Some(prev) = prev {
1600 if prev != *coerce_to {
1601 sql_bail!(
1602 "there are contradicting constraints for \
1603 the type of parameter ${}: \
1604 should be both {} and {}",
1605 n,
1606 ecx.humanize_sql_scalar_type(&prev, false),
1607 ecx.humanize_sql_scalar_type(coerce_to, false),
1608 );
1609 }
1610 }
1611 HirScalarExpr::parameter(n)
1612 }
1613 })
1614}
1615
1616fn validate_range_element_type(
1620 ecx: &ExprContext,
1621 element_type: &SqlScalarType,
1622) -> Result<(), CastError> {
1623 let allowed = matches!(
1624 element_type,
1625 SqlScalarType::Int32
1626 | SqlScalarType::Int64
1627 | SqlScalarType::Date
1628 | SqlScalarType::Numeric { .. }
1629 | SqlScalarType::Timestamp { .. }
1630 | SqlScalarType::TimestampTz { .. }
1631 );
1632 if allowed {
1633 Ok(())
1634 } else {
1635 Err(CastError::UnsupportedRangeElementType {
1636 element_type_name: ecx.humanize_sql_scalar_type(element_type, false),
1637 })
1638 }
1639}
1640
1641pub fn plan_hypothetical_cast(
1648 ecx: &ExprContext,
1649 ccx: CastContext,
1650 from: &SqlScalarType,
1651 to: &SqlScalarType,
1652) -> Option<mz_expr::MirScalarExpr> {
1653 plan_hypothetical_cast_with_failure_mode(ecx, ccx, CastFailureMode::Error, from, to)
1654}
1655
1656pub fn plan_hypothetical_cast_with_failure_mode(
1658 ecx: &ExprContext,
1659 ccx: CastContext,
1660 mode: CastFailureMode,
1661 from: &SqlScalarType,
1662 to: &SqlScalarType,
1663) -> Option<mz_expr::MirScalarExpr> {
1664 let mut scx = ecx.qcx.scx.clone();
1667 scx.param_types = RefCell::new(BTreeMap::new());
1668 let qcx = QueryContext::root(&scx, ecx.qcx.lifetime);
1669 let relation_type = SqlRelationType {
1670 column_types: vec![SqlColumnType {
1671 nullable: true,
1672 scalar_type: from.clone(),
1673 }],
1674 keys: vec![vec![0]],
1675 };
1676 let ecx = ExprContext {
1677 qcx: &qcx,
1678 name: "plan_hypothetical_cast",
1679 scope: &Scope::empty(),
1680 relation_type: &relation_type,
1681 allow_aggregates: false,
1682 allow_subqueries: true,
1683 allow_parameters: true,
1684 allow_windows: false,
1685 };
1686
1687 let col_expr = HirScalarExpr::column(0);
1688
1689 plan_cast_with_failure_mode(&ecx, ccx, mode, col_expr, to)
1698 .ok()?
1699 .lower_uncorrelated(ecx.catalog().system_vars())
1701 .ok()
1702}
1703
1704pub fn plan_cast(
1714 ecx: &ExprContext,
1715 ccx: CastContext,
1716 expr: HirScalarExpr,
1717 to: &SqlScalarType,
1718) -> Result<HirScalarExpr, PlanError> {
1719 plan_cast_with_failure_mode(ecx, ccx, CastFailureMode::Error, expr, to)
1720}
1721
1722pub fn plan_cast_with_failure_mode(
1727 ecx: &ExprContext,
1728 ccx: CastContext,
1729 mode: CastFailureMode,
1730 expr: HirScalarExpr,
1731 to: &SqlScalarType,
1732) -> Result<HirScalarExpr, PlanError> {
1733 let from = ecx.scalar_type(&expr);
1734
1735 let cast_inner = |from, to, expr| {
1738 get_cast(ecx, ccx, mode, from, to)
1739 .map(|cast| cast(expr))
1740 .map_err(|e| e.into_plan_error(ecx.name.into()))
1741 };
1742
1743 let from_category = TypeCategory::from_type(&from);
1747 let to_category = TypeCategory::from_type(to);
1748 if from_category == TypeCategory::String && to_category != TypeCategory::String {
1749 let expr = match from {
1757 SqlScalarType::PgLegacyChar => match get_cast(ecx, ccx, mode, &from, to) {
1758 Ok(cast) => return Ok(cast(expr)),
1759 Err(_) => cast_inner(&from, &SqlScalarType::String, expr)?,
1760 },
1761 _ => expr,
1762 };
1763 cast_inner(&SqlScalarType::String, to, expr)
1764 } else if from_category != TypeCategory::String && to_category == TypeCategory::String {
1765 let expr = cast_inner(&from, &SqlScalarType::String, expr)?;
1768 cast_inner(&SqlScalarType::String, to, expr)
1769 } else {
1770 cast_inner(&from, to, expr)
1772 }
1773}
1774
1775pub fn can_cast(
1777 ecx: &ExprContext,
1778 ccx: CastContext,
1779 cast_from: &SqlScalarType,
1780 cast_to: &SqlScalarType,
1781) -> bool {
1782 get_cast(ecx, ccx, CastFailureMode::Error, cast_from, cast_to).is_ok()
1783}
1784
1785#[cfg(test)]
1786mod tests {
1787 use mz_expr::func::{CastInt64ToInt32, CastStringToInt64, Eq as EqFunc, IsNull};
1788 use mz_repr::SqlScalarType;
1789
1790 use super::extract_unary_stages_for_try_cast;
1791 use crate::plan::hir::HirScalarExpr;
1792
1793 #[mz_ore::test]
1794 fn cast_stages_of_chain_are_innermost_first() {
1795 let stages =
1796 extract_unary_stages_for_try_cast(&SqlScalarType::String, |e: HirScalarExpr| {
1797 e.call_unary(CastStringToInt64.into())
1798 .call_unary(CastInt64ToInt32.into())
1799 });
1800 assert_eq!(
1801 stages,
1802 Some(vec![CastStringToInt64.into(), CastInt64ToInt32.into()])
1803 );
1804 }
1805
1806 #[mz_ore::test]
1807 fn cast_stages_of_identity_is_empty() {
1808 assert_eq!(
1809 extract_unary_stages_for_try_cast(&SqlScalarType::Int32, |e: HirScalarExpr| e),
1810 Some(vec![])
1811 );
1812 }
1813
1814 #[mz_ore::test]
1815 fn cast_stages_rejects_non_unary_shapes() {
1816 let compares = |e: HirScalarExpr| {
1817 let zero = HirScalarExpr::literal_null(SqlScalarType::Int32);
1818 e.call_binary(zero, EqFunc)
1819 };
1820 assert_eq!(
1821 extract_unary_stages_for_try_cast(&SqlScalarType::Int32, compares),
1822 None
1823 );
1824 let ignores_input = |_: HirScalarExpr| HirScalarExpr::literal_null(SqlScalarType::Int32);
1825 assert_eq!(
1826 extract_unary_stages_for_try_cast(&SqlScalarType::Int32, ignores_input),
1827 None
1828 );
1829 }
1830
1831 #[mz_ore::test]
1834 fn cast_stages_rejects_non_null_propagating_later_stage() {
1835 let chain = |e: HirScalarExpr| {
1836 e.call_unary(CastInt64ToInt32.into())
1837 .call_unary(IsNull.into())
1838 };
1839 let outcome = mz_ore::panic::catch_unwind(|| {
1840 extract_unary_stages_for_try_cast(&SqlScalarType::Int64, chain)
1841 });
1842 if mz_ore::assert::soft_assertions_enabled() {
1843 assert!(outcome.is_err(), "soft assertion should have fired");
1844 } else {
1845 assert_eq!(outcome.expect("no panic"), None);
1846 }
1847 }
1848}