mz_expr/scalar/func/impls/
record.rs1use std::fmt;
11
12use itertools::Itertools;
13use mz_expr_derive::sqlfunc;
14use mz_repr::{Datum, DatumList, ExcludeNull, RowArena, SqlColumnType, SqlScalarType};
15use serde::{Deserialize, Serialize};
16
17use crate::scalar::func::{LazyUnaryFunc, stringify_datum};
18use crate::{Eval, EvalError, MirScalarExpr};
19
20#[derive(
21 Ord,
22 PartialOrd,
23 Clone,
24 Debug,
25 Eq,
26 PartialEq,
27 Serialize,
28 Deserialize,
29 Hash
30)]
31pub struct CastRecordToString {
32 pub ty: SqlScalarType,
33}
34
35#[sqlfunc(
37 CastRecordToString,
38 sqlname = "recordtostr",
39 preserves_uniqueness = true
40)]
41fn cast_record_to_string<'a>(&self, a: ExcludeNull<Datum<'a>>) -> Result<String, EvalError> {
42 let mut buf = String::new();
43 stringify_datum(&mut buf, *a, &self.ty)?;
44 Ok(buf)
45}
46
47#[derive(
50 Ord,
51 PartialOrd,
52 Clone,
53 Debug,
54 Eq,
55 PartialEq,
56 Serialize,
57 Deserialize,
58 Hash
59)]
60pub struct CastRecord1ToRecord2<E = MirScalarExpr> {
61 pub return_ty: SqlScalarType,
62 pub cast_exprs: Box<[E]>,
63}
64
65impl<E: Eval> LazyUnaryFunc for CastRecord1ToRecord2<E> {
66 fn eval<'a>(
67 &'a self,
68 datums: &[Datum<'a>],
69 temp_storage: &'a RowArena,
70 a: &'a impl Eval,
71 ) -> Result<Datum<'a>, EvalError> {
72 let a = a.eval(datums, temp_storage)?;
73 if a.is_null() {
74 return Ok(Datum::Null);
75 }
76 let mut cast_datums = Vec::new();
77 for (el, cast_expr) in a.unwrap_list().iter().zip_eq(&self.cast_exprs) {
78 cast_datums.push(cast_expr.eval(&[el], temp_storage)?);
79 }
80 Ok(temp_storage.make_datum(|packer| packer.push_list(cast_datums)))
81 }
82
83 fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
84 self.return_ty
85 .without_modifiers()
86 .nullable(input_type.nullable)
87 }
88
89 fn propagates_nulls(&self) -> bool {
90 true
91 }
92
93 fn introduces_nulls(&self) -> bool {
94 false
95 }
96
97 fn preserves_uniqueness(&self) -> bool {
98 false
99 }
100
101 fn inverse(&self) -> Option<crate::UnaryFunc> {
102 None
104 }
105
106 fn is_monotone(&self) -> bool {
107 false
111 }
112
113 fn is_eliminable_cast(&self) -> bool {
114 false
115 }
116}
117
118impl<E> CastRecord1ToRecord2<E> {
119 pub fn try_map_expr<'a, E2: TryFrom<&'a E>>(
123 &'a self,
124 ) -> Result<CastRecord1ToRecord2<E2>, E2::Error> {
125 Ok(CastRecord1ToRecord2 {
126 return_ty: self.return_ty.clone(),
127 cast_exprs: self
128 .cast_exprs
129 .iter()
130 .map(E2::try_from)
131 .collect::<Result<Box<[E2]>, _>>()?,
132 })
133 }
134
135 pub fn map_expr<'a, E2: From<&'a E>>(&'a self) -> CastRecord1ToRecord2<E2> {
138 CastRecord1ToRecord2 {
139 return_ty: self.return_ty.clone(),
140 cast_exprs: self.cast_exprs.iter().map(E2::from).collect(),
141 }
142 }
143}
144
145impl<E> fmt::Display for CastRecord1ToRecord2<E> {
146 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
147 f.write_str("record1torecord2")
148 }
149}
150
151#[derive(
152 Ord,
153 PartialOrd,
154 Clone,
155 Debug,
156 Eq,
157 PartialEq,
158 Serialize,
159 Deserialize,
160 Hash
161)]
162pub struct RecordGet(pub usize);
163
164#[sqlfunc(
169 RecordGet,
170 skip_display = true,
171 output_type_expr = match &input_type.scalar_type {
172 SqlScalarType::Record { fields, .. } => fields[self.0].1.clone(),
173 other => unreachable!("RecordGet on non-record input: {other:?}"),
174 }
175)]
176fn record_get<'a>(&self, a: DatumList<'a>) -> Datum<'a> {
177 a.iter().nth(self.0).unwrap()
178}
179
180impl fmt::Display for RecordGet {
181 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
182 write!(f, "record_get[{}]", self.0)
183 }
184}