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