1use 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_ore::assert_none;
22use mz_repr::{ColumnName, ColumnType, Datum, RelationType, ScalarBaseType, ScalarType};
23
24use crate::catalog::TypeCategory;
25use crate::plan::error::PlanError;
26use crate::plan::hir::{
27 AbstractColumnType, CoercibleScalarExpr, CoercibleScalarType, HirScalarExpr, UnaryFunc,
28};
29use crate::plan::query::{ExprContext, QueryContext};
30use crate::plan::scope::Scope;
31
32fn sql_impl_cast(expr: &'static str) -> CastTemplate {
34 let invoke = crate::func::sql_impl(expr);
35 CastTemplate::new(move |ecx, _ccx, from_type, _to_type| {
36 let mut out = invoke(ecx.qcx, vec![from_type.clone()]).ok()?;
38 Some(move |e| {
39 out.splice_parameters(&[e], 0);
40 out
41 })
42 })
43}
44
45fn sql_impl_cast_per_context(casts: &[(CastContext, &'static str)]) -> CastTemplate {
46 let casts: BTreeMap<CastContext, _> = casts
47 .iter()
48 .map(|(ccx, expr)| (ccx.clone(), crate::func::sql_impl(expr)))
49 .collect();
50 CastTemplate::new(move |ecx, ccx, from_type, _to_type| {
51 let invoke = &casts[&ccx];
52 let r = invoke(ecx.qcx, vec![from_type.clone()]);
53 let mut out = r.ok()?;
54 Some(move |e| {
55 out.splice_parameters(&[e], 0);
56 out
57 })
58 })
59}
60
61type Cast = Box<dyn FnOnce(HirScalarExpr) -> HirScalarExpr>;
63
64struct CastTemplate(
74 Box<dyn Fn(&ExprContext, CastContext, &ScalarType, &ScalarType) -> Option<Cast> + Send + Sync>,
75);
76
77impl CastTemplate {
78 fn new<T, C>(t: T) -> CastTemplate
79 where
80 T: Fn(&ExprContext, CastContext, &ScalarType, &ScalarType) -> Option<C>
81 + Send
82 + Sync
83 + 'static,
84 C: FnOnce(HirScalarExpr) -> HirScalarExpr + 'static,
85 {
86 CastTemplate(Box::new(move |ecx, ccx, from_ty, to_ty| {
87 Some(Box::new(t(ecx, ccx, from_ty, to_ty)?))
88 }))
89 }
90}
91
92impl From<UnaryFunc> for CastTemplate {
93 fn from(u: UnaryFunc) -> CastTemplate {
94 CastTemplate::new(move |_ecx, _ccx, _from, _to| {
95 let u = u.clone();
96 Some(move |expr: HirScalarExpr| expr.call_unary(u))
97 })
98 }
99}
100
101impl<const N: usize> From<[UnaryFunc; N]> for CastTemplate {
102 fn from(funcs: [UnaryFunc; N]) -> CastTemplate {
103 CastTemplate::new(move |_ecx, _ccx, _from, _to| {
104 let funcs = funcs.clone();
105 Some(move |mut expr: HirScalarExpr| {
106 for func in funcs {
107 expr = expr.call_unary(func.clone());
108 }
109 expr
110 })
111 })
112 }
113}
114
115const STRING_REG_CAST_TEMPLATE: &str = "
132(SELECT
133CASE
134 WHEN $1 IS NULL THEN NULL
135-- Handle OID-like input, if available via {2}
136 WHEN {2} AND pg_catalog.substring($1, 1, 1) BETWEEN '0' AND '9' THEN
137 $1::pg_catalog.oid::pg_catalog.{0}
138 ELSE (
139 -- String case; look up that the item exists
140 SELECT o.oid
141 FROM mz_unsafe.mz_error_if_null(
142 (
143 -- We need to ensure a distinct here in the case of e.g. functions,
144 -- where multiple items share a GlobalId.
145 SELECT DISTINCT id AS name_id
146 FROM mz_internal.mz_resolve_object_name('{0}', $1)
147 ),
148 -- TODO: Support the correct error code for does not exist (42883).
149 '{1} \"' || $1 || '\" does not exist'
150 ) AS i (name_id),
151 -- Lateral lets us error separately from DNE case
152 LATERAL (
153 SELECT
154 CASE
155 -- Handle too many OIDs
156 WHEN mz_catalog.list_length(mz_catalog.list_agg(oid)) > 1 THEN
157 mz_unsafe.mz_error_if_null(
158 NULL::pg_catalog.{0},
159 'more than one {1} named \"' || $1 || '\"'
160 )
161 -- Resolve object name's OID if we know there is only one
162 ELSE
163 CAST(mz_catalog.list_agg(oid)[1] AS pg_catalog.{0})
164 END
165 FROM mz_catalog.mz_objects
166 WHERE id = name_id
167 GROUP BY id
168 ) AS o (oid)
169 )
170END)";
171
172static STRING_TO_REGCLASS_EXPLICIT: LazyLock<String> = LazyLock::new(|| {
173 SimpleCurlyFormat
174 .format(STRING_REG_CAST_TEMPLATE, ["regclass", "relation", "false"])
175 .unwrap()
176 .to_string()
177});
178
179static STRING_TO_REGCLASS_COERCED: LazyLock<String> = LazyLock::new(|| {
180 SimpleCurlyFormat
181 .format(STRING_REG_CAST_TEMPLATE, ["regclass", "relation", "true"])
182 .unwrap()
183 .to_string()
184});
185
186static STRING_TO_REGPROC: LazyLock<String> = LazyLock::new(|| {
187 SimpleCurlyFormat
188 .format(STRING_REG_CAST_TEMPLATE, ["regproc", "function", "true"])
189 .unwrap()
190 .to_string()
191});
192
193static STRING_TO_REGTYPE: LazyLock<String> = LazyLock::new(|| {
194 SimpleCurlyFormat
195 .format(STRING_REG_CAST_TEMPLATE, ["regtype", "type", "true"])
196 .unwrap()
197 .to_string()
198});
199
200const REG_STRING_CAST_TEMPLATE: &str = "(
201SELECT
202 COALESCE(mz_internal.mz_global_id_to_name(o.id), CAST($1 AS pg_catalog.oid)::pg_catalog.text)
203 AS text
204FROM
205 (
206 SELECT
207 (
208 SELECT DISTINCT id
209 FROM
210 mz_catalog.mz_objects AS o
211 JOIN
212 mz_internal.mz_object_oid_alias AS a
213 ON o.type = a.object_type
214 WHERE
215 oid = CAST($1 AS pg_catalog.oid)
216 AND
217 a.oid_alias = '{0}'
218 )
219 )
220 AS o
221)";
222
223static REGCLASS_TO_STRING: LazyLock<String> = LazyLock::new(|| {
224 SimpleCurlyFormat
225 .format(REG_STRING_CAST_TEMPLATE, ["regclass"])
226 .unwrap()
227 .to_string()
228});
229
230static REGPROC_TO_STRING: LazyLock<String> = LazyLock::new(|| {
231 SimpleCurlyFormat
232 .format(REG_STRING_CAST_TEMPLATE, ["regproc"])
233 .unwrap()
234 .to_string()
235});
236
237static REGTYPE_TO_STRING: LazyLock<String> = LazyLock::new(|| {
238 SimpleCurlyFormat
239 .format(REG_STRING_CAST_TEMPLATE, ["regtype"])
240 .unwrap()
241 .to_string()
242});
243
244#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
250pub enum CastContext {
251 Implicit,
255 Assignment,
259 Explicit,
263 Coerced,
269}
270
271struct CastImpl {
273 template: CastTemplate,
274 context: CastContext,
275}
276
277macro_rules! casts(
278 {
279 $(
280 $from_to:expr => $cast_context:ident: $cast_template:expr
281 ),+
282 } => {{
283 let mut m = BTreeMap::new();
284 $(
285 m.insert($from_to, CastImpl {
286 template: $cast_template.into(),
287 context: CastContext::$cast_context,
288 });
289 )+
290 m
291 }};
292);
293
294static VALID_CASTS: LazyLock<BTreeMap<(ScalarBaseType, ScalarBaseType), CastImpl>> = LazyLock::new(
295 || {
296 use ScalarBaseType::*;
297 use UnaryFunc::*;
298
299 casts! {
300 (Bool, Int32) => Explicit: CastBoolToInt32(func::CastBoolToInt32),
302 (Bool, Int64) => Explicit: CastBoolToInt64(func::CastBoolToInt64),
303 (Bool, String) => Assignment: CastBoolToString(func::CastBoolToString),
304
305 (Int16, Int32) => Implicit: CastInt16ToInt32(func::CastInt16ToInt32),
307 (Int16, Int64) => Implicit: CastInt16ToInt64(func::CastInt16ToInt64),
308 (Int16, UInt16) => Assignment: CastInt16ToUint16(func::CastInt16ToUint16),
309 (Int16, UInt32) => Assignment: CastInt16ToUint32(func::CastInt16ToUint32),
310 (Int16, UInt64) => Assignment: CastInt16ToUint64(func::CastInt16ToUint64),
311 (Int16, Float32) => Implicit: CastInt16ToFloat32(func::CastInt16ToFloat32),
312 (Int16, Float64) => Implicit: CastInt16ToFloat64(func::CastInt16ToFloat64),
313 (Int16, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
314 let s = to_type.unwrap_numeric_max_scale();
315 Some(move |e: HirScalarExpr| e.call_unary(CastInt16ToNumeric(func::CastInt16ToNumeric(s))))
316 }),
317 (Int16, Oid) => Implicit: [
318 CastInt16ToInt32(func::CastInt16ToInt32),
319 CastInt32ToOid(func::CastInt32ToOid),
320 ],
321 (Int16, RegClass) => Implicit: [
322 CastInt16ToInt32(func::CastInt16ToInt32),
323 CastInt32ToOid(func::CastInt32ToOid),
324 CastOidToRegClass(func::CastOidToRegClass),
325 ],
326 (Int16, RegProc) => Implicit: [
327 CastInt16ToInt32(func::CastInt16ToInt32),
328 CastInt32ToOid(func::CastInt32ToOid),
329 CastOidToRegProc(func::CastOidToRegProc),
330 ],
331 (Int16, RegType) => Implicit: [
332 CastInt16ToInt32(func::CastInt16ToInt32),
333 CastInt32ToOid(func::CastInt32ToOid),
334 CastOidToRegType(func::CastOidToRegType),
335 ],
336 (Int16, String) => Assignment: CastInt16ToString(func::CastInt16ToString),
337
338 (Int32, Bool) => Explicit: CastInt32ToBool(func::CastInt32ToBool),
340 (Int32, Oid) => Implicit: CastInt32ToOid(func::CastInt32ToOid),
341 (Int32, RegClass) => Implicit: [
342 CastInt32ToOid(func::CastInt32ToOid),
343 CastOidToRegClass(func::CastOidToRegClass),
344 ],
345 (Int32, RegProc) => Implicit: [
346 CastInt32ToOid(func::CastInt32ToOid),
347 CastOidToRegProc(func::CastOidToRegProc),
348 ],
349 (Int32, RegType) => Implicit: [
350 CastInt32ToOid(func::CastInt32ToOid),
351 CastOidToRegType(func::CastOidToRegType),
352 ],
353 (Int32, PgLegacyChar) => Explicit: CastInt32ToPgLegacyChar(func::CastInt32ToPgLegacyChar),
354 (Int32, Int16) => Assignment: CastInt32ToInt16(func::CastInt32ToInt16),
355 (Int32, Int64) => Implicit: CastInt32ToInt64(func::CastInt32ToInt64),
356 (Int32, UInt16) => Assignment: CastInt32ToUint16(func::CastInt32ToUint16),
357 (Int32, UInt32) => Assignment: CastInt32ToUint32(func::CastInt32ToUint32),
358 (Int32, UInt64) => Assignment: CastInt32ToUint64(func::CastInt32ToUint64),
359 (Int32, Float32) => Implicit: CastInt32ToFloat32(func::CastInt32ToFloat32),
360 (Int32, Float64) => Implicit: CastInt32ToFloat64(func::CastInt32ToFloat64),
361 (Int32, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
362 let s = to_type.unwrap_numeric_max_scale();
363 Some(move |e: HirScalarExpr| e.call_unary(CastInt32ToNumeric(func::CastInt32ToNumeric(s))))
364 }),
365 (Int32, String) => Assignment: CastInt32ToString(func::CastInt32ToString),
366
367 (Int64, Bool) => Explicit: CastInt64ToBool(func::CastInt64ToBool),
369 (Int64, Int16) => Assignment: CastInt64ToInt16(func::CastInt64ToInt16),
370 (Int64, Int32) => Assignment: CastInt64ToInt32(func::CastInt64ToInt32),
371 (Int64, UInt16) => Assignment: CastInt64ToUint16(func::CastInt64ToUint16),
372 (Int64, UInt32) => Assignment: CastInt64ToUint32(func::CastInt64ToUint32),
373 (Int64, UInt64) => Assignment: CastInt64ToUint64(func::CastInt64ToUint64),
374 (Int64, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
375 let s = to_type.unwrap_numeric_max_scale();
376 Some(move |e: HirScalarExpr| e.call_unary(CastInt64ToNumeric(func::CastInt64ToNumeric(s))))
377 }),
378 (Int64, Float32) => Implicit: CastInt64ToFloat32(func::CastInt64ToFloat32),
379 (Int64, Float64) => Implicit: CastInt64ToFloat64(func::CastInt64ToFloat64),
380 (Int64, Oid) => Implicit: CastInt64ToOid(func::CastInt64ToOid),
381 (Int64, RegClass) => Implicit: [
382 CastInt64ToOid(func::CastInt64ToOid),
383 CastOidToRegClass(func::CastOidToRegClass),
384 ],
385 (Int64, RegProc) => Implicit: [
386 CastInt64ToOid(func::CastInt64ToOid),
387 CastOidToRegProc(func::CastOidToRegProc),
388 ],
389 (Int64, RegType) => Implicit: [
390 CastInt64ToOid(func::CastInt64ToOid),
391 CastOidToRegType(func::CastOidToRegType),
392 ],
393 (Int64, String) => Assignment: CastInt64ToString(func::CastInt64ToString),
394
395 (UInt16, UInt32) => Implicit: CastUint16ToUint32(func::CastUint16ToUint32),
397 (UInt16, UInt64) => Implicit: CastUint16ToUint64(func::CastUint16ToUint64),
398 (UInt16, Int16) => Assignment: CastUint16ToInt16(func::CastUint16ToInt16),
399 (UInt16, Int32) => Implicit: CastUint16ToInt32(func::CastUint16ToInt32),
400 (UInt16, Int64) => Implicit: CastUint16ToInt64(func::CastUint16ToInt64),
401 (UInt16, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
402 let s = to_type.unwrap_numeric_max_scale();
403 Some(move |e: HirScalarExpr| e.call_unary(CastUint16ToNumeric(func::CastUint16ToNumeric(s))))
404 }),
405 (UInt16, Float32) => Implicit: CastUint16ToFloat32(func::CastUint16ToFloat32),
406 (UInt16, Float64) => Implicit: CastUint16ToFloat64(func::CastUint16ToFloat64),
407 (UInt16, String) => Assignment: CastUint16ToString(func::CastUint16ToString),
408
409 (UInt32, UInt16) => Assignment: CastUint32ToUint16(func::CastUint32ToUint16),
411 (UInt32, UInt64) => Implicit: CastUint32ToUint64(func::CastUint32ToUint64),
412 (UInt32, Int16) => Assignment: CastUint32ToInt16(func::CastUint32ToInt16),
413 (UInt32, Int32) => Assignment: CastUint32ToInt32(func::CastUint32ToInt32),
414 (UInt32, Int64) => Implicit: CastUint32ToInt64(func::CastUint32ToInt64),
415 (UInt32, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
416 let s = to_type.unwrap_numeric_max_scale();
417 Some(move |e: HirScalarExpr| e.call_unary(CastUint32ToNumeric(func::CastUint32ToNumeric(s))))
418 }),
419 (UInt32, Float32) => Implicit: CastUint32ToFloat32(func::CastUint32ToFloat32),
420 (UInt32, Float64) => Implicit: CastUint32ToFloat64(func::CastUint32ToFloat64),
421 (UInt32, String) => Assignment: CastUint32ToString(func::CastUint32ToString),
422
423 (UInt64, UInt16) => Assignment: CastUint64ToUint16(func::CastUint64ToUint16),
425 (UInt64, UInt32) => Assignment: CastUint64ToUint32(func::CastUint64ToUint32),
426 (UInt64, Int16) => Assignment: CastUint64ToInt16(func::CastUint64ToInt16),
427 (UInt64, Int32) => Assignment: CastUint64ToInt32(func::CastUint64ToInt32),
428 (UInt64, Int64) => Assignment: CastUint64ToInt64(func::CastUint64ToInt64),
429 (UInt64, Numeric) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
430 let s = to_type.unwrap_numeric_max_scale();
431 Some(move |e: HirScalarExpr| e.call_unary(CastUint64ToNumeric(func::CastUint64ToNumeric(s))))
432 }),
433 (UInt64, Float32) => Implicit: CastUint64ToFloat32(func::CastUint64ToFloat32),
434 (UInt64, Float64) => Implicit: CastUint64ToFloat64(func::CastUint64ToFloat64),
435 (UInt64, String) => Assignment: CastUint64ToString(func::CastUint64ToString),
436
437 (MzTimestamp, String) => Assignment: CastMzTimestampToString(func::CastMzTimestampToString),
439 (MzTimestamp, Timestamp) => Assignment: CastMzTimestampToTimestamp(func::CastMzTimestampToTimestamp),
440 (MzTimestamp, TimestampTz) => Assignment: CastMzTimestampToTimestampTz(func::CastMzTimestampToTimestampTz),
441 (String, MzTimestamp) => Assignment: CastStringToMzTimestamp(func::CastStringToMzTimestamp),
442 (UInt64, MzTimestamp) => Implicit: CastUint64ToMzTimestamp(func::CastUint64ToMzTimestamp),
443 (UInt32, MzTimestamp) => Implicit: CastUint32ToMzTimestamp(func::CastUint32ToMzTimestamp),
444 (Int64, MzTimestamp) => Implicit: CastInt64ToMzTimestamp(func::CastInt64ToMzTimestamp),
445 (Int32, MzTimestamp) => Implicit: CastInt32ToMzTimestamp(func::CastInt32ToMzTimestamp),
446 (Numeric, MzTimestamp) => Implicit: CastNumericToMzTimestamp(func::CastNumericToMzTimestamp),
447 (Timestamp, MzTimestamp) => Implicit: CastTimestampToMzTimestamp(func::CastTimestampToMzTimestamp),
448 (TimestampTz, MzTimestamp) => Implicit: CastTimestampTzToMzTimestamp(func::CastTimestampTzToMzTimestamp),
449 (Date, MzTimestamp) => Implicit: CastDateToMzTimestamp(func::CastDateToMzTimestamp),
450
451 (Oid, Int32) => Assignment: CastOidToInt32(func::CastOidToInt32),
453 (Oid, Int64) => Assignment: CastOidToInt32(func::CastOidToInt32),
454 (Oid, String) => Explicit: CastOidToString(func::CastOidToString),
455 (Oid, RegClass) => Implicit: CastOidToRegClass(func::CastOidToRegClass),
456 (Oid, RegProc) => Implicit: CastOidToRegProc(func::CastOidToRegProc),
457 (Oid, RegType) => Implicit: CastOidToRegType(func::CastOidToRegType),
458
459 (RegClass, Oid) => Implicit: CastRegClassToOid(func::CastRegClassToOid),
461 (RegClass, String) => Explicit: sql_impl_cast(®CLASS_TO_STRING),
462
463 (RegProc, Oid) => Implicit: CastRegProcToOid(func::CastRegProcToOid),
465 (RegProc, String) => Explicit: sql_impl_cast(®PROC_TO_STRING),
466
467 (RegType, Oid) => Implicit: CastRegTypeToOid(func::CastRegTypeToOid),
469 (RegType, String) => Explicit: sql_impl_cast(®TYPE_TO_STRING),
470
471 (Float32, Int16) => Assignment: CastFloat32ToInt16(func::CastFloat32ToInt16),
473 (Float32, Int32) => Assignment: CastFloat32ToInt32(func::CastFloat32ToInt32),
474 (Float32, Int64) => Assignment: CastFloat32ToInt64(func::CastFloat32ToInt64),
475 (Float32, UInt16) => Assignment: CastFloat32ToUint16(func::CastFloat32ToUint16),
476 (Float32, UInt32) => Assignment: CastFloat32ToUint32(func::CastFloat32ToUint32),
477 (Float32, UInt64) => Assignment: CastFloat32ToUint64(func::CastFloat32ToUint64),
478 (Float32, Float64) => Implicit: CastFloat32ToFloat64(func::CastFloat32ToFloat64),
479 (Float32, Numeric) => Assignment: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
480 let s = to_type.unwrap_numeric_max_scale();
481 Some(move |e: HirScalarExpr| e.call_unary(CastFloat32ToNumeric(func::CastFloat32ToNumeric(s))))
482 }),
483 (Float32, String) => Assignment: CastFloat32ToString(func::CastFloat32ToString),
484
485 (Float64, Int16) => Assignment: CastFloat64ToInt16(func::CastFloat64ToInt16),
487 (Float64, Int32) => Assignment: CastFloat64ToInt32(func::CastFloat64ToInt32),
488 (Float64, Int64) => Assignment: CastFloat64ToInt64(func::CastFloat64ToInt64),
489 (Float64, UInt16) => Assignment: CastFloat64ToUint16(func::CastFloat64ToUint16),
490 (Float64, UInt32) => Assignment: CastFloat64ToUint32(func::CastFloat64ToUint32),
491 (Float64, UInt64) => Assignment: CastFloat64ToUint64(func::CastFloat64ToUint64),
492 (Float64, Float32) => Assignment: CastFloat64ToFloat32(func::CastFloat64ToFloat32),
493 (Float64, Numeric) => Assignment: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
494 let s = to_type.unwrap_numeric_max_scale();
495 Some(move |e: HirScalarExpr| e.call_unary(CastFloat64ToNumeric(func::CastFloat64ToNumeric(s))))
496 }),
497 (Float64, String) => Assignment: CastFloat64ToString(func::CastFloat64ToString),
498
499 (Date, Timestamp) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
501 let p = to_type.unwrap_timestamp_precision();
502 Some(move |e: HirScalarExpr| e.call_unary(CastDateToTimestamp(func::CastDateToTimestamp(p))))
503 }),
504 (Date, TimestampTz) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
505 let p = to_type.unwrap_timestamp_precision();
506 Some(move |e: HirScalarExpr| e.call_unary(CastDateToTimestampTz(func::CastDateToTimestampTz(p))))
507 }),
508 (Date, String) => Assignment: CastDateToString(func::CastDateToString),
509
510 (Time, Interval) => Implicit: CastTimeToInterval(func::CastTimeToInterval),
512 (Time, String) => Assignment: CastTimeToString(func::CastTimeToString),
513
514 (Timestamp, Date) => Assignment: CastTimestampToDate(func::CastTimestampToDate),
516 (Timestamp, TimestampTz) => Implicit: CastTemplate::new(|_ecx, _ccx, from_type, to_type| {
517 let from = from_type.unwrap_timestamp_precision();
518 let to = to_type.unwrap_timestamp_precision();
519 Some(move |e: HirScalarExpr| e.call_unary(CastTimestampToTimestampTz(func::CastTimestampToTimestampTz{from, to})))
520 }),
521 (Timestamp, Timestamp) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, to_type| {
522 let from = from_type.unwrap_timestamp_precision();
523 let to = to_type.unwrap_timestamp_precision();
524 Some(move |e: HirScalarExpr| e.call_unary(AdjustTimestampPrecision(func::AdjustTimestampPrecision{from, to})))
525 }),
526 (Timestamp, Time) => Assignment: CastTimestampToTime(func::CastTimestampToTime),
527 (Timestamp, String) => Assignment: CastTimestampToString(func::CastTimestampToString),
528
529 (TimestampTz, Date) => Assignment: CastTimestampTzToDate(func::CastTimestampTzToDate),
531 (TimestampTz, Timestamp) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, to_type| {
532 let from = from_type.unwrap_timestamp_precision();
533 let to = to_type.unwrap_timestamp_precision();
534 Some(move |e: HirScalarExpr| e.call_unary(CastTimestampTzToTimestamp(func::CastTimestampTzToTimestamp{from, to})))
535 }),
536 (TimestampTz, TimestampTz) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, to_type| {
537 let from = from_type.unwrap_timestamp_precision();
538 let to = to_type.unwrap_timestamp_precision();
539 Some(move |e: HirScalarExpr| e.call_unary(AdjustTimestampTzPrecision(func::AdjustTimestampTzPrecision{from, to})))
540 }),
541 (TimestampTz, Time) => Assignment: CastTimestampTzToTime(func::CastTimestampTzToTime),
542 (TimestampTz, String) => Assignment: CastTimestampTzToString(func::CastTimestampTzToString),
543
544 (Interval, Time) => Assignment: CastIntervalToTime(func::CastIntervalToTime),
546 (Interval, String) => Assignment: CastIntervalToString(func::CastIntervalToString),
547
548 (Bytes, String) => Assignment: CastBytesToString(func::CastBytesToString),
550
551 (String, Bool) => Explicit: CastStringToBool(func::CastStringToBool),
553 (String, Int16) => Explicit: CastStringToInt16(func::CastStringToInt16),
554 (String, Int32) => Explicit: CastStringToInt32(func::CastStringToInt32),
555 (String, Int64) => Explicit: CastStringToInt64(func::CastStringToInt64),
556 (String, UInt16) => Explicit: CastStringToUint16(func::CastStringToUint16),
557 (String, UInt32) => Explicit: CastStringToUint32(func::CastStringToUint32),
558 (String, UInt64) => Explicit: CastStringToUint64(func::CastStringToUint64),
559 (String, Oid) => Explicit: CastStringToOid(func::CastStringToOid),
560
561 (String, RegClass) => Explicit: sql_impl_cast_per_context(
570 &[
571 (CastContext::Explicit, &STRING_TO_REGCLASS_EXPLICIT),
572 (CastContext::Coerced, &STRING_TO_REGCLASS_COERCED)
573 ]
574 ),
575 (String, RegProc) => Explicit: sql_impl_cast(&STRING_TO_REGPROC),
576 (String, RegType) => Explicit: sql_impl_cast(&STRING_TO_REGTYPE),
577
578 (String, Float32) => Explicit: CastStringToFloat32(func::CastStringToFloat32),
579 (String, Float64) => Explicit: CastStringToFloat64(func::CastStringToFloat64),
580 (String, Numeric) => Explicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
581 let s = to_type.unwrap_numeric_max_scale();
582 Some(move |e: HirScalarExpr| e.call_unary(CastStringToNumeric(func::CastStringToNumeric(s))))
583 }),
584 (String, Date) => Explicit: CastStringToDate(func::CastStringToDate),
585 (String, Time) => Explicit: CastStringToTime(func::CastStringToTime),
586 (String, Timestamp) => Explicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
587 let p = to_type.unwrap_timestamp_precision();
588 Some(move |e: HirScalarExpr| e.call_unary(CastStringToTimestamp(func::CastStringToTimestamp(p))))
589 }),
590 (String, TimestampTz) => Explicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
591 let p = to_type.unwrap_timestamp_precision();
592 Some(move |e: HirScalarExpr| e.call_unary(CastStringToTimestampTz(func::CastStringToTimestampTz(p))))
593 }),
594 (String, Interval) => Explicit: CastStringToInterval(func::CastStringToInterval),
595 (String, Bytes) => Explicit: CastStringToBytes(func::CastStringToBytes),
596 (String, Jsonb) => Explicit: CastStringToJsonb(func::CastStringToJsonb),
597 (String, Uuid) => Explicit: CastStringToUuid(func::CastStringToUuid),
598 (String, Array) => Explicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
599 let return_ty = to_type.clone();
600 let to_el_type = to_type.unwrap_array_element_type();
601 let cast_expr = plan_hypothetical_cast(ecx, ccx, from_type, to_el_type)?;
602 Some(|e: HirScalarExpr| e.call_unary(UnaryFunc::CastStringToArray(func::CastStringToArray {
603 return_ty,
604 cast_expr: Box::new(cast_expr),
605 })))
606 }),
607 (String, List) => Explicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
608 let return_ty = to_type.clone();
609 let to_el_type = to_type.unwrap_list_element_type();
610 let cast_expr = plan_hypothetical_cast(ecx, ccx, from_type, to_el_type)?;
611 Some(|e: HirScalarExpr| e.call_unary(UnaryFunc::CastStringToList(func::CastStringToList {
612 return_ty,
613 cast_expr: Box::new(cast_expr),
614 })))
615 }),
616 (String, Map) => Explicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
617 let return_ty = to_type.clone();
618 let to_val_type = to_type.unwrap_map_value_type();
619 let cast_expr = plan_hypothetical_cast(ecx, ccx, from_type, to_val_type)?;
620 Some(|e: HirScalarExpr| e.call_unary(UnaryFunc::CastStringToMap(func::CastStringToMap {
621 return_ty,
622 cast_expr: Box::new(cast_expr),
623 })))
624 }),
625 (String, Range) => Explicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
626 let return_ty = to_type.clone();
627 let to_el_type = to_type.unwrap_range_element_type();
628 let cast_expr = plan_hypothetical_cast(ecx, ccx, from_type, to_el_type)?;
629 Some(|e: HirScalarExpr| e.call_unary(UnaryFunc::CastStringToRange(func::CastStringToRange {
630 return_ty,
631 cast_expr: Box::new(cast_expr),
632 })))
633 }),
634 (String, Int2Vector) => Explicit: CastStringToInt2Vector(func::CastStringToInt2Vector),
635 (String, Char) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
636 let length = to_type.unwrap_char_length();
637 Some(move |e: HirScalarExpr| e.call_unary(CastStringToChar(func::CastStringToChar {length, fail_on_len: ccx != CastContext::Explicit})))
638 }),
639 (String, VarChar) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
640 let length = to_type.unwrap_varchar_max_length();
641 Some(move |e: HirScalarExpr| e.call_unary(CastStringToVarChar(func::CastStringToVarChar {length, fail_on_len: ccx != CastContext::Explicit})))
642 }),
643 (String, PgLegacyChar) => Assignment: CastStringToPgLegacyChar(func::CastStringToPgLegacyChar),
644 (Char, String) => Implicit: CastCharToString(func::CastCharToString),
646 (Char, Char) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
647 let length = to_type.unwrap_char_length();
648 Some(move |e: HirScalarExpr| e.call_unary(CastStringToChar(func::CastStringToChar {length, fail_on_len: ccx != CastContext::Explicit})))
649 }),
650 (Char, VarChar) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
651 let length = to_type.unwrap_varchar_max_length();
652 Some(move |e: HirScalarExpr| e.call_unary(CastStringToVarChar(func::CastStringToVarChar {length, fail_on_len: ccx != CastContext::Explicit})))
653 }),
654 (Char, PgLegacyChar) => Assignment: CastStringToPgLegacyChar(func::CastStringToPgLegacyChar),
655
656 (VarChar, String) => Implicit: CastVarCharToString(func::CastVarCharToString),
658 (VarChar, Char) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
659 let length = to_type.unwrap_char_length();
660 Some(move |e: HirScalarExpr| e.call_unary(CastStringToChar(func::CastStringToChar {length, fail_on_len: ccx != CastContext::Explicit})))
661 }),
662 (VarChar, VarChar) => Implicit: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
663 let length = to_type.unwrap_varchar_max_length();
664 Some(move |e: HirScalarExpr| e.call_unary(CastStringToVarChar(func::CastStringToVarChar {length, fail_on_len: ccx != CastContext::Explicit})))
665 }),
666 (VarChar, PgLegacyChar) => Assignment: CastStringToPgLegacyChar(func::CastStringToPgLegacyChar),
667
668 (PgLegacyChar, String) => Implicit: CastPgLegacyCharToString(func::CastPgLegacyCharToString),
670 (PgLegacyChar, Char) => Assignment: CastPgLegacyCharToChar(func::CastPgLegacyCharToChar),
671 (PgLegacyChar, VarChar) => Assignment: CastPgLegacyCharToVarChar(func::CastPgLegacyCharToVarChar),
672 (PgLegacyChar, Int32) => Explicit: CastPgLegacyCharToInt32(func::CastPgLegacyCharToInt32),
673
674 (PgLegacyName, String) => Implicit: CastVarCharToString(func::CastVarCharToString),
678 (PgLegacyName, Char) => Assignment: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
679 let length = to_type.unwrap_char_length();
680 Some(move |e: HirScalarExpr| e.call_unary(CastStringToChar(func::CastStringToChar {length, fail_on_len: ccx != CastContext::Explicit})))
681 }),
682 (PgLegacyName, VarChar) => Assignment: CastTemplate::new(|_ecx, ccx, _from_type, to_type| {
683 let length = to_type.unwrap_varchar_max_length();
684 Some(move |e: HirScalarExpr| e.call_unary(CastStringToVarChar(func::CastStringToVarChar {length, fail_on_len: ccx != CastContext::Explicit})))
685 }),
686 (String, PgLegacyName) => Implicit: CastStringToPgLegacyName(func::CastStringToPgLegacyName),
687 (Char, PgLegacyName) => Implicit: CastStringToPgLegacyName(func::CastStringToPgLegacyName),
688 (VarChar, PgLegacyName) => Implicit: CastStringToPgLegacyName(func::CastStringToPgLegacyName),
689
690 (Record, String) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, _to_type| {
692 let ty = from_type.clone();
693 Some(|e: HirScalarExpr| e.call_unary(CastRecordToString(func::CastRecordToString { ty })))
694 }),
695 (Record, Record) => Implicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
696 if from_type.unwrap_record_element_type().len() != to_type.unwrap_record_element_type().len() {
697 return None;
698 }
699
700 if let (l @ ScalarType::Record {custom_id: Some(..), ..}, r) = (from_type, to_type) {
701 if ccx == CastContext::Implicit && l != r {
703 return None;
704 }
705 }
706
707 let cast_exprs = from_type.unwrap_record_element_type()
708 .iter()
709 .zip_eq(to_type.unwrap_record_element_type())
710 .map(|(f, t)| plan_hypothetical_cast(ecx, ccx, f, t))
711 .collect::<Option<Box<_>>>()?;
712 let to = to_type.clone();
713 Some(|e: HirScalarExpr| e.call_unary(CastRecord1ToRecord2(func::CastRecord1ToRecord2 { return_ty: to, cast_exprs })))
714 }),
715
716 (Array, String) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, _to_type| {
718 let ty = from_type.clone();
719 Some(|e: HirScalarExpr| e.call_unary(CastArrayToString(func::CastArrayToString { ty })))
720 }),
721 (Array, List) => Explicit: CastArrayToListOneDim(func::CastArrayToListOneDim),
722 (Array, Array) => Explicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
723 let inner_from_type = from_type.unwrap_array_element_type();
724 let inner_to_type = to_type.unwrap_array_element_type();
725 let cast_expr = plan_hypothetical_cast(ecx, ccx, inner_from_type, inner_to_type)?;
726 let return_ty = to_type.clone();
727
728 Some(move |e: HirScalarExpr| e.call_unary(CastArrayToArray(func::CastArrayToArray { return_ty, cast_expr: Box::new(cast_expr) })))
729 }),
730
731 (Int2Vector, Array) => Implicit: CastTemplate::new(|_ecx, _ccx, _from_type, _to_type| {
733 Some(|e: HirScalarExpr| e.call_unary(UnaryFunc::CastInt2VectorToArray(func::CastInt2VectorToArray)))
734 }),
735 (Int2Vector, String) => Explicit: CastInt2VectorToString(func::CastInt2VectorToString),
736
737 (List, String) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, _to_type| {
739 let ty = from_type.clone();
740 Some(|e: HirScalarExpr| e.call_unary(CastListToString(func::CastListToString { ty })))
741 }),
742 (List, List) => Implicit: CastTemplate::new(|ecx, ccx, from_type, to_type| {
743
744 if let (l @ ScalarType::List {custom_id: Some(..), ..}, r) = (from_type, to_type) {
745 if ccx == CastContext::Implicit && !l.base_eq(r) {
747 return None;
748 }
749 }
750
751 let return_ty = to_type.clone();
752 let from_el_type = from_type.unwrap_list_element_type();
753 let to_el_type = to_type.unwrap_list_element_type();
754 let cast_expr = plan_hypothetical_cast(ecx, ccx, from_el_type, to_el_type)?;
755 Some(|e: HirScalarExpr| e.call_unary(UnaryFunc::CastList1ToList2(func::CastList1ToList2 {
756 return_ty,
757 cast_expr: Box::new(cast_expr),
758 })))
759 }),
760
761 (Map, String) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, _to_type| {
763 let ty = from_type.clone();
764 Some(|e: HirScalarExpr| e.call_unary(CastMapToString(func::CastMapToString { ty })))
765 }),
766
767 (Jsonb, Bool) => Explicit: CastJsonbToBool(func::CastJsonbToBool),
769 (Jsonb, Int16) => Explicit: CastJsonbToInt16(func::CastJsonbToInt16),
770 (Jsonb, Int32) => Explicit: CastJsonbToInt32(func::CastJsonbToInt32),
771 (Jsonb, Int64) => Explicit: CastJsonbToInt64(func::CastJsonbToInt64),
772 (Jsonb, Float32) => Explicit: CastJsonbToFloat32(func::CastJsonbToFloat32),
773 (Jsonb, Float64) => Explicit: CastJsonbToFloat64(func::CastJsonbToFloat64),
774 (Jsonb, Numeric) => Explicit: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
775 let s = to_type.unwrap_numeric_max_scale();
776 Some(move |e: HirScalarExpr| e.call_unary(CastJsonbToNumeric(func::CastJsonbToNumeric(s))))
777 }),
778 (Jsonb, String) => Assignment: CastJsonbToString(func::CastJsonbToString),
779
780 (Uuid, String) => Assignment: CastUuidToString(func::CastUuidToString),
782
783 (Numeric, Numeric) => Assignment: CastTemplate::new(|_ecx, _ccx, _from_type, to_type| {
785 let scale = to_type.unwrap_numeric_max_scale();
786 Some(move |e: HirScalarExpr| match scale {
787 None => e,
788 Some(scale) => e.call_unary(UnaryFunc::AdjustNumericScale(func::AdjustNumericScale(scale))),
789 })
790 }),
791 (Numeric, Float32) => Implicit: CastNumericToFloat32(func::CastNumericToFloat32),
792 (Numeric, Float64) => Implicit: CastNumericToFloat64(func::CastNumericToFloat64),
793 (Numeric, Int16) => Assignment: CastNumericToInt16(func::CastNumericToInt16),
794 (Numeric, Int32) => Assignment: CastNumericToInt32(func::CastNumericToInt32),
795 (Numeric, Int64) => Assignment: CastNumericToInt64(func::CastNumericToInt64),
796 (Numeric, UInt16) => Assignment: CastNumericToUint16(func::CastNumericToUint16),
797 (Numeric, UInt32) => Assignment: CastNumericToUint32(func::CastNumericToUint32),
798 (Numeric, UInt64) => Assignment: CastNumericToUint64(func::CastNumericToUint64),
799 (Numeric, String) => Assignment: CastNumericToString(func::CastNumericToString),
800
801 (Range, String) => Assignment: CastTemplate::new(|_ecx, _ccx, from_type, _to_type| {
803 let ty = from_type.clone();
804 Some(|e: HirScalarExpr| e.call_unary(CastRangeToString(func::CastRangeToString { ty })))
805 }),
806
807 (MzAclItem, String) => Explicit: sql_impl_cast("(
809 SELECT
810 (CASE
811 WHEN grantee_role_id = 'p' THEN ''
812 ELSE COALESCE(grantee_role.name, grantee_role_id)
813 END)
814 || '='
815 || mz_internal.mz_aclitem_privileges($1)
816 || '/'
817 || COALESCE(grantor_role.name, grantor_role_id)
818 FROM
819 (SELECT mz_internal.mz_aclitem_grantee($1) AS grantee_role_id),
820 (SELECT mz_internal.mz_aclitem_grantor($1) AS grantor_role_id)
821 LEFT JOIN mz_catalog.mz_roles AS grantee_role ON grantee_role_id = grantee_role.id
822 LEFT JOIN mz_catalog.mz_roles AS grantor_role ON grantor_role_id = grantor_role.id
823 )"),
824 (MzAclItem, AclItem) => Explicit: sql_impl_cast("(
825 SELECT makeaclitem(
826 (CASE mz_internal.mz_aclitem_grantee($1)
827 WHEN 'p' THEN 0
828 ELSE (SELECT oid FROM mz_catalog.mz_roles WHERE id = mz_internal.mz_aclitem_grantee($1))
829 END),
830 (SELECT oid FROM mz_catalog.mz_roles WHERE id = mz_internal.mz_aclitem_grantor($1)),
831 (SELECT array_to_string(mz_internal.mz_format_privileges(mz_internal.mz_aclitem_privileges($1)), ',')),
832 -- GRANT OPTION isn't implemented so we hardcode false.
833 false
834 )
835 )"),
836
837 (AclItem, String) => Explicit: sql_impl_cast("(
839 SELECT
840 (CASE grantee_oid
841 WHEN 0 THEN ''
842 ELSE COALESCE(grantee_role.name, grantee_oid::text)
843 END)
844 || '='
845 || mz_internal.aclitem_privileges($1)
846 || '/'
847 || COALESCE(grantor_role.name, grantor_oid::text)
848 FROM
849 (SELECT mz_internal.aclitem_grantee($1) AS grantee_oid),
850 (SELECT mz_internal.aclitem_grantor($1) AS grantor_oid)
851 LEFT JOIN mz_catalog.mz_roles AS grantee_role ON grantee_oid = grantee_role.oid
852 LEFT JOIN mz_catalog.mz_roles AS grantor_role ON grantor_oid = grantor_role.oid
853 )"),
854 (AclItem, MzAclItem) => Explicit: sql_impl_cast("(
855 SELECT mz_internal.make_mz_aclitem(
856 (CASE mz_internal.aclitem_grantee($1)
857 WHEN 0 THEN 'p'
858 ELSE (SELECT id FROM mz_catalog.mz_roles WHERE oid = mz_internal.aclitem_grantee($1))
859 END),
860 (SELECT id FROM mz_catalog.mz_roles WHERE oid = mz_internal.aclitem_grantor($1)),
861 (SELECT array_to_string(mz_internal.mz_format_privileges(mz_internal.aclitem_privileges($1)), ','))
862 )
863 )")
864 }
865 },
866);
867
868fn get_cast(
871 ecx: &ExprContext,
872 ccx: CastContext,
873 from: &ScalarType,
874 to: &ScalarType,
875) -> Option<Cast> {
876 use CastContext::*;
877
878 if from == to || (ccx == Implicit && from.base_eq(to)) {
879 return Some(Box::new(|expr| expr));
880 }
881
882 let imp = VALID_CASTS.get(&(from.into(), to.into()))?;
883 let template = if ccx >= imp.context {
884 Some(&imp.template)
885 } else {
886 None
887 };
888 template.and_then(|template| (template.0)(ecx, ccx, from, to))
889}
890
891pub fn to_string(ecx: &ExprContext, expr: HirScalarExpr) -> HirScalarExpr {
895 plan_cast(ecx, CastContext::Explicit, expr, &ScalarType::String).expect("cast known to exist")
896}
897
898pub fn to_jsonb(ecx: &ExprContext, expr: HirScalarExpr) -> HirScalarExpr {
909 use ScalarType::*;
910
911 match ecx.scalar_type(&expr) {
912 Bool | Jsonb | Numeric { .. } => {
913 expr.call_unary(UnaryFunc::CastJsonbableToJsonb(func::CastJsonbableToJsonb))
914 }
915 Int16 | Int32 | Float32 | Float64 => plan_cast(
916 ecx,
917 CastContext::Explicit,
918 expr,
919 &Numeric { max_scale: None },
920 )
921 .expect("cast known to exist")
922 .call_unary(UnaryFunc::CastJsonbableToJsonb(func::CastJsonbableToJsonb)),
923 Record { fields, .. } => {
924 let mut exprs = vec![];
925 for (i, (name, _ty)) in fields.iter().enumerate() {
926 exprs.push(HirScalarExpr::literal(
927 Datum::String(name.as_str()),
928 ScalarType::String,
929 ));
930 exprs.push(to_jsonb(
931 ecx,
932 expr.clone()
933 .call_unary(UnaryFunc::RecordGet(func::RecordGet(i))),
934 ));
935 }
936 HirScalarExpr::call_variadic(VariadicFunc::JsonbBuildObject, exprs)
937 }
938 ref ty @ List {
939 ref element_type, ..
940 }
941 | ref ty @ Array(ref element_type) => {
942 let qcx = QueryContext::root(ecx.qcx.scx, ecx.qcx.lifetime);
945 let ecx = ExprContext {
946 qcx: &qcx,
947 name: "to_jsonb",
948 scope: &Scope::empty(),
949 relation_type: &RelationType::new(vec![element_type.clone().nullable(true)]),
950 allow_aggregates: false,
951 allow_subqueries: false,
952 allow_parameters: false,
953 allow_windows: false,
954 };
955
956 let cast_element = to_jsonb(&ecx, HirScalarExpr::column(0));
959 let cast_element = cast_element
960 .lower_uncorrelated()
961 .expect("to_jsonb does not produce correlated expressions on uncorrelated input");
962
963 let func = match ty {
967 List { .. } => UnaryFunc::CastListToJsonb(CastListToJsonb {
968 cast_element: Box::new(cast_element),
969 }),
970 Array { .. } => UnaryFunc::CastArrayToJsonb(CastArrayToJsonb {
971 cast_element: Box::new(cast_element),
972 }),
973 _ => unreachable!("validated above"),
974 };
975
976 expr.call_unary(func)
977 }
978 _ => to_string(ecx, expr)
979 .call_unary(UnaryFunc::CastJsonbableToJsonb(func::CastJsonbableToJsonb)),
980 }
981}
982
983pub fn guess_best_common_type(
992 ecx: &ExprContext,
993 types: &[CoercibleScalarType],
994) -> Result<ScalarType, PlanError> {
995 if let Some(CoercibleScalarType::Record(field_tys)) = types.first() {
1004 if types
1005 .iter()
1006 .all(|t| matches!(t, CoercibleScalarType::Record(fts) if field_tys.len() == fts.len()))
1007 {
1008 let mut fields = vec![];
1009 for i in 0..field_tys.len() {
1010 let name = ColumnName::from(format!("f{}", fields.len() + 1));
1011 let mut guesses = vec![];
1012 let mut nullable = false;
1013 for ty in types {
1014 let field_ty = match ty {
1015 CoercibleScalarType::Record(fts) => fts[i].clone(),
1016 _ => unreachable!(),
1017 };
1018 if field_ty.nullable() {
1019 nullable = true;
1020 }
1021 guesses.push(field_ty.scalar_type());
1022 }
1023 let guess = guess_best_common_type(ecx, &guesses)?;
1024 fields.push((name, guess.nullable(nullable)));
1025 }
1026 return Ok(ScalarType::Record {
1027 fields: fields.into(),
1028 custom_id: None,
1029 });
1030 }
1031 }
1032
1033 let mut types: Vec<_> = types.into_iter().filter_map(|v| v.as_coerced()).collect();
1035
1036 let contains_int = types
1038 .iter()
1039 .any(|t| matches!(t, ScalarType::Int16 | ScalarType::Int32 | ScalarType::Int64));
1040
1041 for t in types.iter_mut() {
1042 if contains_int
1043 && matches!(
1044 t,
1045 ScalarType::UInt16 | ScalarType::UInt32 | ScalarType::UInt64
1046 )
1047 {
1048 *t = t.near_match().expect("unsigned ints have near matches")
1049 }
1050 }
1051
1052 let mut types = types.iter();
1053
1054 let mut candidate = match types.next() {
1055 None => return Ok(ScalarType::String),
1057 Some(t) => t,
1059 };
1060
1061 let preferred_type = TypeCategory::from_type(candidate).preferred_type();
1062
1063 for typ in types {
1064 if TypeCategory::from_type(candidate) != TypeCategory::from_type(typ) {
1065 sql_bail!(
1067 "{} types {} and {} cannot be matched",
1068 ecx.name,
1069 ecx.humanize_scalar_type(candidate, false),
1070 ecx.humanize_scalar_type(typ, false),
1071 );
1072 };
1073
1074 if preferred_type.as_ref() != Some(candidate)
1076 && can_cast(ecx, CastContext::Implicit, candidate, typ)
1077 && !can_cast(ecx, CastContext::Implicit, typ, candidate)
1078 {
1079 candidate = typ;
1084 }
1085 }
1086 Ok(candidate.without_modifiers())
1087}
1088
1089pub fn plan_coerce<'a>(
1090 ecx: &'a ExprContext,
1091 e: CoercibleScalarExpr,
1092 coerce_to: &ScalarType,
1093) -> Result<HirScalarExpr, PlanError> {
1094 use CoercibleScalarExpr::*;
1095
1096 Ok(match e {
1097 Coerced(e) => e,
1098
1099 LiteralNull => HirScalarExpr::literal_null(coerce_to.clone()),
1100
1101 LiteralString(s) => {
1102 let lit = HirScalarExpr::literal(Datum::String(&s), ScalarType::String);
1103 let coerce_to_base = &coerce_to.without_modifiers();
1108 plan_cast(ecx, CastContext::Coerced, lit, coerce_to_base)?
1109 }
1110
1111 LiteralRecord(exprs) => {
1112 let arity = exprs.len();
1113 let coercions = match coerce_to {
1114 ScalarType::Record { fields, .. } if fields.len() == arity => fields
1115 .iter()
1116 .map(|(_name, ty)| &ty.scalar_type)
1117 .cloned()
1118 .collect(),
1119 _ => vec![ScalarType::String; exprs.len()],
1120 };
1121 let mut out = vec![];
1122 for (e, coerce_to) in exprs.into_iter().zip(coercions) {
1123 out.push(plan_coerce(ecx, e, &coerce_to)?);
1124 }
1125 HirScalarExpr::call_variadic(
1126 VariadicFunc::RecordCreate {
1127 field_names: (0..arity)
1128 .map(|i| ColumnName::from(format!("f{}", i + 1)))
1129 .collect(),
1130 },
1131 out,
1132 )
1133 }
1134
1135 Parameter(n) => {
1136 let prev = ecx.param_types().borrow_mut().insert(n, coerce_to.clone());
1137 assert_none!(prev);
1138 HirScalarExpr::parameter(n)
1139 }
1140 })
1141}
1142
1143pub fn plan_hypothetical_cast(
1150 ecx: &ExprContext,
1151 ccx: CastContext,
1152 from: &ScalarType,
1153 to: &ScalarType,
1154) -> Option<mz_expr::MirScalarExpr> {
1155 let mut scx = ecx.qcx.scx.clone();
1158 scx.param_types = RefCell::new(BTreeMap::new());
1159 let qcx = QueryContext::root(&scx, ecx.qcx.lifetime);
1160 let relation_type = RelationType {
1161 column_types: vec![ColumnType {
1162 nullable: true,
1163 scalar_type: from.clone(),
1164 }],
1165 keys: vec![vec![0]],
1166 };
1167 let ecx = ExprContext {
1168 qcx: &qcx,
1169 name: "plan_hypothetical_cast",
1170 scope: &Scope::empty(),
1171 relation_type: &relation_type,
1172 allow_aggregates: false,
1173 allow_subqueries: true,
1174 allow_parameters: true,
1175 allow_windows: false,
1176 };
1177
1178 let col_expr = HirScalarExpr::column(0);
1179
1180 plan_cast(&ecx, ccx, col_expr, to)
1183 .ok()?
1184 .lower_uncorrelated()
1186 .ok()
1187}
1188
1189pub fn plan_cast(
1199 ecx: &ExprContext,
1200 ccx: CastContext,
1201 expr: HirScalarExpr,
1202 to: &ScalarType,
1203) -> Result<HirScalarExpr, PlanError> {
1204 let from = ecx.scalar_type(&expr);
1205
1206 let cast_inner = |from, to, expr| match get_cast(ecx, ccx, from, to) {
1209 Some(cast) => Ok(cast(expr)),
1210 None => Err(PlanError::InvalidCast {
1211 name: ecx.name.into(),
1212 ccx,
1213 from: ecx.humanize_scalar_type(from, false),
1214 to: ecx.humanize_scalar_type(to, false),
1215 }),
1216 };
1217
1218 let from_category = TypeCategory::from_type(&from);
1224 let to_category = TypeCategory::from_type(to);
1225 if from_category == TypeCategory::String && to_category != TypeCategory::String {
1226 cast_inner(&ScalarType::String, to, expr)
1229 } else if from_category != TypeCategory::String && to_category == TypeCategory::String {
1230 let expr = cast_inner(&from, &ScalarType::String, expr)?;
1233 cast_inner(&ScalarType::String, to, expr)
1234 } else {
1235 cast_inner(&from, to, expr)
1237 }
1238}
1239
1240pub fn can_cast(
1242 ecx: &ExprContext,
1243 ccx: CastContext,
1244 cast_from: &ScalarType,
1245 cast_to: &ScalarType,
1246) -> bool {
1247 get_cast(ecx, ccx, cast_from, cast_to).is_some()
1248}