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_result = match DecimalCastPlan::<T>::new(from_decimal_dtype, to_decimal_dtype) {
220 DecimalCastPlan::SameScale { min, max } => {
221 cast_decimal_buffer(values, valid_values, |value| {
222 let value = <T as BigCast>::from(value)?;
223 (value >= min && value <= max).then_some(value)
224 })
225 }
226 cast_plan => cast_decimal_buffer(values, valid_values, |value| cast_plan.cast(value)),
227 };
228 let buffer = cast_result.map_err(|idx| {
229 decimal_cast_error::<F, T>(values[idx], from_decimal_dtype, to_decimal_dtype)
230 })?;
231
232 Ok(DecimalArray::new(buffer, to_decimal_dtype, validity).into_array())
233}
234
235fn cast_decimal_buffer<F, T>(
236 values: &[F],
237 valid_values: &Mask,
238 mut cast: impl FnMut(F) -> Option<T>,
239) -> Result<Buffer<T>, usize>
240where
241 F: NativeDecimalType,
242 T: NativeDecimalType,
243{
244 let mut buffer = BufferMut::<T>::with_capacity(values.len());
245 match valid_values {
246 Mask::AllTrue(_) => {
247 values.try_map_into(&mut buffer.spare_capacity_mut()[..values.len()], &mut cast)?;
248 }
249 Mask::AllFalse(_) => return Ok(BufferMut::<T>::zeroed(values.len()).freeze()),
250 Mask::Values(mask) => {
251 values.try_map_masked_into(
252 mask.bit_buffer(),
253 &mut buffer.spare_capacity_mut()[..values.len()],
254 &mut cast,
255 )?;
256 }
257 }
258 unsafe { buffer.set_len(values.len()) };
260 Ok(buffer.freeze())
261}
262
263#[cold]
264fn decimal_cast_error<F, T>(
265 value: F,
266 from_decimal_dtype: DecimalDType,
267 to_decimal_dtype: DecimalDType,
268) -> VortexError
269where
270 F: NativeDecimalType,
271 T: NativeDecimalType,
272 DecimalValue: From<F>,
273{
274 match DecimalValue::from(value)
275 .cast_decimal(from_decimal_dtype, to_decimal_dtype)
276 .and_then(|value| {
277 value.cast::<T>().ok_or_else(|| {
278 vortex_err!(
279 "decimal value cannot be represented as {} after casting to {}",
280 T::DECIMAL_TYPE,
281 to_decimal_dtype
282 )
283 })
284 }) {
285 Ok(_) => {
286 debug_assert!(
290 false,
291 "decimal fast-path cast rejected value {value} that the slow path accepts \
292 (from {from_decimal_dtype} to {to_decimal_dtype})"
293 );
294 vortex_err!(
295 "decimal value cannot be represented as {} after casting from {} to {}",
296 T::DECIMAL_TYPE,
297 from_decimal_dtype,
298 to_decimal_dtype
299 )
300 }
301 Err(error) => error,
302 }
303}
304
305#[derive(Debug, Clone, Copy)]
306enum DecimalCastPlan<T> {
307 SameScale { min: T, max: T },
308 ScaleUp { factor: T, min: T, max: T },
309 ScaleUpOverflow,
310 ScaleDown { factor: i256, min: i256, max: i256 },
311 ScaleDownOverflow,
312}
313
314impl<T> DecimalCastPlan<T>
315where
316 T: NativeDecimalType + CheckedMul,
317{
318 fn new(from_decimal_dtype: DecimalDType, to_decimal_dtype: DecimalDType) -> Self {
319 let scale_delta = to_decimal_dtype.scale() as i16 - from_decimal_dtype.scale() as i16;
320 if scale_delta == 0 {
321 let (min, max) = decimal_precision_range::<T>(to_decimal_dtype);
322 return Self::SameScale { min, max };
323 }
324
325 if scale_delta > 0 {
326 let Some(factor) = decimal_scale_factor::<T>(scale_delta as u32) else {
327 return Self::ScaleUpOverflow;
328 };
329 let (min, max) = decimal_precision_range::<T>(to_decimal_dtype);
330 return Self::ScaleUp { factor, min, max };
331 }
332
333 let Some(factor) = decimal_scale_factor::<i256>((-scale_delta) as u32) else {
334 return Self::ScaleDownOverflow;
335 };
336 let (min, max) = decimal_precision_range::<i256>(to_decimal_dtype);
337 Self::ScaleDown { factor, min, max }
338 }
339
340 #[inline]
341 fn cast<F>(&self, value: F) -> Option<T>
342 where
343 F: NativeDecimalType,
344 {
345 match *self {
346 DecimalCastPlan::SameScale { min, max } => {
347 let value = <T as BigCast>::from(value)?;
348 (value >= min && value <= max).then_some(value)
349 }
350 DecimalCastPlan::ScaleUp { factor, min, max } => {
351 let value = <T as BigCast>::from(value)?;
352 let value = value.checked_mul(&factor)?;
353 (value >= min && value <= max).then_some(value)
354 }
355 DecimalCastPlan::ScaleUpOverflow | DecimalCastPlan::ScaleDownOverflow => {
356 (value == F::default()).then_some(T::default())
357 }
358 DecimalCastPlan::ScaleDown { factor, min, max } => {
359 let value = <i256 as BigCast>::from(value)?;
360 if value == i256::ZERO {
361 return Some(T::default());
362 }
363 if value % factor != i256::ZERO {
364 return None;
365 }
366
367 let value = value / factor;
368 if value < min || value > max {
369 return None;
370 }
371 <T as BigCast>::from(value)
372 }
373 }
374 }
375}
376
377fn decimal_precision_range<T: NativeDecimalType>(decimal_dtype: DecimalDType) -> (T, T) {
378 let precision = usize::from(decimal_dtype.precision());
379 (
380 T::MIN_BY_PRECISION[precision],
381 T::MAX_BY_PRECISION[precision],
382 )
383}
384
385fn decimal_scale_factor<T>(exp: u32) -> Option<T>
386where
387 T: NativeDecimalType + CheckedMul,
388{
389 let ten = <T as BigCast>::from(10_i8)?;
390 let mut factor = <T as BigCast>::from(1_i8)?;
391 for _ in 0..exp {
392 factor = factor.checked_mul(&ten)?;
393 }
394 Some(factor)
395}
396
397pub fn upcast_decimal_values(
409 array: ArrayView<'_, Decimal>,
410 to_values_type: DecimalType,
411) -> VortexResult<DecimalArray> {
412 let from_values_type = array.values_type();
413
414 if from_values_type == to_values_type {
416 return Ok(array.array().as_::<Decimal>().into_owned());
417 }
418
419 if to_values_type < from_values_type {
421 vortex_bail!(
422 "Cannot downcast decimal values from {:?} to {:?}. Only upcasting is supported.",
423 from_values_type,
424 to_values_type
425 );
426 }
427
428 let decimal_dtype = array.decimal_dtype();
429 let validity = array.validity()?;
430
431 match_each_decimal_value_type!(from_values_type, |F| {
433 let from_buffer = array.buffer::<F>();
434 match_each_decimal_value_type!(to_values_type, |T| {
435 let to_buffer = upcast_decimal_buffer::<F, T>(from_buffer);
436 Ok(DecimalArray::new(to_buffer, decimal_dtype, validity))
437 })
438 })
439}
440
441fn upcast_decimal_buffer<F: NativeDecimalType, T: NativeDecimalType>(from: Buffer<F>) -> Buffer<T> {
444 from.iter()
445 .map(|&v| T::from(v).vortex_expect("upcast should never fail"))
446 .collect()
447}
448
449#[cfg(test)]
450mod tests {
451 use rstest::rstest;
452 use vortex_buffer::buffer;
453
454 use super::upcast_decimal_values;
455 use crate::Canonical;
456 use crate::IntoArray;
457 use crate::VortexSessionExecute;
458 use crate::array_session;
459 use crate::arrays::DecimalArray;
460 use crate::builtins::ArrayBuiltins;
461 use crate::compute::conformance::cast::test_cast_conformance;
462 use crate::dtype::DType;
463 use crate::dtype::DecimalDType;
464 use crate::dtype::DecimalType;
465 use crate::dtype::Nullability;
466 use crate::dtype::PType;
467 use crate::scalar::Scalar;
468 use crate::validity::Validity;
469
470 #[test]
471 fn cast_decimal_to_nullable() {
472 let mut ctx = array_session().create_execution_ctx();
473 let decimal_dtype = DecimalDType::new(10, 2);
474 let array = DecimalArray::new(
475 buffer![100i32, 200, 300],
476 decimal_dtype,
477 Validity::NonNullable,
478 );
479
480 let nullable_dtype = DType::Decimal(decimal_dtype, Nullability::Nullable);
482 let casted = array
483 .into_array()
484 .cast(nullable_dtype.clone())
485 .unwrap()
486 .execute::<DecimalArray>(&mut ctx)
487 .unwrap();
488
489 assert_eq!(casted.dtype(), &nullable_dtype);
490 assert!(matches!(casted.validity(), Ok(Validity::AllValid)));
491 assert_eq!(casted.len(), 3);
492 }
493
494 #[test]
495 fn cast_nullable_to_non_nullable() {
496 let mut ctx = array_session().create_execution_ctx();
497 let decimal_dtype = DecimalDType::new(10, 2);
498
499 let array = DecimalArray::new(buffer![100i32, 200, 300], decimal_dtype, Validity::AllValid);
501
502 let non_nullable_dtype = DType::Decimal(decimal_dtype, Nullability::NonNullable);
504 let casted = array
505 .into_array()
506 .cast(non_nullable_dtype.clone())
507 .unwrap()
508 .execute::<DecimalArray>(&mut ctx)
509 .unwrap();
510
511 assert_eq!(casted.dtype(), &non_nullable_dtype);
512 assert!(matches!(casted.validity(), Ok(Validity::NonNullable)));
513 }
514
515 #[test]
516 #[should_panic(expected = "Cannot cast array with invalid values to non-nullable type")]
517 fn cast_nullable_with_nulls_to_non_nullable_fails() {
518 let mut ctx = array_session().create_execution_ctx();
519 let decimal_dtype = DecimalDType::new(10, 2);
520
521 let array = DecimalArray::from_option_iter([Some(100i32), None, Some(300)], decimal_dtype);
523
524 let non_nullable_dtype = DType::Decimal(decimal_dtype, Nullability::NonNullable);
526 array
527 .into_array()
528 .cast(non_nullable_dtype)
529 .and_then(|a| a.execute::<Canonical>(&mut ctx).map(|c| c.into_array()))
530 .unwrap();
531 }
532
533 #[test]
534 fn cast_different_scale_rescales() {
535 let mut ctx = array_session().create_execution_ctx();
536 let array = DecimalArray::new(
537 buffer![100i32],
538 DecimalDType::new(10, 2),
539 Validity::NonNullable,
540 );
541
542 let different_dtype = DType::Decimal(DecimalDType::new(15, 3), Nullability::NonNullable);
544 let casted = array
545 .into_array()
546 .cast(different_dtype)
547 .unwrap()
548 .execute::<DecimalArray>(&mut ctx)
549 .unwrap();
550
551 assert_eq!(casted.precision(), 15);
552 assert_eq!(casted.scale(), 3);
553 assert_eq!(casted.values_type(), DecimalType::I64);
554 assert_eq!(casted.buffer::<i64>().as_ref(), &[1000]);
555 }
556
557 #[test]
558 fn cast_downcast_precision_succeeds_when_values_fit() {
559 let mut ctx = array_session().create_execution_ctx();
560 let array = DecimalArray::new(
561 buffer![100i64],
562 DecimalDType::new(18, 2),
563 Validity::NonNullable,
564 );
565
566 let smaller_dtype = DType::Decimal(DecimalDType::new(10, 2), Nullability::NonNullable);
568 let casted = array
569 .into_array()
570 .cast(smaller_dtype)
571 .unwrap()
572 .execute::<DecimalArray>(&mut ctx)
573 .unwrap();
574
575 assert_eq!(casted.precision(), 10);
576 assert_eq!(casted.scale(), 2);
577 assert_eq!(casted.buffer::<i64>().as_ref(), &[100]);
578 }
579
580 #[test]
581 fn cast_downcast_precision_checks_values() {
582 let mut ctx = array_session().create_execution_ctx();
583 let array = DecimalArray::new(
584 buffer![1000i64],
585 DecimalDType::new(18, 0),
586 Validity::NonNullable,
587 );
588
589 let smaller_dtype = DType::Decimal(DecimalDType::new(3, 0), Nullability::NonNullable);
590 let result = array
591 .into_array()
592 .cast(smaller_dtype)
593 .and_then(|a| a.execute::<Canonical>(&mut ctx).map(|c| c.into_array()));
594
595 assert!(result.is_err());
596 assert!(
597 result
598 .unwrap_err()
599 .to_string()
600 .contains("does not fit in precision")
601 );
602 }
603
604 #[test]
605 fn cast_lower_scale_requires_exact_rescale() {
606 let mut ctx = array_session().create_execution_ctx();
607 let array = DecimalArray::new(
608 buffer![123456i64],
609 DecimalDType::new(10, 4),
610 Validity::NonNullable,
611 );
612
613 let lower_scale_dtype = DType::Decimal(DecimalDType::new(10, 2), Nullability::NonNullable);
614 let result = array
615 .into_array()
616 .cast(lower_scale_dtype)
617 .and_then(|a| a.execute::<Canonical>(&mut ctx).map(|c| c.into_array()));
618
619 assert!(result.is_err());
620 assert!(
621 result
622 .unwrap_err()
623 .to_string()
624 .contains("would lose precision")
625 );
626 }
627
628 #[test]
629 fn cast_lower_scale_ignores_null_lane_failures() {
630 let mut ctx = array_session().create_execution_ctx();
631 let array = DecimalArray::new(
632 buffer![100i64, 123456],
633 DecimalDType::new(10, 4),
634 Validity::from_iter([true, false]),
635 );
636
637 let lower_scale_dtype = DType::Decimal(DecimalDType::new(3, 2), Nullability::Nullable);
638 let casted = array
639 .into_array()
640 .cast(lower_scale_dtype)
641 .unwrap()
642 .execute::<DecimalArray>(&mut ctx)
643 .unwrap();
644
645 let mask = casted
646 .as_ref()
647 .validity()
648 .unwrap()
649 .execute_mask(
650 casted.as_ref().len(),
651 &mut array_session().create_execution_ctx(),
652 )
653 .unwrap();
654 assert!(mask.value(0));
655 assert!(!mask.value(1));
656 assert_eq!(casted.buffer::<i16>().as_ref()[0], 1);
657 }
658
659 #[test]
660 fn cast_upcast_precision_succeeds() {
661 let mut ctx = array_session().create_execution_ctx();
662 let array = DecimalArray::new(
663 buffer![100i32, 200, 300],
664 DecimalDType::new(10, 2),
665 Validity::NonNullable,
666 );
667
668 let wider_dtype = DType::Decimal(DecimalDType::new(38, 2), Nullability::NonNullable);
670 let casted = array
671 .into_array()
672 .cast(wider_dtype)
673 .unwrap()
674 .execute::<DecimalArray>(&mut ctx)
675 .unwrap();
676
677 assert_eq!(casted.precision(), 38);
678 assert_eq!(casted.scale(), 2);
679 assert_eq!(casted.len(), 3);
680 assert_eq!(casted.values_type(), DecimalType::I128);
682 }
683
684 #[test]
685 fn cast_widening_same_physical_type_is_zero_copy() {
686 let mut ctx = array_session().create_execution_ctx();
687 let array = DecimalArray::new(
690 buffer![100i64, 200, 300],
691 DecimalDType::new(10, 2),
692 Validity::NonNullable,
693 );
694 let src_ptr = array.buffer::<i64>().as_ptr();
695
696 let wider_dtype = DType::Decimal(DecimalDType::new(18, 2), Nullability::NonNullable);
697 let casted = array
698 .into_array()
699 .cast(wider_dtype)
700 .unwrap()
701 .execute::<DecimalArray>(&mut ctx)
702 .unwrap();
703
704 assert_eq!(casted.precision(), 18);
705 assert_eq!(casted.scale(), 2);
706 assert_eq!(casted.values_type(), DecimalType::I64);
707 assert_eq!(casted.buffer::<i64>().as_ref(), &[100, 200, 300]);
708 assert_eq!(
710 casted.buffer::<i64>().as_ptr(),
711 src_ptr,
712 "precision-widening cast must reuse the source values buffer"
713 );
714 }
715
716 #[test]
717 fn cast_to_non_decimal_returns_err() {
718 let mut ctx = array_session().create_execution_ctx();
719 let array = DecimalArray::new(
720 buffer![100i32],
721 DecimalDType::new(10, 2),
722 Validity::NonNullable,
723 );
724
725 let result = array
727 .into_array()
728 .cast(DType::Utf8(Nullability::NonNullable))
729 .and_then(|a| a.execute::<Canonical>(&mut ctx).map(|c| c.into_array()));
730
731 assert!(result.is_err());
732 assert!(
733 result
734 .unwrap_err()
735 .to_string()
736 .contains("No CastKernel to cast canonical array")
737 );
738 }
739
740 #[rstest]
741 #[case(DecimalArray::new(buffer![100i32, 200, 300], DecimalDType::new(10, 2), Validity::NonNullable))]
742 #[case(DecimalArray::new(buffer![10000i64, 20000, 30000], DecimalDType::new(18, 4), Validity::NonNullable))]
743 #[case(DecimalArray::from_option_iter([Some(100i32), None, Some(300)], DecimalDType::new(10, 2)))]
744 #[case(DecimalArray::new(buffer![42i32], DecimalDType::new(5, 1), Validity::NonNullable))]
745 fn test_cast_decimal_conformance(#[case] array: DecimalArray) {
746 test_cast_conformance(
747 &array.into_array(),
748 &mut array_session().create_execution_ctx(),
749 );
750 }
751
752 #[test]
753 fn upcast_decimal_values_i32_to_i64() {
754 let decimal_dtype = DecimalDType::new(10, 2);
755 let array = DecimalArray::new(
756 buffer![100i32, 200, 300],
757 decimal_dtype,
758 Validity::NonNullable,
759 );
760
761 assert_eq!(array.values_type(), DecimalType::I32);
762
763 let array = array.as_view();
764 let casted = upcast_decimal_values(array, DecimalType::I64).unwrap();
765
766 assert_eq!(casted.values_type(), DecimalType::I64);
767 assert_eq!(casted.decimal_dtype(), decimal_dtype);
768 assert_eq!(casted.len(), 3);
769
770 let buffer = casted.buffer::<i64>();
772 assert_eq!(buffer.as_ref(), &[100i64, 200, 300]);
773 }
774
775 #[test]
776 fn upcast_decimal_values_i64_to_i128() {
777 let decimal_dtype = DecimalDType::new(18, 4);
778 let array = DecimalArray::new(
779 buffer![10000i64, 20000, 30000],
780 decimal_dtype,
781 Validity::NonNullable,
782 );
783
784 let array = array.as_view();
785 let casted = upcast_decimal_values(array, DecimalType::I128).unwrap();
786
787 assert_eq!(casted.values_type(), DecimalType::I128);
788 assert_eq!(casted.decimal_dtype(), decimal_dtype);
789
790 let buffer = casted.buffer::<i128>();
791 assert_eq!(buffer.as_ref(), &[10000i128, 20000, 30000]);
792 }
793
794 #[test]
795 fn upcast_decimal_values_same_type_returns_clone() {
796 let decimal_dtype = DecimalDType::new(10, 2);
797 let array = DecimalArray::new(
798 buffer![100i32, 200, 300],
799 decimal_dtype,
800 Validity::NonNullable,
801 );
802
803 let array = array.as_view();
804 let casted = upcast_decimal_values(array, DecimalType::I32).unwrap();
805
806 assert_eq!(casted.values_type(), DecimalType::I32);
807 assert_eq!(casted.decimal_dtype(), decimal_dtype);
808 }
809
810 #[test]
811 fn upcast_decimal_values_with_nulls() {
812 let decimal_dtype = DecimalDType::new(10, 2);
813 let array = DecimalArray::from_option_iter([Some(100i32), None, Some(300)], decimal_dtype);
814
815 let array = array.as_view();
816 let casted = upcast_decimal_values(array, DecimalType::I64).unwrap();
817
818 assert_eq!(casted.values_type(), DecimalType::I64);
819 assert_eq!(casted.len(), 3);
820
821 let mask = casted
823 .as_ref()
824 .validity()
825 .unwrap()
826 .execute_mask(
827 casted.as_ref().len(),
828 &mut array_session().create_execution_ctx(),
829 )
830 .unwrap();
831 assert!(mask.value(0));
832 assert!(!mask.value(1));
833 assert!(mask.value(2));
834
835 let buffer = casted.buffer::<i64>();
837 assert_eq!(buffer[0], 100);
838 assert_eq!(buffer[2], 300);
839 }
840
841 #[test]
842 fn upcast_decimal_values_downcast_fails() {
843 let decimal_dtype = DecimalDType::new(18, 4);
844 let array = DecimalArray::new(
845 buffer![10000i64, 20000, 30000],
846 decimal_dtype,
847 Validity::NonNullable,
848 );
849
850 let array = array.as_view();
852 let result = upcast_decimal_values(array, DecimalType::I32);
853 assert!(result.is_err());
854 assert!(
855 result
856 .unwrap_err()
857 .to_string()
858 .contains("Cannot downcast decimal values")
859 );
860 }
861
862 #[test]
863 fn cast_decimal_f64() {
864 let dtype = DecimalDType::new(6, 2);
865 let array = DecimalArray::new(buffer![125i32, 250, -375], dtype, Validity::NonNullable);
866 let target = DType::Primitive(PType::F64, Nullability::NonNullable);
867 let casted = array.into_array().cast(target.clone()).unwrap();
868 assert_eq!(casted.dtype(), &target);
869
870 let mut ctx = array_session().create_execution_ctx();
871 let primitive = casted
872 .execute::<Canonical>(&mut ctx)
873 .unwrap()
874 .into_primitive();
875 assert_eq!(primitive.to_buffer::<f64>().as_ref(), &[1.25, 2.5, -3.75]);
876 }
877
878 #[test]
879 fn cast_decimal_f64_null() {
880 let values = [Some(100i32), None, Some(-200)];
881 let array = DecimalArray::from_option_iter(values, DecimalDType::new(6, 2));
882 let target = DType::Primitive(PType::F64, Nullability::Nullable);
883 let casted = array.into_array().cast(target.clone()).unwrap();
884 assert_eq!(casted.dtype(), &target);
885
886 let mut ctx = array_session().create_execution_ctx();
887 let primitive = casted
888 .execute::<Canonical>(&mut ctx)
889 .unwrap()
890 .into_primitive();
891
892 assert!(primitive.is_valid(0, &mut ctx).unwrap());
893 assert!(!primitive.is_valid(1, &mut ctx).unwrap());
894 assert!(primitive.is_valid(2, &mut ctx).unwrap());
895
896 assert_eq!(
897 primitive.execute_scalar(0, &mut ctx).unwrap(),
898 Scalar::from(1f64)
899 );
900 assert_eq!(
901 primitive.execute_scalar(2, &mut ctx).unwrap(),
902 Scalar::from(-2f64)
903 );
904 }
905
906 #[test]
907 fn cast_decimal_f64_all_null() {
908 let dtype = DecimalDType::new(6, 2);
909 let buf = buffer![i32::MAX, i32::MIN, 12345];
910 let array = DecimalArray::new(buf, dtype, Validity::AllInvalid);
911 let target = DType::Primitive(PType::F64, Nullability::Nullable);
912 let casted = array.into_array().cast(target.clone()).unwrap();
913 assert_eq!(casted.dtype(), &target);
914
915 let primitive = casted
916 .execute::<Canonical>(&mut array_session().create_execution_ctx())
917 .unwrap()
918 .into_primitive();
919 let mut ctx = array_session().create_execution_ctx();
920 for i in 0..2 {
921 assert!(!primitive.is_valid(i, &mut ctx).unwrap());
922 }
923 assert_eq!(primitive.to_buffer::<f64>().as_ref(), &[0.0, 0.0, 0.0]);
924 }
925}