reifydb_engine/expression/
scalar.rs1use reifydb_core::interface::catalog::policy::ColumnSaturationPolicy;
5use reifydb_type::{
6 error::{Error, diagnostic::number::number_out_of_range},
7 fragment::LazyFragment,
8 return_error,
9 value::{
10 is::IsNumber,
11 number::{
12 promote::Promote,
13 safe::{add::SafeAdd, div::SafeDiv, mul::SafeMul, remainder::SafeRemainder, sub::SafeSub},
14 },
15 r#type::get::GetType,
16 },
17};
18
19use crate::expression::context::EvalContext;
20
21impl EvalContext<'_> {
22 pub fn add<'a, L, R>(
23 &self,
24 l: &L,
25 r: &R,
26 fragment: impl LazyFragment + Copy,
27 ) -> reifydb_type::Result<Option<<L as Promote<R>>::Output>>
28 where
29 L: Promote<R>,
30 R: IsNumber,
31 <L as Promote<R>>::Output: IsNumber,
32 <L as Promote<R>>::Output: SafeAdd,
33 {
34 match &self.saturation_policy() {
35 ColumnSaturationPolicy::Error => {
36 let Some((lp, rp)) = l.checked_promote(r) else {
37 let descriptor = self.target.as_ref().and_then(|c| c.to_number_descriptor());
38 return_error!(number_out_of_range(
39 fragment.fragment(),
40 <L as Promote<R>>::Output::get_type(),
41 descriptor.as_ref(),
42 ));
43 };
44
45 lp.checked_add(&rp)
46 .ok_or_else(|| {
47 let descriptor =
48 self.target.as_ref().and_then(|c| c.to_number_descriptor());
49 Error(number_out_of_range(
50 fragment.fragment(),
51 <L as Promote<R>>::Output::get_type(),
52 descriptor.as_ref(),
53 ))
54 })
55 .map(Some)
56 }
57 ColumnSaturationPolicy::None => {
58 let Some((lp, rp)) = l.checked_promote(r) else {
59 return Ok(None);
60 };
61
62 match lp.checked_add(&rp) {
63 None => Ok(None),
64 Some(value) => Ok(Some(value)),
65 }
66 }
67 }
68 }
69}
70
71impl EvalContext<'_> {
72 pub fn sub<'a, L, R>(
73 &self,
74 l: &L,
75 r: &R,
76 fragment: impl LazyFragment + Copy,
77 ) -> reifydb_type::Result<Option<<L as Promote<R>>::Output>>
78 where
79 L: Promote<R>,
80 R: IsNumber,
81 <L as Promote<R>>::Output: IsNumber,
82 <L as Promote<R>>::Output: SafeSub,
83 {
84 match &self.saturation_policy() {
85 ColumnSaturationPolicy::Error => {
86 let Some((lp, rp)) = l.checked_promote(r) else {
87 let descriptor = self.target.as_ref().and_then(|c| c.to_number_descriptor());
88 return_error!(number_out_of_range(
89 fragment.fragment(),
90 <L as Promote<R>>::Output::get_type(),
91 descriptor.as_ref(),
92 ));
93 };
94
95 lp.checked_sub(&rp)
96 .ok_or_else(|| {
97 let descriptor =
98 self.target.as_ref().and_then(|c| c.to_number_descriptor());
99 Error(number_out_of_range(
100 fragment.fragment(),
101 <L as Promote<R>>::Output::get_type(),
102 descriptor.as_ref(),
103 ))
104 })
105 .map(Some)
106 }
107 ColumnSaturationPolicy::None => {
108 let Some((lp, rp)) = l.checked_promote(r) else {
109 return Ok(None);
110 };
111
112 match lp.checked_sub(&rp) {
113 None => Ok(None),
114 Some(value) => Ok(Some(value)),
115 }
116 }
117 }
118 }
119}
120
121impl EvalContext<'_> {
122 pub fn mul<'a, L, R>(
123 &self,
124 l: &L,
125 r: &R,
126 fragment: impl LazyFragment + Copy,
127 ) -> reifydb_type::Result<Option<<L as Promote<R>>::Output>>
128 where
129 L: Promote<R>,
130 R: IsNumber,
131 <L as Promote<R>>::Output: IsNumber,
132 <L as Promote<R>>::Output: SafeMul,
133 {
134 match &self.saturation_policy() {
135 ColumnSaturationPolicy::Error => {
136 let Some((lp, rp)) = l.checked_promote(r) else {
137 let descriptor = self.target.as_ref().and_then(|c| c.to_number_descriptor());
138 return_error!(number_out_of_range(
139 fragment.fragment(),
140 <L as Promote<R>>::Output::get_type(),
141 descriptor.as_ref(),
142 ));
143 };
144
145 lp.checked_mul(&rp)
146 .ok_or_else(|| {
147 let descriptor =
148 self.target.as_ref().and_then(|c| c.to_number_descriptor());
149 Error(number_out_of_range(
150 fragment.fragment(),
151 <L as Promote<R>>::Output::get_type(),
152 descriptor.as_ref(),
153 ))
154 })
155 .map(Some)
156 }
157 ColumnSaturationPolicy::None => {
158 let Some((lp, rp)) = l.checked_promote(r) else {
159 return Ok(None);
160 };
161
162 match lp.checked_mul(&rp) {
163 None => Ok(None),
164 Some(value) => Ok(Some(value)),
165 }
166 }
167 }
168 }
169}
170
171impl EvalContext<'_> {
172 pub fn div<'a, L, R>(
173 &self,
174 l: &L,
175 r: &R,
176 fragment: impl LazyFragment + Copy,
177 ) -> reifydb_type::Result<Option<<L as Promote<R>>::Output>>
178 where
179 L: Promote<R>,
180 R: IsNumber,
181 <L as Promote<R>>::Output: IsNumber,
182 <L as Promote<R>>::Output: SafeDiv,
183 {
184 match &self.saturation_policy() {
185 ColumnSaturationPolicy::Error => {
186 let Some((lp, rp)) = l.checked_promote(r) else {
187 let descriptor = self.target.as_ref().and_then(|c| c.to_number_descriptor());
188 return_error!(number_out_of_range(
189 fragment.fragment(),
190 <L as Promote<R>>::Output::get_type(),
191 descriptor.as_ref(),
192 ));
193 };
194
195 lp.checked_div(&rp)
196 .ok_or_else(|| {
197 let descriptor =
198 self.target.as_ref().and_then(|c| c.to_number_descriptor());
199 Error(number_out_of_range(
200 fragment.fragment(),
201 <L as Promote<R>>::Output::get_type(),
202 descriptor.as_ref(),
203 ))
204 })
205 .map(Some)
206 }
207 ColumnSaturationPolicy::None => {
208 let Some((lp, rp)) = l.checked_promote(r) else {
209 return Ok(None);
210 };
211
212 match lp.checked_div(&rp) {
213 None => Ok(None),
214 Some(value) => Ok(Some(value)),
215 }
216 }
217 }
218 }
219}
220
221impl EvalContext<'_> {
222 pub fn remainder<'a, L, R>(
223 &self,
224 l: &L,
225 r: &R,
226 fragment: impl LazyFragment + Copy,
227 ) -> reifydb_type::Result<Option<<L as Promote<R>>::Output>>
228 where
229 L: Promote<R>,
230 R: IsNumber,
231 <L as Promote<R>>::Output: IsNumber,
232 <L as Promote<R>>::Output: SafeRemainder,
233 {
234 match &self.saturation_policy() {
235 ColumnSaturationPolicy::Error => {
236 let Some((lp, rp)) = l.checked_promote(r) else {
237 let descriptor = self.target.as_ref().and_then(|c| c.to_number_descriptor());
238 return_error!(number_out_of_range(
239 fragment.fragment(),
240 <L as Promote<R>>::Output::get_type(),
241 descriptor.as_ref(),
242 ));
243 };
244
245 lp.checked_rem(&rp)
246 .ok_or_else(|| {
247 let descriptor =
248 self.target.as_ref().and_then(|c| c.to_number_descriptor());
249 Error(number_out_of_range(
250 fragment.fragment(),
251 <L as Promote<R>>::Output::get_type(),
252 descriptor.as_ref(),
253 ))
254 })
255 .map(Some)
256 }
257 ColumnSaturationPolicy::None => {
258 let Some((lp, rp)) = l.checked_promote(r) else {
259 return Ok(None);
260 };
261
262 match lp.checked_rem(&rp) {
263 None => Ok(None),
264 Some(value) => Ok(Some(value)),
265 }
266 }
267 }
268 }
269}
270
271#[cfg(test)]
272pub mod tests {
273 use reifydb_type::fragment::Fragment;
274
275 use crate::expression::context::EvalContext;
276
277 #[test]
278 fn test_add() {
279 let test_instance = EvalContext::testing();
280 let result = test_instance.add(&1i8, &255i16, || Fragment::testing_empty());
281 assert_eq!(result, Ok(Some(256i128)));
282 }
283
284 #[test]
285 fn test_sub() {
286 let test_instance = EvalContext::testing();
287 let result = test_instance.sub(&1i8, &255i16, || Fragment::testing_empty());
288 assert_eq!(result, Ok(Some(-254i128)));
289 }
290
291 #[test]
292 fn test_mul() {
293 let test_instance = EvalContext::testing();
294 let result = test_instance.mul(&23i8, &255i16, || Fragment::testing_empty());
295 assert_eq!(result, Ok(Some(5865i128)));
296 }
297
298 #[test]
299 fn test_div() {
300 let test_instance = EvalContext::testing();
301 let result = test_instance.div(&120i8, &20i16, || Fragment::testing_empty());
302 assert_eq!(result, Ok(Some(6i128)));
303 }
304
305 #[test]
306 fn test_remainder() {
307 let test_instance = EvalContext::testing();
308 let result = test_instance.remainder(&120i8, &21i16, || Fragment::testing_empty());
309 assert_eq!(result, Ok(Some(15i128)));
310 }
311}