mz_expr/scalar/func/impls/
range.rs1use 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 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 }
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}