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