1use std::any::Any;
5
6use vortex_buffer::BufferAllocatorRef;
7use vortex_buffer::BufferMut;
8use vortex_error::VortexExpect;
9use vortex_error::VortexResult;
10use vortex_error::vortex_ensure;
11use vortex_error::vortex_err;
12use vortex_error::vortex_panic;
13
14use crate::ArrayRef;
15use crate::ExecutionCtx;
16use crate::IntoArray;
17use crate::arrays::DecimalArray;
18use crate::builders::ArrayBuilder;
19use crate::builders::DEFAULT_BUILDER_CAPACITY;
20use crate::builders::LazyBitBufferBuilder;
21use crate::canonical::Canonical;
22use crate::dtype::BigCast;
23use crate::dtype::DType;
24use crate::dtype::DecimalDType;
25use crate::dtype::NativeDecimalType;
26use crate::dtype::Nullability;
27use crate::dtype::i256;
28use crate::match_each_decimal_value;
29use crate::match_each_decimal_value_type;
30use crate::scalar::DecimalValue;
31use crate::scalar::Scalar;
32
33pub struct DecimalBuilder {
39 dtype: DType,
40 values: DecimalBuffer,
41 nulls: LazyBitBufferBuilder,
42}
43
44enum DecimalBuffer {
50 I8(BufferMut<i8>),
51 I16(BufferMut<i16>),
52 I32(BufferMut<i32>),
53 I64(BufferMut<i64>),
54 I128(BufferMut<i128>),
55 I256(BufferMut<i256>),
56}
57
58macro_rules! delegate_fn {
59 ($self:expr, | $tname:ident, $buffer:ident | $body:block) => {{
60 #[allow(unused)]
61 match $self {
62 DecimalBuffer::I8(buffer) => {
63 type $tname = i8;
64 let $buffer = buffer;
65 $body
66 }
67 DecimalBuffer::I16(buffer) => {
68 type $tname = i16;
69 let $buffer = buffer;
70 $body
71 }
72 DecimalBuffer::I32(buffer) => {
73 type $tname = i32;
74 let $buffer = buffer;
75 $body
76 }
77 DecimalBuffer::I64(buffer) => {
78 type $tname = i64;
79 let $buffer = buffer;
80 $body
81 }
82 DecimalBuffer::I128(buffer) => {
83 type $tname = i128;
84 let $buffer = buffer;
85 $body
86 }
87 DecimalBuffer::I256(buffer) => {
88 type $tname = i256;
89 let $buffer = buffer;
90 $body
91 }
92 }
93 }};
94}
95
96impl DecimalBuilder {
97 #[deprecated(note = "use `new_in` with an explicit allocator")]
99 pub fn new<T: NativeDecimalType>(decimal: DecimalDType, nullability: Nullability) -> Self {
100 Self::new_in::<T>(decimal, nullability, BufferAllocatorRef::static_ref())
101 }
102
103 pub fn new_in<T: NativeDecimalType>(
105 decimal: DecimalDType,
106 nullability: Nullability,
107 allocator: &BufferAllocatorRef,
108 ) -> Self {
109 Self::with_capacity_in::<T>(DEFAULT_BUILDER_CAPACITY, decimal, nullability, allocator)
110 }
111
112 #[deprecated(note = "use `with_capacity_in` with an explicit allocator")]
114 pub fn with_capacity<T: NativeDecimalType>(
115 capacity: usize,
116 decimal: DecimalDType,
117 nullability: Nullability,
118 ) -> Self {
119 Self::with_capacity_in::<T>(
120 capacity,
121 decimal,
122 nullability,
123 BufferAllocatorRef::static_ref(),
124 )
125 }
126
127 pub fn with_capacity_in<T: NativeDecimalType>(
129 capacity: usize,
130 decimal: DecimalDType,
131 nullability: Nullability,
132 allocator: &BufferAllocatorRef,
133 ) -> Self {
134 Self {
135 dtype: DType::Decimal(decimal, nullability),
136 values: match_each_decimal_value_type!(T::DECIMAL_TYPE, |D| {
137 DecimalBuffer::from(BufferMut::<D>::with_capacity_in(
138 capacity,
139 allocator.clone(),
140 ))
141 }),
142 nulls: LazyBitBufferBuilder::new(capacity, allocator.clone()),
143 }
144 }
145
146 pub fn append_value<V: NativeDecimalType>(&mut self, value: V) {
148 self.values.push(value);
149 self.nulls.append_non_null();
150 }
151
152 pub fn append_n_values<V: NativeDecimalType>(&mut self, value: V, n: usize) {
154 self.values.push_n(value, n);
155 self.nulls.append_n_non_nulls(n);
156 }
157
158 pub(crate) fn append_decimal_array(
161 &mut self,
162 array: &DecimalArray,
163 ctx: &mut ExecutionCtx,
164 ) -> VortexResult<()> {
165 match_each_decimal_value_type!(array.values_type(), |D| {
166 self.values.extend(array.buffer::<D>().iter().copied());
169 });
170
171 self.nulls.append_validity_mask(
172 &array
173 .as_ref()
174 .validity()?
175 .execute_mask(array.as_ref().len(), ctx)?,
176 );
177 Ok(())
178 }
179
180 pub fn finish_into_decimal(&mut self) -> DecimalArray {
182 let validity = self.nulls.finish_with_nullability(self.dtype.nullability());
183
184 let decimal_dtype = *self.decimal_dtype();
185
186 delegate_fn!(self.values.take(), |T, values| {
187 DecimalArray::new::<T>(values.freeze(), decimal_dtype, validity)
188 })
189 }
190
191 pub fn decimal_dtype(&self) -> &DecimalDType {
193 let DType::Decimal(decimal_dtype, _) = &self.dtype else {
194 vortex_panic!("`DecimalBuilder` somehow had dtype {}", self.dtype);
195 };
196
197 decimal_dtype
198 }
199}
200
201impl ArrayBuilder for DecimalBuilder {
202 fn as_any(&self) -> &dyn Any {
203 self
204 }
205
206 fn as_any_mut(&mut self) -> &mut dyn Any {
207 self
208 }
209
210 fn dtype(&self) -> &DType {
211 &self.dtype
212 }
213
214 fn len(&self) -> usize {
215 self.values.len()
216 }
217
218 fn append_zeros(&mut self, n: usize) {
219 self.values.push_n(0, n);
220 self.nulls.append_n_non_nulls(n);
221 }
222
223 unsafe fn append_nulls_unchecked(&mut self, n: usize) {
224 self.values.push_n(0, n);
225 self.nulls.append_n_nulls(n);
226 }
227
228 fn append_scalar(&mut self, scalar: &Scalar) -> VortexResult<()> {
229 vortex_ensure!(
230 scalar.dtype() == self.dtype(),
231 "DecimalBuilder expected scalar with dtype {}, got {}",
232 self.dtype(),
233 scalar.dtype()
234 );
235
236 match scalar.as_decimal().decimal_value() {
237 None => self.append_null(),
238 Some(v) => match_each_decimal_value!(v, |dec_val| {
239 self.append_value(dec_val);
240 }),
241 }
242
243 Ok(())
244 }
245
246 fn reserve_exact(&mut self, additional: usize) {
247 self.values.reserve(additional);
248 self.nulls.reserve_exact(additional);
249 }
250
251 fn finish(&mut self) -> ArrayRef {
252 self.finish_into_decimal().into_array()
253 }
254
255 fn finish_into_canonical(&mut self, _ctx: &mut ExecutionCtx) -> Canonical {
256 Canonical::Decimal(self.finish_into_decimal())
257 }
258}
259
260impl DecimalBuffer {
261 fn take(&mut self) -> Self {
262 delegate_fn!(self, |T, buffer| {
263 let allocator = buffer.allocator();
264 DecimalBuffer::from(std::mem::replace(buffer, allocator.with_capacity(0)))
265 })
266 }
267
268 fn push<V: NativeDecimalType>(&mut self, value: V) {
269 delegate_fn!(self, |T, buffer| {
270 buffer.push(
271 <T as BigCast>::from(value)
272 .ok_or_else(|| {
273 vortex_err!(
274 "decimal conversion failure {:?}, type: {:?} to {:?}",
275 value,
276 V::DECIMAL_TYPE,
277 T::DECIMAL_TYPE,
278 )
279 })
280 .vortex_expect("operation should succeed in builder"),
281 )
282 });
283 }
284
285 fn push_n<V: NativeDecimalType>(&mut self, value: V, n: usize) {
286 delegate_fn!(self, |T, buffer| {
287 buffer.push_n(
288 <T as BigCast>::from(value).vortex_expect("decimal conversion failure"),
289 n,
290 )
291 });
292 }
293
294 fn reserve(&mut self, additional: usize) {
295 delegate_fn!(self, |T, buffer| { buffer.reserve(additional) })
296 }
297
298 fn len(&self) -> usize {
299 delegate_fn!(self, |T, buffer| { buffer.len() })
300 }
301
302 pub fn extend<I, V: NativeDecimalType>(&mut self, iter: I)
303 where
304 I: Iterator<Item = V>,
305 {
306 delegate_fn!(self, |T, buffer| {
307 buffer.extend(
308 iter.map(|x| <T as BigCast>::from(x).vortex_expect("decimal conversion failure")),
309 )
310 })
311 }
312}
313
314macro_rules! impl_from_buffer {
315 ($T:ty, $variant:ident) => {
316 impl From<BufferMut<$T>> for DecimalBuffer {
317 fn from(buffer: BufferMut<$T>) -> Self {
318 Self::$variant(buffer)
319 }
320 }
321 };
322}
323
324impl_from_buffer!(i8, I8);
325impl_from_buffer!(i16, I16);
326impl_from_buffer!(i32, I32);
327impl_from_buffer!(i64, I64);
328impl_from_buffer!(i128, I128);
329impl_from_buffer!(i256, I256);
330
331#[cfg(test)]
332mod tests {
333 use vortex_buffer::BufferAllocatorRef;
334
335 use crate::VortexSessionExecute;
336 use crate::array_session;
337 use crate::assert_arrays_eq;
338 use crate::builders::ArrayBuilder;
339 use crate::builders::DecimalBuilder;
340 use crate::builders::decimal::DecimalArray;
341 use crate::dtype::DecimalDType;
342
343 #[test]
344 fn test_mixed_extend() {
345 let values = 42i8;
346
347 let mut i8s = DecimalBuilder::new_in::<i8>(
348 DecimalDType::new(2, 1),
349 false.into(),
350 BufferAllocatorRef::static_ref(),
351 );
352 for v in 0..values {
353 i8s.append_value(v);
354 }
355 let i8s = i8s.finish();
356
357 let mut i128s = DecimalBuilder::new_in::<i128>(
358 DecimalDType::new(2, 1),
359 false.into(),
360 BufferAllocatorRef::static_ref(),
361 );
362 i8s.append_to_builder(&mut i128s, &mut array_session().create_execution_ctx())
363 .unwrap();
364 let i128s = i128s.finish();
365
366 for i in 0..i8s.len() {
367 assert_eq!(
368 i8s.execute_scalar(i, &mut array_session().create_execution_ctx())
369 .unwrap(),
370 i128s
371 .execute_scalar(i, &mut array_session().create_execution_ctx())
372 .unwrap()
373 );
374 }
375 }
376
377 #[test]
378 fn test_append_scalar() {
379 let mut ctx = array_session().create_execution_ctx();
380 use crate::scalar::Scalar;
381
382 let mut builder = DecimalBuilder::new_in::<i64>(
384 DecimalDType::new(10, 2),
385 true.into(),
386 BufferAllocatorRef::static_ref(),
387 );
388 builder.append_value(1234i64);
389 builder.append_value(5678i64);
390 builder.append_null();
391
392 let array = builder.finish();
393 let expected = DecimalArray::from_option_iter(
394 [Some(1234i64), Some(5678), None],
395 DecimalDType::new(10, 2),
396 );
397 assert_arrays_eq!(&array, &expected, &mut ctx);
398
399 let mut builder2 = DecimalBuilder::new_in::<i64>(
401 DecimalDType::new(10, 2),
402 true.into(),
403 BufferAllocatorRef::static_ref(),
404 );
405 for i in 0..array.len() {
406 let scalar = array
407 .execute_scalar(i, &mut array_session().create_execution_ctx())
408 .unwrap();
409 builder2.append_scalar(&scalar).unwrap();
410 }
411
412 let array2 = builder2.finish();
413 assert_arrays_eq!(&array2, &array, &mut ctx);
414
415 let mut builder = DecimalBuilder::new_in::<i64>(
417 DecimalDType::new(10, 2),
418 false.into(),
419 BufferAllocatorRef::static_ref(),
420 );
421 let wrong_scalar = Scalar::from(true);
422 assert!(builder.append_scalar(&wrong_scalar).is_err());
423 }
424}