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