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
21macro_rules! impl_scalar_op {
22 ($method:ident, $safe_trait:ident, $checked_method:ident) => {
23 impl EvalContext<'_> {
24 pub fn $method<'a, L, R>(
25 &self,
26 l: &L,
27 r: &R,
28 fragment: impl LazyFragment + Copy,
29 ) -> Result<Option<<L as Promote<R>>::Output>>
30 where
31 L: Promote<R>,
32 R: IsNumber,
33 <L as Promote<R>>::Output: IsNumber,
34 <L as Promote<R>>::Output: $safe_trait,
35 {
36 match &self.saturation_policy() {
37 ColumnSaturationPolicy::Error => {
38 let Some((lp, rp)) = l.checked_promote(r) else {
39 let descriptor = self
40 .target
41 .as_ref()
42 .and_then(|c| c.to_number_descriptor());
43 return Err(TypeError::NumberOutOfRange {
44 target: <L as Promote<R>>::Output::get_type(),
45 fragment: fragment.fragment(),
46 descriptor,
47 }
48 .into());
49 };
50
51 lp.$checked_method(&rp)
52 .ok_or_else(|| {
53 let descriptor = self
54 .target
55 .as_ref()
56 .and_then(|c| c.to_number_descriptor());
57 TypeError::NumberOutOfRange {
58 target: <L as Promote<R>>::Output::get_type(),
59 fragment: fragment.fragment(),
60 descriptor,
61 }
62 .into()
63 })
64 .map(Some)
65 }
66 ColumnSaturationPolicy::None => {
67 let Some((lp, rp)) = l.checked_promote(r) else {
68 return Ok(None);
69 };
70
71 match lp.$checked_method(&rp) {
72 None => Ok(None),
73 Some(value) => Ok(Some(value)),
74 }
75 }
76 }
77 }
78 }
79 };
80}
81
82impl_scalar_op!(add, SafeAdd, checked_add);
83impl_scalar_op!(sub, SafeSub, checked_sub);
84impl_scalar_op!(mul, SafeMul, checked_mul);
85impl_scalar_op!(div, SafeDiv, checked_div);
86impl_scalar_op!(remainder, SafeRemainder, checked_rem);
87
88#[cfg(test)]
89pub mod tests {
90 use reifydb_type::fragment::Fragment;
91
92 use crate::expression::context::EvalContext;
93
94 #[test]
95 fn test_add() {
96 let test_instance = EvalContext::testing();
97 let result = test_instance.add(&1i8, &255i16, || Fragment::testing_empty());
98 assert_eq!(result, Ok(Some(256i128)));
99 }
100
101 #[test]
102 fn test_sub() {
103 let test_instance = EvalContext::testing();
104 let result = test_instance.sub(&1i8, &255i16, || Fragment::testing_empty());
105 assert_eq!(result, Ok(Some(-254i128)));
106 }
107
108 #[test]
109 fn test_mul() {
110 let test_instance = EvalContext::testing();
111 let result = test_instance.mul(&23i8, &255i16, || Fragment::testing_empty());
112 assert_eq!(result, Ok(Some(5865i128)));
113 }
114
115 #[test]
116 fn test_div() {
117 let test_instance = EvalContext::testing();
118 let result = test_instance.div(&120i8, &20i16, || Fragment::testing_empty());
119 assert_eq!(result, Ok(Some(6i128)));
120 }
121
122 #[test]
123 fn test_remainder() {
124 let test_instance = EvalContext::testing();
125 let result = test_instance.remainder(&120i8, &21i16, || Fragment::testing_empty());
126 assert_eq!(result, Ok(Some(15i128)));
127 }
128}