1use std::fmt::Display;
7use std::fmt::Formatter;
8use std::hash::Hasher;
9
10use vortex_array::Array;
11use vortex_array::ArrayEq;
12use vortex_array::ArrayHash;
13use vortex_array::ArrayId;
14use vortex_array::ArrayParts;
15use vortex_array::ArrayRef;
16use vortex_array::ArrayView;
17use vortex_array::EqMode;
18use vortex_array::ExecutionCtx;
19use vortex_array::ExecutionResult;
20use vortex_array::TypedArrayRef;
21use vortex_array::array_slots;
22use vortex_array::arrays::Primitive;
23use vortex_array::buffer::BufferHandle;
24use vortex_array::dtype::DType;
25use vortex_array::dtype::DecimalDType;
26use vortex_array::require_child;
27use vortex_array::scalar::DecimalValue;
28use vortex_array::scalar::Scalar;
29use vortex_array::scalar::ScalarValue;
30use vortex_array::serde::ArrayChildren;
31use vortex_array::vtable::OperationsVTable;
32use vortex_array::vtable::VTable;
33use vortex_array::vtable::ValidityChild;
34use vortex_array::vtable::ValidityVTableFromChild;
35use vortex_error::VortexExpect;
36use vortex_error::VortexResult;
37use vortex_error::vortex_bail;
38use vortex_error::vortex_ensure;
39use vortex_error::vortex_ensure_eq;
40use vortex_error::vortex_panic;
41use vortex_session::VortexSession;
42
43use super::MAX_LOWER_PARTS;
44use super::assemble::assemble_decimal;
45use super::assemble::assemble_wide_decimal_value;
46use super::decimal_byte_parts_v2_id;
47use super::rules::PARENT_RULES;
48
49pub type DecimalBytePartsArray = Array<DecimalByteParts>;
51
52#[derive(Clone, Debug)]
64pub struct DecimalBytePartsData;
65
66impl Display for DecimalBytePartsData {
67 fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
68 Ok(())
69 }
70}
71
72impl ArrayHash for DecimalBytePartsData {
73 fn array_hash<H: Hasher>(&self, _state: &mut H, _accuracy: EqMode) {}
74}
75
76impl ArrayEq for DecimalBytePartsData {
77 fn array_eq(&self, _other: &Self, _accuracy: EqMode) -> bool {
78 true
79 }
80}
81
82impl DecimalBytePartsData {
83 pub fn validate<'a>(
91 msp: &ArrayRef,
92 lower_parts: impl ExactSizeIterator<Item = &'a ArrayRef>,
93 decimal_dtype: DecimalDType,
94 dtype: &DType,
95 len: usize,
96 ) -> VortexResult<()> {
97 if !msp.dtype().is_signed_int() {
98 vortex_bail!("msp must be a signed integer array")
99 }
100
101 let expected_dtype = DType::Decimal(decimal_dtype, msp.dtype().nullability());
102 vortex_ensure_eq!(dtype, &expected_dtype);
103 vortex_ensure_eq!(msp.len(), len);
104
105 let lower_part_count = lower_parts.len();
106
107 vortex_ensure!(
108 lower_part_count <= MAX_LOWER_PARTS,
109 "at most {MAX_LOWER_PARTS} lower parts are supported, got {lower_part_count}"
110 );
111 for (idx, part) in lower_parts.enumerate() {
112 vortex_ensure!(
113 part.dtype().is_unsigned_int() && !part.dtype().is_nullable(),
114 "lower part {idx} must have a non-nullable unsigned integer dtype, got {}",
115 part.dtype()
116 );
117 vortex_ensure_eq!(part.len(), len, "lower part {idx} length mismatch");
118 }
119 Ok(())
120 }
121}
122
123#[derive(Clone, Debug)]
124pub struct DecimalByteParts;
125
126impl DecimalByteParts {
127 pub fn try_new(
133 msp: ArrayRef,
134 decimal_dtype: DecimalDType,
135 ) -> VortexResult<DecimalBytePartsArray> {
136 Self::try_new_with_lower_parts(msp, Vec::new(), decimal_dtype)
137 }
138
139 pub fn try_new_with_lower_parts(
151 msp: ArrayRef,
152 lower_parts: Vec<ArrayRef>,
153 decimal_dtype: DecimalDType,
154 ) -> VortexResult<DecimalBytePartsArray> {
155 let len = msp.len();
156 let dtype = DType::Decimal(decimal_dtype, msp.dtype().nullability());
157 let slots = DecimalBytePartsSlots { msp, lower_parts }.into_slots();
158 Array::try_from_parts(ArrayParts::new(
159 DecimalByteParts,
160 dtype,
161 len,
162 DecimalBytePartsData,
163 slots,
164 ))
165 }
166
167 pub(super) unsafe fn new_unchecked(
175 msp: ArrayRef,
176 lower_parts: Vec<ArrayRef>,
177 decimal_dtype: DecimalDType,
178 ) -> DecimalBytePartsArray {
179 let len = msp.len();
180 let dtype = DType::Decimal(decimal_dtype, msp.dtype().nullability());
181 let slots = DecimalBytePartsSlots { msp, lower_parts }.into_slots();
182 unsafe {
185 Array::from_parts_unchecked(ArrayParts::new(
186 DecimalByteParts,
187 dtype,
188 len,
189 DecimalBytePartsData,
190 slots,
191 ))
192 }
193 }
194}
195
196impl VTable for DecimalByteParts {
197 type TypedArrayData = DecimalBytePartsData;
198
199 type OperationsVTable = Self;
200 type ValidityVTable = ValidityVTableFromChild;
201
202 fn id(&self) -> ArrayId {
203 decimal_byte_parts_v2_id()
204 }
205
206 fn validate(
207 &self,
208 _data: &Self::TypedArrayData,
209 dtype: &DType,
210 len: usize,
211 slots: &[Option<ArrayRef>],
212 ) -> VortexResult<()> {
213 let Some(decimal_dtype) = dtype.as_decimal_opt() else {
214 vortex_bail!("expected decimal dtype, got {}", dtype)
215 };
216
217 let min_slots = DecimalBytePartsSlots::FIXED_COUNT;
218 let max_slots = min_slots + MAX_LOWER_PARTS;
219 vortex_ensure!(
220 (min_slots..=max_slots).contains(&slots.len()),
221 "expected {min_slots}..={max_slots} slots, got {}",
222 slots.len()
223 );
224 for (idx, slot) in slots.iter().enumerate() {
225 vortex_ensure!(slot.is_some(), "missing required slot {idx}");
226 }
227
228 let slots = DecimalBytePartsSlotsView::from_slots(slots);
229 DecimalBytePartsData::validate(
230 slots.msp,
231 slots.lower_parts.iter(),
232 *decimal_dtype,
233 dtype,
234 len,
235 )
236 }
237
238 fn nbuffers(_array: ArrayView<'_, Self>) -> usize {
239 0
240 }
241
242 fn buffer(_array: ArrayView<'_, Self>, idx: usize) -> BufferHandle {
243 vortex_panic!("DecimalBytePartsArray buffer index {idx} out of bounds")
244 }
245
246 fn buffer_name(_array: ArrayView<'_, Self>, idx: usize) -> Option<String> {
247 vortex_panic!("DecimalBytePartsArray buffer_name index {idx} out of bounds")
248 }
249
250 fn with_buffers(
251 &self,
252 array: ArrayView<'_, Self>,
253 buffers: &[BufferHandle],
254 ) -> VortexResult<ArrayParts<Self>> {
255 vortex_array::vtable::with_empty_buffers(self, array, buffers)
256 }
257
258 fn serialize(
259 _array: ArrayView<'_, Self>,
260 _session: &VortexSession,
261 ) -> VortexResult<Option<Vec<u8>>> {
262 vortex_bail!("DecimalByteParts serialization requires DecimalBytePartsPlugin")
263 }
264
265 fn deserialize(
266 &self,
267 _dtype: &DType,
268 _len: usize,
269 _metadata: &[u8],
270 _buffers: &[BufferHandle],
271 _children: &dyn ArrayChildren,
272 _session: &VortexSession,
273 ) -> VortexResult<ArrayParts<Self>> {
274 vortex_bail!("DecimalByteParts deserialization requires DecimalBytePartsPlugin")
275 }
276
277 fn slot_name(_array: ArrayView<'_, Self>, idx: usize) -> String {
278 DecimalBytePartsSlots::slot_name(idx)
279 }
280
281 fn reduce_parent(
282 array: ArrayView<'_, Self>,
283 parent: &ArrayRef,
284 child_idx: usize,
285 ) -> VortexResult<Option<ArrayRef>> {
286 PARENT_RULES.evaluate(array, parent, child_idx)
287 }
288
289 fn execute(array: Array<Self>, ctx: &mut ExecutionCtx) -> VortexResult<ExecutionResult> {
290 let mut array = require_child!(array, array.msp(), DecimalBytePartsSlots::MSP => Primitive);
291 for idx in 0..array.lower_parts().len() {
292 array = require_child!(
293 array,
294 array.lower_parts()[idx],
295 DecimalBytePartsSlots::LOWER_PARTS_OFFSET + idx => Primitive
296 );
297 }
298 let lower_parts = array.lower_parts().to_vec();
299 let assembled = assemble_decimal(array.msp(), &lower_parts, array.decimal_dtype(), ctx)?;
300
301 Ok(ExecutionResult::done(assembled))
302 }
303}
304
305#[array_slots(DecimalByteParts)]
306pub struct DecimalBytePartsSlots {
307 #[slot(0)]
309 pub msp: ArrayRef,
310 #[slot(1..)]
312 pub lower_parts: Vec<ArrayRef>,
313}
314
315pub(crate) trait DecimalBytePartsArrayExt: DecimalBytePartsArraySlotsExt {
316 fn decimal_dtype(&self) -> DecimalDType {
318 *self
319 .as_ref()
320 .dtype()
321 .as_decimal_opt()
322 .vortex_expect("must be a decimal dtype")
323 }
324
325 fn map_parts(
330 &self,
331 mut f: impl FnMut(&ArrayRef) -> VortexResult<ArrayRef>,
332 ) -> VortexResult<DecimalBytePartsArray> {
333 let msp = f(self.msp())?;
334 let lower_parts = self
335 .lower_parts()
336 .iter()
337 .map(&mut f)
338 .collect::<VortexResult<Vec<_>>>()?;
339 DecimalByteParts::try_new_with_lower_parts(msp, lower_parts, self.decimal_dtype())
340 }
341
342 fn with_msp(&self, msp: ArrayRef) -> VortexResult<DecimalBytePartsArray> {
347 DecimalByteParts::try_new_with_lower_parts(
348 msp,
349 self.lower_parts().to_vec(),
350 self.decimal_dtype(),
351 )
352 }
353}
354
355impl<T: TypedArrayRef<DecimalByteParts>> DecimalBytePartsArrayExt for T {}
356
357impl OperationsVTable<DecimalByteParts> for DecimalByteParts {
358 type ProbeState = ();
359
360 fn scalar_at(
361 array: ArrayView<'_, DecimalByteParts>,
362 index: usize,
363 ctx: &mut ExecutionCtx,
364 ) -> VortexResult<Scalar> {
365 let scalar = array.msp().execute_scalar(index, ctx)?;
366
367 let primitive_scalar = scalar.as_primitive();
370 let msp = primitive_scalar.as_::<i64>().vortex_expect("non-null");
371
372 let lower_parts = array
374 .lower_parts()
375 .iter()
376 .map(|part| {
377 Ok(part
378 .execute_scalar(index, ctx)?
379 .as_primitive()
380 .as_::<u64>()
381 .vortex_expect("lower parts are non-nullable"))
382 })
383 .collect::<VortexResult<Vec<_>>>()?;
384
385 let value = match lower_parts.as_slice() {
386 [] => DecimalValue::I64(msp),
387 [first] => DecimalValue::I128(assemble_wide_decimal_value(msp, [*first])),
388 [first, second] => {
389 DecimalValue::I256(assemble_wide_decimal_value(msp, [*first, *second]))
390 }
391 [first, second, third] => {
392 DecimalValue::I256(assemble_wide_decimal_value(msp, [*first, *second, *third]))
393 }
394 _ => vortex_bail!(
395 "at most {MAX_LOWER_PARTS} lower parts are supported, got {}",
396 lower_parts.len()
397 ),
398 };
399
400 Scalar::try_new(array.dtype().clone(), Some(ScalarValue::Decimal(value)))
401 }
402}
403
404impl ValidityChild<DecimalByteParts> for DecimalByteParts {
405 fn validity_child(array: ArrayView<'_, DecimalByteParts>) -> ArrayRef {
406 array.msp().clone()
408 }
409}
410
411#[cfg(test)]
412mod tests {
413 use rstest::rstest;
414 use vortex_array::Array;
415 use vortex_array::ArrayParts;
416 use vortex_array::ArrayRef;
417 use vortex_array::IntoArray;
418 use vortex_array::VortexSessionExecute;
419 use vortex_array::array_session;
420 use vortex_array::arrays::BoolArray;
421 use vortex_array::arrays::DecimalArray;
422 use vortex_array::arrays::PrimitiveArray;
423 use vortex_array::assert_arrays_eq;
424 use vortex_array::builtins::ArrayBuiltins;
425 use vortex_array::dtype::DType;
426 use vortex_array::dtype::DecimalDType;
427 use vortex_array::dtype::DecimalType;
428 use vortex_array::dtype::Nullability;
429 use vortex_array::dtype::PType;
430 use vortex_array::dtype::i256;
431 use vortex_array::scalar::DecimalValue;
432 use vortex_array::scalar::Scalar;
433 use vortex_array::scalar::ScalarValue;
434 use vortex_array::validity::Validity;
435 use vortex_buffer::buffer;
436 use vortex_error::VortexResult;
437
438 use super::DecimalByteParts;
439 use super::DecimalBytePartsArraySlotsExt;
440 use super::DecimalBytePartsData;
441 use crate::decimal_byte_parts::LOWER_PART_DTYPE;
442 use crate::decimal_byte_parts::MAX_LOWER_PARTS;
443 use crate::decimal_byte_parts::testing::i128_parts;
444 use crate::decimal_byte_parts::testing::i256_parts;
445
446 #[test]
447 fn test_scalar_at_decimal_parts() {
448 let decimal_dtype = DecimalDType::new(8, 2);
449 let dtype = DType::Decimal(decimal_dtype, Nullability::Nullable);
450 let array = DecimalByteParts::try_new(
451 PrimitiveArray::new(
452 buffer![100i32, 200i32, 400i32],
453 Validity::Array(BoolArray::from_iter(vec![false, true, true]).into_array()),
454 )
455 .into_array(),
456 decimal_dtype,
457 )
458 .unwrap()
459 .into_array();
460
461 assert_eq!(
462 Scalar::null(dtype.clone()),
463 array
464 .execute_scalar(0, &mut array_session().create_execution_ctx())
465 .unwrap()
466 );
467 assert_eq!(
468 Scalar::try_new(
469 dtype.clone(),
470 Some(ScalarValue::Decimal(DecimalValue::I64(200)))
471 )
472 .unwrap(),
473 array
474 .execute_scalar(1, &mut array_session().create_execution_ctx())
475 .unwrap()
476 );
477 assert_eq!(
478 Scalar::try_new(dtype, Some(ScalarValue::Decimal(DecimalValue::I64(400)))).unwrap(),
479 array
480 .execute_scalar(2, &mut array_session().create_execution_ctx())
481 .unwrap()
482 );
483 }
484
485 #[test]
486 fn test_lower_part_layout_i128() -> VortexResult<()> {
487 let array = i128_parts(vec![(3i128 << 64) | 7], Validity::NonNullable);
488 assert_eq!(array.lower_parts().len(), 1);
489 assert_eq!(array.msp().dtype().as_ptype(), PType::I64);
490 assert_eq!(array.lower_parts()[0].dtype(), &LOWER_PART_DTYPE);
491
492 let mut ctx = array_session().create_execution_ctx();
493 let msp = array.msp().clone().execute::<PrimitiveArray>(&mut ctx)?;
494 let lower = array.lower_parts()[0]
495 .clone()
496 .execute::<PrimitiveArray>(&mut ctx)?;
497 assert_eq!(msp.as_slice::<i64>(), &[3]);
498 assert_eq!(lower.as_slice::<u64>(), &[7]);
499 Ok(())
500 }
501
502 #[test]
503 fn test_lower_part_layout_i256() -> VortexResult<()> {
504 let array = i256_parts(
505 vec![i256::from_parts((7u128 << 64) | 8, (5i128 << 64) | 6)],
506 Validity::NonNullable,
507 );
508 assert_eq!(array.lower_parts().len(), MAX_LOWER_PARTS);
509
510 let mut ctx = array_session().create_execution_ctx();
511 let msp = array.msp().clone().execute::<PrimitiveArray>(&mut ctx)?;
512 assert_eq!(msp.as_slice::<i64>(), &[5]);
513 for (part, expected) in array.lower_parts().iter().zip([6u64, 7, 8]) {
514 let part = part.clone().execute::<PrimitiveArray>(&mut ctx)?;
515 assert_eq!(part.as_slice::<u64>(), &[expected]);
516 }
517 Ok(())
518 }
519
520 #[rstest]
521 fn test_scalar_at_matches_canonical_for_each_part_count(
522 #[values(false, true)] narrow_msp: bool,
523 #[values(0, 1, 2, 3)] lower_count: usize,
524 ) -> VortexResult<()> {
525 let validity = Validity::from_iter([false, true, true]);
526 let msp = if narrow_msp {
527 PrimitiveArray::new(buffer![0i8, 3, -3], validity)
528 } else {
529 PrimitiveArray::new(buffer![0i64, 3, -3], validity)
530 };
531 let lower = [4u64, 1, 2]
532 .into_iter()
533 .take(lower_count)
534 .map(|word| PrimitiveArray::new(buffer![word; 3], Validity::NonNullable).into_array())
535 .collect();
536 let dtype = DecimalDType::new(if lower_count <= 1 { 38 } else { 76 }, 0);
537 let array = DecimalByteParts::try_new_with_lower_parts(msp.into_array(), lower, dtype)?;
538 let mut ctx = array_session().create_execution_ctx();
539 let canonical = array
540 .clone()
541 .into_array()
542 .execute::<DecimalArray>(&mut ctx)?;
543 for row in 0..array.len() {
544 assert_eq!(
545 array.execute_scalar(row, &mut ctx)?,
546 canonical.execute_scalar(row, &mut ctx)?
547 );
548 }
549 Ok(())
550 }
551
552 #[test]
553 fn test_scalar_at_null_with_lower_parts() -> VortexResult<()> {
554 let array = i128_parts(
555 vec![1i128 << 100, 2, 3],
556 Validity::Array(BoolArray::from_iter([false, true, true]).into_array()),
557 )
558 .into_array();
559 let mut ctx = array_session().create_execution_ctx();
560 assert_eq!(
561 array.execute_scalar(0, &mut ctx)?,
562 Scalar::null(array.dtype().clone())
563 );
564 assert_eq!(
565 array.execute_scalar(1, &mut ctx)?,
566 Scalar::decimal(
567 DecimalValue::I128(2),
568 DecimalDType::new(38, 2),
569 Nullability::Nullable
570 )
571 );
572 Ok(())
573 }
574
575 #[rstest]
576 #[case([PType::U8, PType::U16, PType::U32])]
577 #[case([PType::U16, PType::U32, PType::U64])]
578 #[case([PType::U32, PType::U64, PType::U8])]
579 #[case([PType::U64, PType::U8, PType::U16])]
580 fn test_independently_narrowed_parts(
581 #[case] lower_ptypes: [PType; 3],
582 #[values(PType::I8, PType::I16, PType::I32, PType::I64)] msp_ptype: PType,
583 #[values(1, 2, 3)] lower_count: usize,
584 ) -> VortexResult<()> {
585 let mut ctx = array_session().create_execution_ctx();
586 let validity = Validity::from_iter([false, true, true]);
587 let msp = PrimitiveArray::new(buffer![0i64, 1, -1], validity.clone())
588 .into_array()
589 .cast(DType::Primitive(msp_ptype, Nullability::Nullable))?;
590 let words = lower_ptypes.map(|ptype| 1u64 << (ptype.byte_width() * 8 - 1));
592 let lower = lower_ptypes
593 .into_iter()
594 .zip(words)
595 .take(lower_count)
596 .map(|(ptype, word)| {
597 buffer![0u64, word, word]
598 .into_array()
599 .cast(DType::Primitive(ptype, Nullability::NonNullable))
600 })
601 .collect::<VortexResult<Vec<_>>>()?;
602 let dtype = DecimalDType::new(if lower_count == 1 { 38 } else { 76 }, 0);
603 let encoded = DecimalByteParts::try_new_with_lower_parts(msp, lower, dtype)?;
604 let expected = match lower_count {
605 1 => DecimalArray::new(
606 buffer![
607 0i128,
608 (1i128 << 64) | i128::from(words[0]),
609 (-1i128 << 64) | i128::from(words[0])
610 ],
611 dtype,
612 validity,
613 ),
614 2 => {
615 let low = (u128::from(words[0]) << 64) | u128::from(words[1]);
616 DecimalArray::new(
617 buffer![
618 i256::ZERO,
619 i256::from_parts(low, 1),
620 i256::from_parts(low, -1)
621 ],
622 dtype,
623 validity,
624 )
625 }
626 _ => {
627 let low = (u128::from(words[1]) << 64) | u128::from(words[2]);
628 DecimalArray::new(
629 buffer![
630 i256::ZERO,
631 i256::from_parts(low, (1i128 << 64) | i128::from(words[0])),
632 i256::from_parts(low, (-1i128 << 64) | i128::from(words[0])),
633 ],
634 dtype,
635 validity,
636 )
637 }
638 };
639 assert_arrays_eq!(encoded, expected, &mut ctx);
640 for row in 0..expected.len() {
641 assert_eq!(
642 encoded.execute_scalar(row, &mut ctx)?,
643 expected.execute_scalar(row, &mut ctx)?
644 );
645 }
646 Ok(())
647 }
648
649 fn msp() -> ArrayRef {
650 buffer![1i64, 2, 3].into_array()
651 }
652
653 fn lower_part() -> ArrayRef {
654 buffer![1u64, 2, 3].into_array()
655 }
656
657 #[rstest]
658 #[case::signed_lower_part(vec![buffer![1i64, 2, 3].into_array()], DecimalDType::new(38, 2))]
659 #[case::nullable_lower_part(
660 vec![PrimitiveArray::new(buffer![1u64, 2, 3], Validity::AllValid).into_array()],
661 DecimalDType::new(38, 2)
662 )]
663 #[case::mismatched_length(vec![buffer![1u64, 2].into_array()], DecimalDType::new(38, 2))]
664 #[case::too_many_parts(
665 vec![lower_part(), lower_part(), lower_part(), lower_part()],
666 DecimalDType::new(76, 2)
667 )]
668 fn test_rejects_invalid_parts(
669 #[case] lower_parts: Vec<ArrayRef>,
670 #[case] decimal_dtype: DecimalDType,
671 ) {
672 assert!(
673 DecimalByteParts::try_new_with_lower_parts(msp(), lower_parts, decimal_dtype).is_err()
674 );
675 }
676
677 #[rstest]
678 #[case::no_slots(vec![])]
679 #[case::missing_msp(vec![None])]
680 #[case::missing_lower(vec![Some(msp()), None])]
681 #[case::gap_in_lower(vec![Some(msp()), None, Some(lower_part())])]
682 fn test_rejects_missing_slots(#[case] slots: Vec<Option<ArrayRef>>) {
683 let parts = ArrayParts::new(
684 DecimalByteParts,
685 DType::Decimal(DecimalDType::new(76, 2), Nullability::NonNullable),
686 3,
687 DecimalBytePartsData,
688 slots.into_iter().collect(),
689 );
690 assert!(Array::try_from_parts(parts).is_err());
691 }
692
693 #[test]
694 fn test_wide_decimal_buffer_types() -> VortexResult<()> {
695 let mut ctx = array_session().create_execution_ctx();
696
697 let i128_array = i128_parts(vec![1i128 << 100], Validity::NonNullable);
698 let canonical = i128_array.into_array().execute::<DecimalArray>(&mut ctx)?;
699 assert_eq!(canonical.values_type(), DecimalType::I128);
700
701 let i256_array = i256_parts(vec![i256::from_parts(0, 1 << 100)], Validity::NonNullable);
702 let canonical = i256_array.into_array().execute::<DecimalArray>(&mut ctx)?;
703 assert_eq!(canonical.values_type(), DecimalType::I256);
704
705 let array = DecimalByteParts::try_new_with_lower_parts(
707 buffer![1i8, -1, 0].into_array(),
708 vec![buffer![7u64, 7, 7].into_array()],
709 DecimalDType::new(38, 2),
710 )?;
711 let canonical = array.into_array().execute::<DecimalArray>(&mut ctx)?;
712 assert_eq!(canonical.values_type(), DecimalType::I128);
713 assert_eq!(
714 canonical.buffer::<i128>().as_slice(),
715 &[(1i128 << 64) | 7, (-1i128 << 64) | 7, 7]
716 );
717
718 let array = DecimalByteParts::try_new_with_lower_parts(
720 buffer![1i8].into_array(),
721 vec![buffer![0u64].into_array(), buffer![9u64].into_array()],
722 DecimalDType::new(76, 2),
723 )?;
724 let canonical = array.into_array().execute::<DecimalArray>(&mut ctx)?;
725 assert_eq!(canonical.values_type(), DecimalType::I256);
726 assert_eq!(
727 canonical.buffer::<i256>().as_slice(),
728 &[i256::from_parts(9, 1)]
729 );
730 Ok(())
731 }
732
733 #[test]
734 fn test_unused_buffer_of_values_is_ignored_for_null_rows() -> VortexResult<()> {
735 let array = DecimalByteParts::try_new_with_lower_parts(
737 PrimitiveArray::new(
738 buffer![0i64, 0, 0],
739 Validity::Array(BoolArray::from_iter([false, false, true]).into_array()),
740 )
741 .into_array(),
742 vec![buffer![7u64, 9, 11].into_array()],
743 DecimalDType::new(38, 2),
744 )?
745 .into_array();
746
747 let mut ctx = array_session().create_execution_ctx();
748 assert_eq!(
749 array.execute_scalar(0, &mut ctx)?,
750 Scalar::null(array.dtype().clone())
751 );
752 assert_arrays_eq!(
753 array,
754 DecimalArray::new(
755 buffer![0i128, 0, 11],
756 DecimalDType::new(38, 2),
757 Validity::from_iter([false, false, true]),
758 ),
759 &mut ctx
760 );
761 Ok(())
762 }
763}