Skip to main content

mz_expr/scalar/func/impls/
record.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10use std::fmt;
11
12use itertools::Itertools;
13use mz_repr::{Datum, RowArena, SqlColumnType, SqlScalarType};
14use serde::{Deserialize, Serialize};
15
16use crate::scalar::func::{LazyUnaryFunc, stringify_datum};
17use crate::{Eval, EvalError, MirScalarExpr};
18
19#[derive(
20    Ord,
21    PartialOrd,
22    Clone,
23    Debug,
24    Eq,
25    PartialEq,
26    Serialize,
27    Deserialize,
28    Hash
29)]
30pub struct CastRecordToString {
31    pub ty: SqlScalarType,
32}
33
34impl LazyUnaryFunc for CastRecordToString {
35    fn eval<'a>(
36        &'a self,
37        datums: &[Datum<'a>],
38        temp_storage: &'a RowArena,
39        a: &'a impl Eval,
40    ) -> Result<Datum<'a>, EvalError> {
41        let a = a.eval(datums, temp_storage)?;
42        if a.is_null() {
43            return Ok(Datum::Null);
44        }
45        let mut buf = String::new();
46        stringify_datum(&mut buf, a, &self.ty)?;
47        Ok(Datum::String(temp_storage.push_string(buf)))
48    }
49
50    fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
51        SqlScalarType::String.nullable(input_type.nullable)
52    }
53
54    fn propagates_nulls(&self) -> bool {
55        true
56    }
57
58    fn introduces_nulls(&self) -> bool {
59        false
60    }
61
62    fn preserves_uniqueness(&self) -> bool {
63        true
64    }
65
66    fn inverse(&self) -> Option<crate::UnaryFunc> {
67        // TODO? if we moved typeconv into expr, we could evaluate this
68        None
69    }
70
71    fn is_monotone(&self) -> bool {
72        false
73    }
74
75    fn is_eliminable_cast(&self) -> bool {
76        false
77    }
78}
79
80impl fmt::Display for CastRecordToString {
81    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
82        f.write_str("recordtostr")
83    }
84}
85
86/// Casts between two record types by casting each element of `a` ("record1") using
87/// `cast_expr` and collecting the results into a new record ("record2").
88#[derive(
89    Ord,
90    PartialOrd,
91    Clone,
92    Debug,
93    Eq,
94    PartialEq,
95    Serialize,
96    Deserialize,
97    Hash
98)]
99pub struct CastRecord1ToRecord2 {
100    pub return_ty: SqlScalarType,
101    pub cast_exprs: Box<[MirScalarExpr]>,
102}
103
104impl LazyUnaryFunc for CastRecord1ToRecord2 {
105    fn eval<'a>(
106        &'a self,
107        datums: &[Datum<'a>],
108        temp_storage: &'a RowArena,
109        a: &'a impl Eval,
110    ) -> Result<Datum<'a>, EvalError> {
111        let a = a.eval(datums, temp_storage)?;
112        if a.is_null() {
113            return Ok(Datum::Null);
114        }
115        let mut cast_datums = Vec::new();
116        for (el, cast_expr) in a.unwrap_list().iter().zip_eq(&self.cast_exprs) {
117            cast_datums.push(cast_expr.eval(&[el], temp_storage)?);
118        }
119        Ok(temp_storage.make_datum(|packer| packer.push_list(cast_datums)))
120    }
121
122    fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
123        self.return_ty
124            .without_modifiers()
125            .nullable(input_type.nullable)
126    }
127
128    fn propagates_nulls(&self) -> bool {
129        true
130    }
131
132    fn introduces_nulls(&self) -> bool {
133        false
134    }
135
136    fn preserves_uniqueness(&self) -> bool {
137        false
138    }
139
140    fn inverse(&self) -> Option<crate::UnaryFunc> {
141        // TODO: we could determine Record1's type from `cast_exprs`
142        None
143    }
144
145    fn is_monotone(&self) -> bool {
146        // In theory this could be marked as monotone if we knew that all the expressions were
147        // monotone in the same direction. (ie. all increasing or all decreasing.) We don't yet
148        // track enough information to make that call, though!
149        false
150    }
151
152    fn is_eliminable_cast(&self) -> bool {
153        false
154    }
155}
156
157impl fmt::Display for CastRecord1ToRecord2 {
158    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159        f.write_str("record1torecord2")
160    }
161}
162
163#[derive(
164    Ord,
165    PartialOrd,
166    Clone,
167    Debug,
168    Eq,
169    PartialEq,
170    Serialize,
171    Deserialize,
172    Hash
173)]
174pub struct RecordGet(pub usize);
175
176impl LazyUnaryFunc for RecordGet {
177    fn eval<'a>(
178        &'a self,
179        datums: &[Datum<'a>],
180        temp_storage: &'a RowArena,
181        a: &'a impl Eval,
182    ) -> Result<Datum<'a>, EvalError> {
183        let a = a.eval(datums, temp_storage)?;
184        if a.is_null() {
185            return Ok(Datum::Null);
186        }
187        Ok(a.unwrap_list().iter().nth(self.0).unwrap())
188    }
189
190    fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
191        match input_type.scalar_type {
192            SqlScalarType::Record { fields, .. } => {
193                let (_name, ty) = &fields[self.0];
194                let mut ty = ty.clone();
195                ty.nullable = ty.nullable || input_type.nullable;
196                ty
197            }
198            _ => unreachable!(
199                "RecordGet on non-record input: {:?}",
200                input_type.scalar_type
201            ),
202        }
203    }
204
205    fn propagates_nulls(&self) -> bool {
206        true
207    }
208
209    fn introduces_nulls(&self) -> bool {
210        // Return null if the inner field is null
211        true
212    }
213
214    fn preserves_uniqueness(&self) -> bool {
215        false
216    }
217
218    fn inverse(&self) -> Option<crate::UnaryFunc> {
219        None
220    }
221
222    fn is_monotone(&self) -> bool {
223        false
224    }
225
226    fn is_eliminable_cast(&self) -> bool {
227        false
228    }
229}
230
231impl fmt::Display for RecordGet {
232    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
233        write!(f, "record_get[{}]", self.0)
234    }
235}