Skip to main content

mz_expr/scalar/func/impls/
map.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_expr_derive::sqlfunc;
14use mz_repr::{Datum, DatumMap, RowArena, SqlColumnType, SqlScalarType};
15use serde::{Deserialize, Serialize};
16
17use crate::scalar::func::{LazyUnaryFunc, stringify_datum};
18use crate::{Eval, EvalError};
19
20#[derive(
21    Ord,
22    PartialOrd,
23    Clone,
24    Debug,
25    Eq,
26    PartialEq,
27    Serialize,
28    Deserialize,
29    Hash
30)]
31pub struct CastMapToString {
32    pub ty: SqlScalarType,
33}
34
35impl LazyUnaryFunc for CastMapToString {
36    fn eval<'a>(
37        &'a self,
38        datums: &[Datum<'a>],
39        temp_storage: &'a RowArena,
40        a: &'a impl Eval,
41    ) -> Result<Datum<'a>, EvalError> {
42        let a = a.eval(datums, temp_storage)?;
43        if a.is_null() {
44            return Ok(Datum::Null);
45        }
46        let mut buf = String::new();
47        stringify_datum(&mut buf, a, &self.ty)?;
48        Ok(Datum::String(temp_storage.push_string(buf)))
49    }
50
51    fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
52        SqlScalarType::String.nullable(input_type.nullable)
53    }
54
55    fn propagates_nulls(&self) -> bool {
56        true
57    }
58
59    fn introduces_nulls(&self) -> bool {
60        false
61    }
62
63    fn preserves_uniqueness(&self) -> bool {
64        true
65    }
66
67    fn inverse(&self) -> Option<crate::UnaryFunc> {
68        // TODO? If we moved typeconv into expr, we could evaluate this
69        None
70    }
71
72    fn is_monotone(&self) -> bool {
73        false
74    }
75
76    fn is_eliminable_cast(&self) -> bool {
77        false
78    }
79}
80
81impl fmt::Display for CastMapToString {
82    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83        f.write_str("maptostr")
84    }
85}
86
87#[sqlfunc(sqlname = "map_length")]
88fn map_length<'a>(a: DatumMap<'a>) -> Result<i32, EvalError> {
89    let count = a.iter().count();
90    count
91        .try_into()
92        .map_err(|_| EvalError::Int32OutOfRange(count.to_string().into()))
93}
94
95#[derive(
96    Ord,
97    PartialOrd,
98    Clone,
99    Debug,
100    Eq,
101    PartialEq,
102    Serialize,
103    Deserialize,
104    Hash
105)]
106pub struct MapBuildFromRecordList {
107    pub value_type: SqlScalarType,
108}
109
110impl LazyUnaryFunc for MapBuildFromRecordList {
111    fn eval<'a>(
112        &'a self,
113        datums: &[Datum<'a>],
114        temp_storage: &'a RowArena,
115        a: &'a impl Eval,
116    ) -> Result<Datum<'a>, EvalError> {
117        let a = a.eval(datums, temp_storage)?;
118        if a.is_null() {
119            return Ok(Datum::Null);
120        }
121        let list = a.unwrap_list();
122        let mut map = std::collections::BTreeMap::new();
123
124        for i in list.iter() {
125            if i.is_null() {
126                continue;
127            }
128
129            for (k, v) in i.unwrap_list().iter().tuples() {
130                if k.is_null() {
131                    continue;
132                }
133                map.insert(k.unwrap_str(), v);
134            }
135        }
136
137        let map = temp_storage.make_datum(|packer| packer.push_dict(map));
138        Ok(map)
139    }
140
141    fn output_sql_type(&self, _input_type: SqlColumnType) -> SqlColumnType {
142        SqlScalarType::Map {
143            value_type: Box::new(self.value_type.clone()),
144            custom_id: None,
145        }
146        .nullable(true)
147    }
148
149    fn propagates_nulls(&self) -> bool {
150        true
151    }
152
153    fn introduces_nulls(&self) -> bool {
154        true
155    }
156
157    fn preserves_uniqueness(&self) -> bool {
158        false
159    }
160
161    fn inverse(&self) -> Option<crate::UnaryFunc> {
162        None
163    }
164
165    fn is_monotone(&self) -> bool {
166        false
167    }
168
169    fn is_eliminable_cast(&self) -> bool {
170        false
171    }
172}
173
174impl fmt::Display for MapBuildFromRecordList {
175    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
176        f.write_str("map_build")
177    }
178}