mz_expr/scalar/func/impls/
range.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::adt::range::Range;
14use mz_repr::{Datum, RowArena, SqlColumnType, SqlScalarType};
15use serde::{Deserialize, Serialize};
16
17use crate::scalar::func::{LazyUnaryFunc, stringify_datum};
18use crate::{EvalError, MirScalarExpr};
19
20#[derive(Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect)]
21pub struct CastRangeToString {
22    pub ty: SqlScalarType,
23}
24
25impl LazyUnaryFunc for CastRangeToString {
26    fn eval<'a>(
27        &'a self,
28        datums: &[Datum<'a>],
29        temp_storage: &'a RowArena,
30        a: &'a MirScalarExpr,
31    ) -> Result<Datum<'a>, EvalError> {
32        let a = a.eval(datums, temp_storage)?;
33        if a.is_null() {
34            return Ok(Datum::Null);
35        }
36        let mut buf = String::new();
37        stringify_datum(&mut buf, a, &self.ty)?;
38        Ok(Datum::String(temp_storage.push_string(buf)))
39    }
40
41    fn output_type(&self, input_type: SqlColumnType) -> SqlColumnType {
42        SqlScalarType::String.nullable(input_type.nullable)
43    }
44
45    fn propagates_nulls(&self) -> bool {
46        true
47    }
48
49    fn introduces_nulls(&self) -> bool {
50        false
51    }
52
53    fn preserves_uniqueness(&self) -> bool {
54        true
55    }
56
57    fn inverse(&self) -> Option<crate::UnaryFunc> {
58        // TODO? if typeconv was in expr, we could determine this
59        None
60    }
61
62    fn is_monotone(&self) -> bool {
63        false
64    }
65}
66
67impl fmt::Display for CastRangeToString {
68    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69        f.write_str("rangetostr")
70    }
71}
72
73#[derive(Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect)]
74pub struct RangeLower;
75
76impl LazyUnaryFunc for RangeLower {
77    fn eval<'a>(
78        &'a self,
79        datums: &[Datum<'a>],
80        temp_storage: &'a RowArena,
81        a: &'a MirScalarExpr,
82    ) -> Result<Datum<'a>, EvalError> {
83        let a = a.eval(datums, temp_storage)?;
84        if a.is_null() {
85            return Ok(Datum::Null);
86        }
87        let r = a.unwrap_range();
88        Ok(Datum::from(
89            r.inner.map(|inner| inner.lower.bound).flatten(),
90        ))
91    }
92
93    fn output_type(&self, input_type: SqlColumnType) -> SqlColumnType {
94        input_type
95            .scalar_type
96            .unwrap_range_element_type()
97            .clone()
98            .nullable(true)
99    }
100
101    fn propagates_nulls(&self) -> bool {
102        true
103    }
104
105    fn introduces_nulls(&self) -> bool {
106        true
107    }
108
109    fn preserves_uniqueness(&self) -> bool {
110        false
111    }
112
113    fn inverse(&self) -> Option<crate::UnaryFunc> {
114        None
115    }
116
117    fn is_monotone(&self) -> bool {
118        true // Ranges are sorted by lower first.
119    }
120}
121
122impl fmt::Display for RangeLower {
123    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124        f.write_str("rangelower")
125    }
126}
127
128#[derive(Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect)]
129pub struct RangeUpper;
130
131impl LazyUnaryFunc for RangeUpper {
132    fn eval<'a>(
133        &'a self,
134        datums: &[Datum<'a>],
135        temp_storage: &'a RowArena,
136        a: &'a MirScalarExpr,
137    ) -> Result<Datum<'a>, EvalError> {
138        let a = a.eval(datums, temp_storage)?;
139        if a.is_null() {
140            return Ok(Datum::Null);
141        }
142        let r = a.unwrap_range();
143        Ok(Datum::from(
144            r.inner.map(|inner| inner.upper.bound).flatten(),
145        ))
146    }
147
148    fn output_type(&self, input_type: SqlColumnType) -> SqlColumnType {
149        input_type
150            .scalar_type
151            .unwrap_range_element_type()
152            .clone()
153            .nullable(true)
154    }
155
156    fn propagates_nulls(&self) -> bool {
157        true
158    }
159
160    fn introduces_nulls(&self) -> bool {
161        true
162    }
163
164    fn preserves_uniqueness(&self) -> bool {
165        false
166    }
167
168    fn inverse(&self) -> Option<crate::UnaryFunc> {
169        None
170    }
171
172    fn is_monotone(&self) -> bool {
173        false
174    }
175}
176
177impl fmt::Display for RangeUpper {
178    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
179        f.write_str("rangeupper")
180    }
181}
182
183sqlfunc!(
184    #[sqlname = "range_empty"]
185    fn range_empty(a: Range<Datum<'a>>) -> bool {
186        a.inner.is_none()
187    }
188);
189
190sqlfunc!(
191    #[sqlname = "range_lower_inc"]
192    fn range_lower_inc(a: Range<Datum<'a>>) -> bool {
193        match a.inner {
194            None => false,
195            Some(inner) => inner.lower.inclusive,
196        }
197    }
198);
199
200sqlfunc!(
201    #[sqlname = "range_upper_inc"]
202    fn range_upper_inc(a: Range<Datum<'a>>) -> bool {
203        match a.inner {
204            None => false,
205            Some(inner) => inner.upper.inclusive,
206        }
207    }
208);
209
210sqlfunc!(
211    #[sqlname = "range_lower_inf"]
212    fn range_lower_inf(a: Range<Datum<'a>>) -> bool {
213        match a.inner {
214            None => false,
215            Some(inner) => inner.lower.bound.is_none(),
216        }
217    }
218);
219
220sqlfunc!(
221    #[sqlname = "range_upper_inf"]
222    fn range_upper_inf(a: Range<Datum<'a>>) -> bool {
223        match a.inner {
224            None => false,
225            Some(inner) => inner.upper.bound.is_none(),
226        }
227    }
228);