reifydb_engine/expression/arith/
add.rs1use reifydb_core::value::column::{ColumnWithName, buffer::ColumnBuffer, push::Push};
5use reifydb_value::{
6 error::{BinaryOp, TypeError},
7 fragment::{Fragment, LazyFragment},
8 reifydb_assertions,
9 value::{
10 container::{number::NumberContainer, temporal::TemporalContainer, utf8::Utf8Container},
11 is::IsNumber,
12 number::{promote::Promote, safe::add::SafeAdd},
13 value_type::{ValueType, get::GetType},
14 },
15};
16
17use crate::{
18 Result,
19 expression::{context::EvalContext, option::binary_op_unwrap_option},
20};
21
22pub(crate) fn add_columns(
23 ctx: &EvalContext,
24 left: &ColumnWithName,
25 right: &ColumnWithName,
26 fragment: impl LazyFragment + Copy,
27) -> Result<ColumnWithName> {
28 binary_op_unwrap_option(left, right, fragment.fragment(), |left, right| {
29 let target = ValueType::promote(left.get_type(), right.get_type());
30
31 dispatch_arith!(
32 &left.data(), &right.data();
33 fixed: add_numeric, arb: add_numeric_clone (ctx, target, fragment);
34
35
36 (ColumnBuffer::Duration(l), ColumnBuffer::Duration(r)) => {
37 let mut container = TemporalContainer::with_capacity(l.len());
38 for i in 0..l.len() {
39 match (l.get(i), r.get(i)) {
40 (Some(lv), Some(rv)) => container.push(*lv + *rv),
41 _ => container.push_default(),
42 }
43 }
44 Ok(ColumnWithName::new(fragment.fragment(), ColumnBuffer::Duration(container)))
45 }
46
47
48 (
49 ColumnBuffer::Utf8 {
50 container: l,
51 ..
52 },
53 ColumnBuffer::Utf8 {
54 container: r,
55 ..
56 },
57 ) => concat_strings(l, r, target, fragment.fragment()),
58
59
60 (
61 ColumnBuffer::Utf8 {
62 container: l,
63 ..
64 },
65 r,
66 ) if can_promote_to_string(r) => concat_string_with_other(l, r, true, target, fragment.fragment()),
67
68
69 (
70 l,
71 ColumnBuffer::Utf8 {
72 container: r,
73 ..
74 },
75 ) if can_promote_to_string(l) => concat_string_with_other(r, l, false, target, fragment.fragment()),
76
77 _ => Err(TypeError::BinaryOperatorNotApplicable {
78 operator: BinaryOp::Add,
79 left: left.get_type(),
80 right: right.get_type(),
81 fragment: fragment.fragment(),
82 }.into()),
83 )
84 })
85}
86
87fn add_numeric<L, R>(
88 ctx: &EvalContext,
89 l: &NumberContainer<L>,
90 r: &NumberContainer<R>,
91 target: ValueType,
92 fragment: impl LazyFragment + Copy,
93) -> Result<ColumnWithName>
94where
95 L: GetType + Promote<R> + IsNumber,
96 R: GetType + IsNumber,
97 <L as Promote<R>>::Output: IsNumber,
98 <L as Promote<R>>::Output: SafeAdd,
99 ColumnBuffer: Push<<L as Promote<R>>::Output>,
100{
101 reifydb_assertions! {
102 assert_eq!(l.len(), r.len());
103 }
104
105 let mut data = ColumnBuffer::with_capacity(target, l.len());
106 let l_data = l.data();
107 let r_data = r.data();
108 for i in 0..l.len() {
109 if let Some(value) = ctx.add(&l_data[i], &r_data[i], fragment)? {
110 data.push(value);
111 } else {
112 data.push_none()
113 }
114 }
115 Ok(ColumnWithName {
116 name: fragment.fragment(),
117 data,
118 })
119}
120
121fn add_numeric_clone<L, R>(
122 ctx: &EvalContext,
123 l: &NumberContainer<L>,
124 r: &NumberContainer<R>,
125 target: ValueType,
126 fragment: impl LazyFragment + Copy,
127) -> Result<ColumnWithName>
128where
129 L: Clone + GetType + Promote<R> + IsNumber,
130 R: Clone + GetType + IsNumber,
131 <L as Promote<R>>::Output: IsNumber,
132 <L as Promote<R>>::Output: SafeAdd,
133 ColumnBuffer: Push<<L as Promote<R>>::Output>,
134{
135 reifydb_assertions! {
136 assert_eq!(l.len(), r.len());
137 }
138
139 let mut data = ColumnBuffer::with_capacity(target, l.len());
140 for i in 0..l.len() {
141 match (l.get(i), r.get(i)) {
142 (Some(l_val), Some(r_val)) => {
143 let l_clone = l_val.clone();
144 let r_clone = r_val.clone();
145 if let Some(value) = ctx.add(&l_clone, &r_clone, fragment)? {
146 data.push(value);
147 } else {
148 data.push_none()
149 }
150 }
151 _ => data.push_none(),
152 }
153 }
154 Ok(ColumnWithName {
155 name: fragment.fragment(),
156 data,
157 })
158}
159
160fn can_promote_to_string(data: &ColumnBuffer) -> bool {
161 matches!(
162 data,
163 ColumnBuffer::Bool(_)
164 | ColumnBuffer::Float4(_)
165 | ColumnBuffer::Float8(_)
166 | ColumnBuffer::Int1(_)
167 | ColumnBuffer::Int2(_)
168 | ColumnBuffer::Int4(_)
169 | ColumnBuffer::Int8(_)
170 | ColumnBuffer::Int16(_)
171 | ColumnBuffer::Uint1(_)
172 | ColumnBuffer::Uint2(_)
173 | ColumnBuffer::Uint4(_)
174 | ColumnBuffer::Uint8(_)
175 | ColumnBuffer::Uint16(_)
176 | ColumnBuffer::Date(_)
177 | ColumnBuffer::DateTime(_)
178 | ColumnBuffer::Time(_)
179 | ColumnBuffer::Duration(_)
180 | ColumnBuffer::Uuid4(_)
181 | ColumnBuffer::Uuid7(_)
182 | ColumnBuffer::Blob { .. }
183 | ColumnBuffer::Int { .. }
184 | ColumnBuffer::Uint { .. }
185 | ColumnBuffer::Decimal { .. }
186 )
187}
188
189fn concat_strings(
190 l: &Utf8Container,
191 r: &Utf8Container,
192 target: ValueType,
193 fragment: Fragment,
194) -> Result<ColumnWithName> {
195 reifydb_assertions! {
196 assert_eq!(l.len(), r.len());
197 }
198
199 let mut data = ColumnBuffer::with_capacity(target, l.len());
200 for i in 0..l.len() {
201 match (l.get(i), r.get(i)) {
202 (Some(l_str), Some(r_str)) => {
203 let concatenated = format!("{}{}", l_str, r_str);
204 data.push(concatenated);
205 }
206 _ => data.push_none(),
207 }
208 }
209 Ok(ColumnWithName {
210 name: fragment,
211 data,
212 })
213}
214
215fn concat_string_with_other(
216 string_data: &Utf8Container,
217 other_data: &ColumnBuffer,
218 string_is_left: bool,
219 target: ValueType,
220 fragment: Fragment,
221) -> Result<ColumnWithName> {
222 reifydb_assertions! {
223 assert_eq!(string_data.len(), other_data.len());
224 }
225
226 let mut data = ColumnBuffer::with_capacity(target, string_data.len());
227 for i in 0..string_data.len() {
228 match (string_data.get(i), other_data.is_defined(i)) {
229 (Some(str_val), true) => {
230 let other_str = other_data.as_string(i);
231 let concatenated = if string_is_left {
232 format!("{}{}", str_val, other_str)
233 } else {
234 format!("{}{}", other_str, str_val)
235 };
236 data.push(concatenated);
237 }
238 _ => data.push_none(),
239 }
240 }
241 Ok(ColumnWithName {
242 name: fragment,
243 data,
244 })
245}