mz_expr/scalar/func/impls/
list.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 mz_lowertest::MzReflect;
13use mz_repr::{Datum, Row, RowArena, SqlColumnType, SqlScalarType};
14use serde::{Deserialize, Serialize};
15
16use crate::scalar::func::{LazyUnaryFunc, stringify_datum};
17use crate::{EvalError, MirScalarExpr};
18
19#[derive(Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect)]
20pub struct CastListToString {
21    pub ty: SqlScalarType,
22}
23
24impl LazyUnaryFunc for CastListToString {
25    fn eval<'a>(
26        &'a self,
27        datums: &[Datum<'a>],
28        temp_storage: &'a RowArena,
29        a: &'a MirScalarExpr,
30    ) -> Result<Datum<'a>, EvalError> {
31        let a = a.eval(datums, temp_storage)?;
32        if a.is_null() {
33            return Ok(Datum::Null);
34        }
35        let mut buf = String::new();
36        stringify_datum(&mut buf, a, &self.ty)?;
37        Ok(Datum::String(temp_storage.push_string(buf)))
38    }
39
40    fn output_type(&self, input_type: SqlColumnType) -> SqlColumnType {
41        SqlScalarType::String.nullable(input_type.nullable)
42    }
43
44    fn propagates_nulls(&self) -> bool {
45        true
46    }
47
48    fn introduces_nulls(&self) -> bool {
49        false
50    }
51
52    fn preserves_uniqueness(&self) -> bool {
53        true
54    }
55
56    fn inverse(&self) -> Option<crate::UnaryFunc> {
57        // TODO? if typeconv was in expr, we could determine this
58        None
59    }
60
61    fn is_monotone(&self) -> bool {
62        false
63    }
64}
65
66impl fmt::Display for CastListToString {
67    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68        f.write_str("listtostr")
69    }
70}
71
72#[derive(Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect)]
73pub struct CastListToJsonb {
74    pub cast_element: Box<MirScalarExpr>,
75}
76
77impl LazyUnaryFunc for CastListToJsonb {
78    fn eval<'a>(
79        &'a self,
80        datums: &[Datum<'a>],
81        temp_storage: &'a RowArena,
82        a: &'a MirScalarExpr,
83    ) -> Result<Datum<'a>, EvalError> {
84        let a = a.eval(datums, temp_storage)?;
85        if a.is_null() {
86            return Ok(Datum::Null);
87        }
88        let mut row = Row::default();
89        row.packer().push_list_with(|packer| {
90            for elem in a.unwrap_list().iter() {
91                let elem = match self.cast_element.eval(&[elem], temp_storage)? {
92                    Datum::Null => Datum::JsonNull,
93                    d => d,
94                };
95                packer.push(elem);
96            }
97            Ok::<_, EvalError>(())
98        })?;
99        Ok(temp_storage.push_unary_row(row))
100    }
101
102    fn output_type(&self, input_type: SqlColumnType) -> SqlColumnType {
103        SqlScalarType::Jsonb.nullable(input_type.nullable)
104    }
105
106    fn propagates_nulls(&self) -> bool {
107        true
108    }
109
110    fn introduces_nulls(&self) -> bool {
111        false
112    }
113
114    fn preserves_uniqueness(&self) -> bool {
115        true
116    }
117
118    fn inverse(&self) -> Option<crate::UnaryFunc> {
119        // TODO? If we moved typeconv into `expr` we could determine the right
120        // inverse of this.
121        None
122    }
123
124    fn is_monotone(&self) -> bool {
125        false
126    }
127}
128
129impl fmt::Display for CastListToJsonb {
130    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131        f.write_str("listtojsonb")
132    }
133}
134
135/// Casts between two list types by casting each element of `a` ("list1") using
136/// `cast_expr` and collecting the results into a new list ("list2").
137#[derive(Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect)]
138pub struct CastList1ToList2 {
139    /// List2's type
140    pub return_ty: SqlScalarType,
141    /// The expression to cast List1's elements to List2's elements' type
142    pub cast_expr: Box<MirScalarExpr>,
143}
144
145impl LazyUnaryFunc for CastList1ToList2 {
146    fn eval<'a>(
147        &'a self,
148        datums: &[Datum<'a>],
149        temp_storage: &'a RowArena,
150        a: &'a MirScalarExpr,
151    ) -> Result<Datum<'a>, EvalError> {
152        let a = a.eval(datums, temp_storage)?;
153        if a.is_null() {
154            return Ok(Datum::Null);
155        }
156        let mut cast_datums = Vec::new();
157        for el in a.unwrap_list().iter() {
158            // `cast_expr` is evaluated as an expression that casts the
159            // first column in `datums` (i.e. `datums[0]`) from the list elements'
160            // current type to a target type.
161            cast_datums.push(self.cast_expr.eval(&[el], temp_storage)?);
162        }
163
164        Ok(temp_storage.make_datum(|packer| packer.push_list(cast_datums)))
165    }
166
167    fn output_type(&self, input_type: SqlColumnType) -> SqlColumnType {
168        self.return_ty
169            .without_modifiers()
170            .nullable(input_type.nullable)
171    }
172
173    fn propagates_nulls(&self) -> bool {
174        true
175    }
176
177    fn introduces_nulls(&self) -> bool {
178        false
179    }
180
181    fn preserves_uniqueness(&self) -> bool {
182        false
183    }
184
185    fn inverse(&self) -> Option<crate::UnaryFunc> {
186        // TODO: this could be figured out--might be easier after enum dispatch?
187        None
188    }
189
190    fn is_monotone(&self) -> bool {
191        false
192    }
193}
194
195impl fmt::Display for CastList1ToList2 {
196    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197        f.write_str("list1tolist2")
198    }
199}
200
201#[derive(Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect)]
202pub struct ListLength;
203
204impl LazyUnaryFunc for ListLength {
205    fn eval<'a>(
206        &'a self,
207        datums: &[Datum<'a>],
208        temp_storage: &'a RowArena,
209        a: &'a MirScalarExpr,
210    ) -> Result<Datum<'a>, EvalError> {
211        let a = a.eval(datums, temp_storage)?;
212        if a.is_null() {
213            return Ok(Datum::Null);
214        }
215        let count = a.unwrap_list().iter().count();
216        match count.try_into() {
217            Ok(c) => Ok(Datum::Int32(c)),
218            Err(_) => Err(EvalError::Int32OutOfRange(count.to_string().into())),
219        }
220    }
221
222    fn output_type(&self, input_type: SqlColumnType) -> SqlColumnType {
223        SqlScalarType::Int32.nullable(input_type.nullable)
224    }
225
226    fn propagates_nulls(&self) -> bool {
227        true
228    }
229
230    fn introduces_nulls(&self) -> bool {
231        false
232    }
233
234    fn preserves_uniqueness(&self) -> bool {
235        false
236    }
237
238    fn inverse(&self) -> Option<crate::UnaryFunc> {
239        None
240    }
241
242    fn is_monotone(&self) -> bool {
243        false
244    }
245}
246
247impl fmt::Display for ListLength {
248    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
249        f.write_str("list_length")
250    }
251}