1use num_traits::AsPrimitive;
5use num_traits::CheckedMul;
6use num_traits::ToPrimitive as NumToPrimitive;
7use vortex_buffer::Buffer;
8use vortex_buffer::BufferMut;
9use vortex_compute::lane_kernels::IndexedSourceExt;
10use vortex_error::VortexError;
11use vortex_error::VortexExpect;
12use vortex_error::VortexResult;
13use vortex_error::vortex_bail;
14use vortex_error::vortex_err;
15use vortex_error::vortex_panic;
16use vortex_mask::Mask;
17
18use crate::ArrayRef;
19use crate::ExecutionCtx;
20use crate::IntoArray;
21use crate::array::ArrayView;
22use crate::arrays::Decimal;
23use crate::arrays::DecimalArray;
24use crate::arrays::PrimitiveArray;
25use crate::dtype::BigCast;
26use crate::dtype::DType;
27use crate::dtype::DecimalDType;
28use crate::dtype::DecimalType;
29use crate::dtype::NativeDecimalType;
30use crate::dtype::Nullability;
31use crate::dtype::PType;
32use crate::dtype::i256;
33use crate::match_each_decimal_value_type;
34use crate::scalar::DecimalValue;
35use crate::scalar_fn::fns::cast::CastKernel;
36use crate::scalar_fn::fns::cast::CastReduce;
37use crate::validity::Validity;
38
39impl CastReduce for Decimal {
40 fn cast(array: ArrayView<'_, Decimal>, dtype: &DType) -> VortexResult<Option<ArrayRef>> {
41 let DType::Decimal(to_decimal_dtype, to_nullability) = dtype else {
44 return Ok(None);
45 };
46 let DType::Decimal(from_decimal_dtype, _) = array.dtype() else {
47 vortex_panic!(
48 "DecimalArray must have decimal dtype, got {:?}",
49 array.dtype()
50 );
51 };
52
53 if from_decimal_dtype != to_decimal_dtype {
54 return Ok(None);
55 }
56
57 let Some(new_validity) = array
58 .validity()?
59 .trivially_cast_nullability(*to_nullability, array.len())?
60 else {
61 return Ok(None);
62 };
63
64 unsafe {
66 Ok(Some(
67 DecimalArray::new_unchecked_handle(
68 array.buffer_handle().clone(),
69 array.values_type(),
70 *to_decimal_dtype,
71 new_validity,
72 )
73 .into_array(),
74 ))
75 }
76 }
77}
78
79impl CastKernel for Decimal {
80 fn cast(
81 array: ArrayView<'_, Decimal>,
82 dtype: &DType,
83 ctx: &mut ExecutionCtx,
84 ) -> VortexResult<Option<ArrayRef>> {
85 let DType::Decimal(from_decimal_dtype, _) = array.dtype() else {
86 vortex_panic!(
87 "DecimalArray must have decimal dtype, got {:?}",
88 array.dtype()
89 );
90 };
91 if let DType::Primitive(PType::F64, nullability) = dtype {
92 let scale = from_decimal_dtype.scale();
93 return cast_to_f64(array, scale, *nullability, ctx).map(Some);
94 }
95 let DType::Decimal(to_decimal_dtype, to_nullability) = dtype else {
96 return Ok(None);
97 };
98
99 if array.dtype() == dtype {
101 return Ok(Some(array.array().clone()));
102 }
103
104 let validity = array.validity()?;
105
106 let new_validity = validity
108 .clone()
109 .cast_nullability(*to_nullability, array.len(), ctx)?;
110
111 if from_decimal_dtype.scale() == to_decimal_dtype.scale()
116 && to_decimal_dtype.precision() >= from_decimal_dtype.precision()
117 && array
118 .values_type()
119 .is_compatible_decimal_value_type(*to_decimal_dtype)
120 {
121 unsafe {
124 return Ok(Some(
125 DecimalArray::new_unchecked_handle(
126 array.buffer_handle().clone(),
127 array.values_type(),
128 *to_decimal_dtype,
129 new_validity,
130 )
131 .into_array(),
132 ));
133 }
134 }
135
136 let valid_values = validity.execute_mask(array.len(), ctx)?;
137 let target_values_type = DecimalType::smallest_decimal_value_type(to_decimal_dtype);
138
139 match_each_decimal_value_type!(array.values_type(), |F| {
140 match_each_decimal_value_type!(target_values_type, |T| {
141 cast_decimal_values::<F, T>(
142 array,
143 *from_decimal_dtype,
144 *to_decimal_dtype,
145 new_validity,
146 &valid_values,
147 )
148 .map(Some)
149 })
150 })
151 }
152}
153
154fn cast_to_f64(
155 array: ArrayView<'_, Decimal>,
156 scale: i8,
157 nullability: Nullability,
158 ctx: &mut ExecutionCtx,
159) -> VortexResult<ArrayRef> {
160 let source_validity = array.validity()?;
161 let n = array.len();
162 let mask = source_validity.execute_mask(n, ctx)?;
163 let validity = source_validity.cast_nullability(nullability, n, ctx)?;
164
165 let scale: i32 = scale.as_();
166 let inv_factor: f64 = 10f64.powi(-scale);
167
168 let buffer = match &mask {
169 Mask::AllFalse(_) => BufferMut::<f64>::zeroed(n),
170 Mask::AllTrue(_) => match_each_decimal_value_type!(array.values_type(), |F| {
171 let values = array.buffer::<F>();
172 let values = values.as_slice();
173 let mut out = BufferMut::<f64>::with_capacity(n);
174 values.map_into(&mut out.spare_capacity_mut()[..n], |v: F| {
175 to_f64_lossy::<F>(v) * inv_factor
176 });
177 unsafe { out.set_len(n) };
179 out
180 }),
181 Mask::Values(mask_values) => match_each_decimal_value_type!(array.values_type(), |F| {
182 let values = array.buffer::<F>();
183 let values = values.as_slice();
184 let mut out = BufferMut::<f64>::with_capacity(n);
185 let write_result = values.try_map_masked_into(
186 mask_values.bit_buffer(),
187 &mut out.spare_capacity_mut()[..n],
188 |v: F| Some(to_f64_lossy::<F>(v) * inv_factor),
189 );
190 debug_assert!(write_result.is_ok());
191 unsafe { out.set_len(n) };
193 out
194 }),
195 };
196
197 Ok(PrimitiveArray::new(buffer, validity).into_array())
198}
199
200#[inline]
201fn to_f64_lossy<F: NativeDecimalType>(value: F) -> f64 {
202 NumToPrimitive::to_f64(&value).unwrap_or(f64::NAN)
203}
204
205fn cast_decimal_values<F, T>(
206 array: ArrayView<'_, Decimal>,
207 from_decimal_dtype: DecimalDType,
208 to_decimal_dtype: DecimalDType,
209 validity: Validity,
210 valid_values: &Mask,
211) -> VortexResult<ArrayRef>
212where
213 F: NativeDecimalType,
214 T: NativeDecimalType + CheckedMul,
215 DecimalValue: From<F>,
216{
217 let values = array.buffer::<F>();
218 let values = values.as_slice();
219 let cast_plan = DecimalCastPlan::<T>::new(from_decimal_dtype, to_decimal_dtype);
220
221 let buffer = match valid_values {
222 Mask::AllTrue(_) => {
223 let mut buffer = BufferMut::<T>::with_capacity(values.len());
224 values
225 .try_map_into(&mut buffer.spare_capacity_mut()[..values.len()], |value| {
226 cast_plan.cast(value)
227 })
228 .map_err(|idx| {
229 decimal_cast_error::<F, T>(values[idx], from_decimal_dtype, to_decimal_dtype)
230 })?;
231 unsafe { buffer.set_len(values.len()) };
233 buffer.freeze()
234 }
235 Mask::AllFalse(_) => BufferMut::<T>::zeroed(values.len()).freeze(),
236 Mask::Values(mask) => {
237 let mut buffer = BufferMut::<T>::with_capacity(values.len());
238 values
239 .try_map_masked_into(
240 mask.bit_buffer(),
241 &mut buffer.spare_capacity_mut()[..values.len()],
242 |value| cast_plan.cast(value),
243 )
244 .map_err(|idx| {
245 decimal_cast_error::<F, T>(values[idx], from_decimal_dtype, to_decimal_dtype)
246 })?;
247 unsafe { buffer.set_len(values.len()) };
249 buffer.freeze()
250 }
251 };
252
253 Ok(DecimalArray::new(buffer, to_decimal_dtype, validity).into_array())
254}
255
256#[cold]
257fn decimal_cast_error<F, T>(
258 value: F,
259 from_decimal_dtype: DecimalDType,
260 to_decimal_dtype: DecimalDType,
261) -> VortexError
262where
263 F: NativeDecimalType,
264 T: NativeDecimalType,
265 DecimalValue: From<F>,
266{
267 match DecimalValue::from(value)
268 .cast_decimal(from_decimal_dtype, to_decimal_dtype)
269 .and_then(|value| {
270 value.cast::<T>().ok_or_else(|| {
271 vortex_err!(
272 "decimal value cannot be represented as {} after casting to {}",
273 T::DECIMAL_TYPE,
274 to_decimal_dtype
275 )
276 })
277 }) {
278 Ok(_) => {
279 debug_assert!(
283 false,
284 "decimal fast-path cast rejected value {value} that the slow path accepts \
285 (from {from_decimal_dtype} to {to_decimal_dtype})"
286 );
287 vortex_err!(
288 "decimal value cannot be represented as {} after casting from {} to {}",
289 T::DECIMAL_TYPE,
290 from_decimal_dtype,
291 to_decimal_dtype
292 )
293 }
294 Err(error) => error,
295 }
296}
297
298#[derive(Debug, Clone, Copy)]
299enum DecimalCastPlan<T> {
300 SameScale { min: T, max: T },
301 ScaleUp { factor: T, min: T, max: T },
302 ScaleUpOverflow,
303 ScaleDown { factor: i256, min: i256, max: i256 },
304 ScaleDownOverflow,
305}
306
307impl<T> DecimalCastPlan<T>
308where
309 T: NativeDecimalType + CheckedMul,
310{
311 fn new(from_decimal_dtype: DecimalDType, to_decimal_dtype: DecimalDType) -> Self {
312 let scale_delta = to_decimal_dtype.scale() as i16 - from_decimal_dtype.scale() as i16;
313 if scale_delta == 0 {
314 let (min, max) = decimal_precision_range::<T>(to_decimal_dtype);
315 return Self::SameScale { min, max };
316 }
317
318 if scale_delta > 0 {
319 let Some(factor) = decimal_scale_factor::<T>(scale_delta as u32) else {
320 return Self::ScaleUpOverflow;
321 };
322 let (min, max) = decimal_precision_range::<T>(to_decimal_dtype);
323 return Self::ScaleUp { factor, min, max };
324 }
325
326 let Some(factor) = decimal_scale_factor::<i256>((-scale_delta) as u32) else {
327 return Self::ScaleDownOverflow;
328 };
329 let (min, max) = decimal_precision_range::<i256>(to_decimal_dtype);
330 Self::ScaleDown { factor, min, max }
331 }
332
333 #[inline]
334 fn cast<F>(&self, value: F) -> Option<T>
335 where
336 F: NativeDecimalType,
337 {
338 match *self {
339 DecimalCastPlan::SameScale { min, max } => {
340 let value = <T as BigCast>::from(value)?;
341 (value >= min && value <= max).then_some(value)
342 }
343 DecimalCastPlan::ScaleUp { factor, min, max } => {
344 let value = <T as BigCast>::from(value)?;
345 let value = value.checked_mul(&factor)?;
346 (value >= min && value <= max).then_some(value)
347 }
348 DecimalCastPlan::ScaleUpOverflow | DecimalCastPlan::ScaleDownOverflow => {
349 (value == F::default()).then_some(T::default())
350 }
351 DecimalCastPlan::ScaleDown { factor, min, max } => {
352 let value = <i256 as BigCast>::from(value)?;
353 if value == i256::ZERO {
354 return Some(T::default());
355 }
356 if value % factor != i256::ZERO {
357 return None;
358 }
359
360 let value = value / factor;
361 if value < min || value > max {
362 return None;
363 }
364 <T as BigCast>::from(value)
365 }
366 }
367 }
368}
369
370fn decimal_precision_range<T: NativeDecimalType>(decimal_dtype: DecimalDType) -> (T, T) {
371 let precision = usize::from(decimal_dtype.precision());
372 (
373 T::MIN_BY_PRECISION[precision],
374 T::MAX_BY_PRECISION[precision],
375 )
376}
377
378fn decimal_scale_factor<T>(exp: u32) -> Option<T>
379where
380 T: NativeDecimalType + CheckedMul,
381{
382 let ten = <T as BigCast>::from(10_i8)?;
383 let mut factor = <T as BigCast>::from(1_i8)?;
384 for _ in 0..exp {
385 factor = factor.checked_mul(&ten)?;
386 }
387 Some(factor)
388}
389
390pub fn upcast_decimal_values(
402 array: ArrayView<'_, Decimal>,
403 to_values_type: DecimalType,
404) -> VortexResult<DecimalArray> {
405 let from_values_type = array.values_type();
406
407 if from_values_type == to_values_type {
409 return Ok(array.array().as_::<Decimal>().into_owned());
410 }
411
412 if to_values_type < from_values_type {
414 vortex_bail!(
415 "Cannot downcast decimal values from {:?} to {:?}. Only upcasting is supported.",
416 from_values_type,
417 to_values_type
418 );
419 }
420
421 let decimal_dtype = array.decimal_dtype();
422 let validity = array.validity()?;
423
424 match_each_decimal_value_type!(from_values_type, |F| {
426 let from_buffer = array.buffer::<F>();
427 match_each_decimal_value_type!(to_values_type, |T| {
428 let to_buffer = upcast_decimal_buffer::<F, T>(from_buffer);
429 Ok(DecimalArray::new(to_buffer, decimal_dtype, validity))
430 })
431 })
432}
433
434fn upcast_decimal_buffer<F: NativeDecimalType, T: NativeDecimalType>(from: Buffer<F>) -> Buffer<T> {
437 from.iter()
438 .map(|&v| T::from(v).vortex_expect("upcast should never fail"))
439 .collect()
440}
441
442#[cfg(test)]
443mod tests {
444 use rstest::rstest;
445 use vortex_buffer::buffer;
446
447 use super::upcast_decimal_values;
448 use crate::Canonical;
449 use crate::IntoArray;
450 use crate::VortexSessionExecute;
451 use crate::array_session;
452 use crate::arrays::DecimalArray;
453 use crate::builtins::ArrayBuiltins;
454 use crate::compute::conformance::cast::test_cast_conformance;
455 use crate::dtype::DType;
456 use crate::dtype::DecimalDType;
457 use crate::dtype::DecimalType;
458 use crate::dtype::Nullability;
459 use crate::dtype::PType;
460 use crate::scalar::Scalar;
461 use crate::validity::Validity;
462
463 #[test]
464 fn cast_decimal_to_nullable() {
465 let mut ctx = array_session().create_execution_ctx();
466 let decimal_dtype = DecimalDType::new(10, 2);
467 let array = DecimalArray::new(
468 buffer![100i32, 200, 300],
469 decimal_dtype,
470 Validity::NonNullable,
471 );
472
473 let nullable_dtype = DType::Decimal(decimal_dtype, Nullability::Nullable);
475 let casted = array
476 .into_array()
477 .cast(nullable_dtype.clone())
478 .unwrap()
479 .execute::<DecimalArray>(&mut ctx)
480 .unwrap();
481
482 assert_eq!(casted.dtype(), &nullable_dtype);
483 assert!(matches!(casted.validity(), Ok(Validity::AllValid)));
484 assert_eq!(casted.len(), 3);
485 }
486
487 #[test]
488 fn cast_nullable_to_non_nullable() {
489 let mut ctx = array_session().create_execution_ctx();
490 let decimal_dtype = DecimalDType::new(10, 2);
491
492 let array = DecimalArray::new(buffer![100i32, 200, 300], decimal_dtype, Validity::AllValid);
494
495 let non_nullable_dtype = DType::Decimal(decimal_dtype, Nullability::NonNullable);
497 let casted = array
498 .into_array()
499 .cast(non_nullable_dtype.clone())
500 .unwrap()
501 .execute::<DecimalArray>(&mut ctx)
502 .unwrap();
503
504 assert_eq!(casted.dtype(), &non_nullable_dtype);
505 assert!(matches!(casted.validity(), Ok(Validity::NonNullable)));
506 }
507
508 #[test]
509 #[should_panic(expected = "Cannot cast array with invalid values to non-nullable type")]
510 fn cast_nullable_with_nulls_to_non_nullable_fails() {
511 let mut ctx = array_session().create_execution_ctx();
512 let decimal_dtype = DecimalDType::new(10, 2);
513
514 let array = DecimalArray::from_option_iter([Some(100i32), None, Some(300)], decimal_dtype);
516
517 let non_nullable_dtype = DType::Decimal(decimal_dtype, Nullability::NonNullable);
519 array
520 .into_array()
521 .cast(non_nullable_dtype)
522 .and_then(|a| a.execute::<Canonical>(&mut ctx).map(|c| c.into_array()))
523 .unwrap();
524 }
525
526 #[test]
527 fn cast_different_scale_rescales() {
528 let mut ctx = array_session().create_execution_ctx();
529 let array = DecimalArray::new(
530 buffer![100i32],
531 DecimalDType::new(10, 2),
532 Validity::NonNullable,
533 );
534
535 let different_dtype = DType::Decimal(DecimalDType::new(15, 3), Nullability::NonNullable);
537 let casted = array
538 .into_array()
539 .cast(different_dtype)
540 .unwrap()
541 .execute::<DecimalArray>(&mut ctx)
542 .unwrap();
543
544 assert_eq!(casted.precision(), 15);
545 assert_eq!(casted.scale(), 3);
546 assert_eq!(casted.values_type(), DecimalType::I64);
547 assert_eq!(casted.buffer::<i64>().as_ref(), &[1000]);
548 }
549
550 #[test]
551 fn cast_downcast_precision_succeeds_when_values_fit() {
552 let mut ctx = array_session().create_execution_ctx();
553 let array = DecimalArray::new(
554 buffer![100i64],
555 DecimalDType::new(18, 2),
556 Validity::NonNullable,
557 );
558
559 let smaller_dtype = DType::Decimal(DecimalDType::new(10, 2), Nullability::NonNullable);
561 let casted = array
562 .into_array()
563 .cast(smaller_dtype)
564 .unwrap()
565 .execute::<DecimalArray>(&mut ctx)
566 .unwrap();
567
568 assert_eq!(casted.precision(), 10);
569 assert_eq!(casted.scale(), 2);
570 assert_eq!(casted.buffer::<i64>().as_ref(), &[100]);
571 }
572
573 #[test]
574 fn cast_downcast_precision_checks_values() {
575 let mut ctx = array_session().create_execution_ctx();
576 let array = DecimalArray::new(
577 buffer![1000i64],
578 DecimalDType::new(18, 0),
579 Validity::NonNullable,
580 );
581
582 let smaller_dtype = DType::Decimal(DecimalDType::new(3, 0), Nullability::NonNullable);
583 let result = array
584 .into_array()
585 .cast(smaller_dtype)
586 .and_then(|a| a.execute::<Canonical>(&mut ctx).map(|c| c.into_array()));
587
588 assert!(result.is_err());
589 assert!(
590 result
591 .unwrap_err()
592 .to_string()
593 .contains("does not fit in precision")
594 );
595 }
596
597 #[test]
598 fn cast_lower_scale_requires_exact_rescale() {
599 let mut ctx = array_session().create_execution_ctx();
600 let array = DecimalArray::new(
601 buffer![123456i64],
602 DecimalDType::new(10, 4),
603 Validity::NonNullable,
604 );
605
606 let lower_scale_dtype = DType::Decimal(DecimalDType::new(10, 2), Nullability::NonNullable);
607 let result = array
608 .into_array()
609 .cast(lower_scale_dtype)
610 .and_then(|a| a.execute::<Canonical>(&mut ctx).map(|c| c.into_array()));
611
612 assert!(result.is_err());
613 assert!(
614 result
615 .unwrap_err()
616 .to_string()
617 .contains("would lose precision")
618 );
619 }
620
621 #[test]
622 fn cast_lower_scale_ignores_null_lane_failures() {
623 let mut ctx = array_session().create_execution_ctx();
624 let array = DecimalArray::new(
625 buffer![100i64, 123456],
626 DecimalDType::new(10, 4),
627 Validity::from_iter([true, false]),
628 );
629
630 let lower_scale_dtype = DType::Decimal(DecimalDType::new(3, 2), Nullability::Nullable);
631 let casted = array
632 .into_array()
633 .cast(lower_scale_dtype)
634 .unwrap()
635 .execute::<DecimalArray>(&mut ctx)
636 .unwrap();
637
638 let mask = casted
639 .as_ref()
640 .validity()
641 .unwrap()
642 .execute_mask(
643 casted.as_ref().len(),
644 &mut array_session().create_execution_ctx(),
645 )
646 .unwrap();
647 assert!(mask.value(0));
648 assert!(!mask.value(1));
649 assert_eq!(casted.buffer::<i16>().as_ref()[0], 1);
650 }
651
652 #[test]
653 fn cast_upcast_precision_succeeds() {
654 let mut ctx = array_session().create_execution_ctx();
655 let array = DecimalArray::new(
656 buffer![100i32, 200, 300],
657 DecimalDType::new(10, 2),
658 Validity::NonNullable,
659 );
660
661 let wider_dtype = DType::Decimal(DecimalDType::new(38, 2), Nullability::NonNullable);
663 let casted = array
664 .into_array()
665 .cast(wider_dtype)
666 .unwrap()
667 .execute::<DecimalArray>(&mut ctx)
668 .unwrap();
669
670 assert_eq!(casted.precision(), 38);
671 assert_eq!(casted.scale(), 2);
672 assert_eq!(casted.len(), 3);
673 assert_eq!(casted.values_type(), DecimalType::I128);
675 }
676
677 #[test]
678 fn cast_widening_same_physical_type_is_zero_copy() {
679 let mut ctx = array_session().create_execution_ctx();
680 let array = DecimalArray::new(
683 buffer![100i64, 200, 300],
684 DecimalDType::new(10, 2),
685 Validity::NonNullable,
686 );
687 let src_ptr = array.buffer::<i64>().as_ptr();
688
689 let wider_dtype = DType::Decimal(DecimalDType::new(18, 2), Nullability::NonNullable);
690 let casted = array
691 .into_array()
692 .cast(wider_dtype)
693 .unwrap()
694 .execute::<DecimalArray>(&mut ctx)
695 .unwrap();
696
697 assert_eq!(casted.precision(), 18);
698 assert_eq!(casted.scale(), 2);
699 assert_eq!(casted.values_type(), DecimalType::I64);
700 assert_eq!(casted.buffer::<i64>().as_ref(), &[100, 200, 300]);
701 assert_eq!(
703 casted.buffer::<i64>().as_ptr(),
704 src_ptr,
705 "precision-widening cast must reuse the source values buffer"
706 );
707 }
708
709 #[test]
710 fn cast_to_non_decimal_returns_err() {
711 let mut ctx = array_session().create_execution_ctx();
712 let array = DecimalArray::new(
713 buffer![100i32],
714 DecimalDType::new(10, 2),
715 Validity::NonNullable,
716 );
717
718 let result = array
720 .into_array()
721 .cast(DType::Utf8(Nullability::NonNullable))
722 .and_then(|a| a.execute::<Canonical>(&mut ctx).map(|c| c.into_array()));
723
724 assert!(result.is_err());
725 assert!(
726 result
727 .unwrap_err()
728 .to_string()
729 .contains("No CastKernel to cast canonical array")
730 );
731 }
732
733 #[rstest]
734 #[case(DecimalArray::new(buffer![100i32, 200, 300], DecimalDType::new(10, 2), Validity::NonNullable))]
735 #[case(DecimalArray::new(buffer![10000i64, 20000, 30000], DecimalDType::new(18, 4), Validity::NonNullable))]
736 #[case(DecimalArray::from_option_iter([Some(100i32), None, Some(300)], DecimalDType::new(10, 2)))]
737 #[case(DecimalArray::new(buffer![42i32], DecimalDType::new(5, 1), Validity::NonNullable))]
738 fn test_cast_decimal_conformance(#[case] array: DecimalArray) {
739 test_cast_conformance(
740 &array.into_array(),
741 &mut array_session().create_execution_ctx(),
742 );
743 }
744
745 #[test]
746 fn upcast_decimal_values_i32_to_i64() {
747 let decimal_dtype = DecimalDType::new(10, 2);
748 let array = DecimalArray::new(
749 buffer![100i32, 200, 300],
750 decimal_dtype,
751 Validity::NonNullable,
752 );
753
754 assert_eq!(array.values_type(), DecimalType::I32);
755
756 let array = array.as_view();
757 let casted = upcast_decimal_values(array, DecimalType::I64).unwrap();
758
759 assert_eq!(casted.values_type(), DecimalType::I64);
760 assert_eq!(casted.decimal_dtype(), decimal_dtype);
761 assert_eq!(casted.len(), 3);
762
763 let buffer = casted.buffer::<i64>();
765 assert_eq!(buffer.as_ref(), &[100i64, 200, 300]);
766 }
767
768 #[test]
769 fn upcast_decimal_values_i64_to_i128() {
770 let decimal_dtype = DecimalDType::new(18, 4);
771 let array = DecimalArray::new(
772 buffer![10000i64, 20000, 30000],
773 decimal_dtype,
774 Validity::NonNullable,
775 );
776
777 let array = array.as_view();
778 let casted = upcast_decimal_values(array, DecimalType::I128).unwrap();
779
780 assert_eq!(casted.values_type(), DecimalType::I128);
781 assert_eq!(casted.decimal_dtype(), decimal_dtype);
782
783 let buffer = casted.buffer::<i128>();
784 assert_eq!(buffer.as_ref(), &[10000i128, 20000, 30000]);
785 }
786
787 #[test]
788 fn upcast_decimal_values_same_type_returns_clone() {
789 let decimal_dtype = DecimalDType::new(10, 2);
790 let array = DecimalArray::new(
791 buffer![100i32, 200, 300],
792 decimal_dtype,
793 Validity::NonNullable,
794 );
795
796 let array = array.as_view();
797 let casted = upcast_decimal_values(array, DecimalType::I32).unwrap();
798
799 assert_eq!(casted.values_type(), DecimalType::I32);
800 assert_eq!(casted.decimal_dtype(), decimal_dtype);
801 }
802
803 #[test]
804 fn upcast_decimal_values_with_nulls() {
805 let decimal_dtype = DecimalDType::new(10, 2);
806 let array = DecimalArray::from_option_iter([Some(100i32), None, Some(300)], decimal_dtype);
807
808 let array = array.as_view();
809 let casted = upcast_decimal_values(array, DecimalType::I64).unwrap();
810
811 assert_eq!(casted.values_type(), DecimalType::I64);
812 assert_eq!(casted.len(), 3);
813
814 let mask = casted
816 .as_ref()
817 .validity()
818 .unwrap()
819 .execute_mask(
820 casted.as_ref().len(),
821 &mut array_session().create_execution_ctx(),
822 )
823 .unwrap();
824 assert!(mask.value(0));
825 assert!(!mask.value(1));
826 assert!(mask.value(2));
827
828 let buffer = casted.buffer::<i64>();
830 assert_eq!(buffer[0], 100);
831 assert_eq!(buffer[2], 300);
832 }
833
834 #[test]
835 fn upcast_decimal_values_downcast_fails() {
836 let decimal_dtype = DecimalDType::new(18, 4);
837 let array = DecimalArray::new(
838 buffer![10000i64, 20000, 30000],
839 decimal_dtype,
840 Validity::NonNullable,
841 );
842
843 let array = array.as_view();
845 let result = upcast_decimal_values(array, DecimalType::I32);
846 assert!(result.is_err());
847 assert!(
848 result
849 .unwrap_err()
850 .to_string()
851 .contains("Cannot downcast decimal values")
852 );
853 }
854
855 #[test]
856 fn cast_decimal_f64() {
857 let dtype = DecimalDType::new(6, 2);
858 let array = DecimalArray::new(buffer![125i32, 250, -375], dtype, Validity::NonNullable);
859 let target = DType::Primitive(PType::F64, Nullability::NonNullable);
860 let casted = array.into_array().cast(target.clone()).unwrap();
861 assert_eq!(casted.dtype(), &target);
862
863 let mut ctx = array_session().create_execution_ctx();
864 let primitive = casted
865 .execute::<Canonical>(&mut ctx)
866 .unwrap()
867 .into_primitive();
868 assert_eq!(primitive.to_buffer::<f64>().as_ref(), &[1.25, 2.5, -3.75]);
869 }
870
871 #[test]
872 fn cast_decimal_f64_null() {
873 let values = [Some(100i32), None, Some(-200)];
874 let array = DecimalArray::from_option_iter(values, DecimalDType::new(6, 2));
875 let target = DType::Primitive(PType::F64, Nullability::Nullable);
876 let casted = array.into_array().cast(target.clone()).unwrap();
877 assert_eq!(casted.dtype(), &target);
878
879 let mut ctx = array_session().create_execution_ctx();
880 let primitive = casted
881 .execute::<Canonical>(&mut ctx)
882 .unwrap()
883 .into_primitive();
884
885 assert!(primitive.is_valid(0, &mut ctx).unwrap());
886 assert!(!primitive.is_valid(1, &mut ctx).unwrap());
887 assert!(primitive.is_valid(2, &mut ctx).unwrap());
888
889 assert_eq!(
890 primitive.execute_scalar(0, &mut ctx).unwrap(),
891 Scalar::from(1f64)
892 );
893 assert_eq!(
894 primitive.execute_scalar(2, &mut ctx).unwrap(),
895 Scalar::from(-2f64)
896 );
897 }
898
899 #[test]
900 fn cast_decimal_f64_all_null() {
901 let dtype = DecimalDType::new(6, 2);
902 let buf = buffer![i32::MAX, i32::MIN, 12345];
903 let array = DecimalArray::new(buf, dtype, Validity::AllInvalid);
904 let target = DType::Primitive(PType::F64, Nullability::Nullable);
905 let casted = array.into_array().cast(target.clone()).unwrap();
906 assert_eq!(casted.dtype(), &target);
907
908 let primitive = casted
909 .execute::<Canonical>(&mut array_session().create_execution_ctx())
910 .unwrap()
911 .into_primitive();
912 let mut ctx = array_session().create_execution_ctx();
913 for i in 0..2 {
914 assert!(!primitive.is_valid(i, &mut ctx).unwrap());
915 }
916 assert_eq!(primitive.to_buffer::<f64>().as_ref(), &[0.0, 0.0, 0.0]);
917 }
918}