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