1use arrow_schema::ArrowError;
18use std::fmt;
19
20pub trait VariantDecimalType: Into<super::Variant<'static, 'static>> {
42 type Native;
44
45 const MAX_PRECISION: u8;
47 const MAX_UNSCALED_VALUE: Self::Native;
49
50 fn is_valid_precision_and_scale(precision: &u8, scale: &i8) -> bool {
69 (1..=Self::MAX_PRECISION).contains(precision) && (0..=*precision as i8).contains(scale)
70 }
71
72 fn try_new(integer: Self::Native, scale: u8) -> Result<Self, ArrowError>;
92
93 fn try_new_with_signed_scale(integer: Self::Native, scale: i8) -> Result<Self, ArrowError>;
101
102 fn integer(&self) -> Self::Native;
104
105 fn scale(&self) -> u8;
107}
108
109macro_rules! impl_variant_decimal {
111 ($struct_name:ident, $native:ty) => {
112 impl $struct_name {
113 pub fn try_new(integer: $native, scale: u8) -> Result<Self, ArrowError> {
116 let max_precision = Self::MAX_PRECISION;
117 if scale > max_precision {
118 return Err(ArrowError::InvalidArgumentError(format!(
119 "Scale {scale} is larger than max precision {max_precision}",
120 )));
121 }
122 if !(-Self::MAX_UNSCALED_VALUE..=Self::MAX_UNSCALED_VALUE).contains(&integer) {
123 return Err(ArrowError::InvalidArgumentError(format!(
124 "{integer} is wider than max precision {max_precision}",
125 )));
126 }
127
128 Ok(Self { integer, scale })
129 }
130
131 pub fn integer(&self) -> $native {
135 self.integer
136 }
137
138 pub fn scale(&self) -> u8 {
142 self.scale
143 }
144 }
145
146 impl VariantDecimalType for $struct_name {
147 type Native = $native;
148 const MAX_PRECISION: u8 = Self::MAX_PRECISION;
149 const MAX_UNSCALED_VALUE: $native = <$native>::pow(10, Self::MAX_PRECISION as u32) - 1;
150
151 fn try_new(integer: $native, scale: u8) -> Result<Self, ArrowError> {
152 Self::try_new(integer, scale)
153 }
154
155 fn try_new_with_signed_scale(integer: $native, scale: i8) -> Result<Self, ArrowError> {
156 let (integer, scale) = if scale < 0 {
157 let multiplier = <$native>::checked_pow(10, -scale as u32);
158 let Some(rescaled) = multiplier.and_then(|m| integer.checked_mul(m)) else {
159 return Err(ArrowError::InvalidArgumentError(format!(
160 "Overflow when rescaling {integer} with scale {scale}"
161 )));
162 };
163 (rescaled, 0u8)
164 } else {
165 (integer, scale as u8)
166 };
167 Self::try_new(integer, scale)
168 }
169
170 fn integer(&self) -> $native {
171 self.integer()
172 }
173
174 fn scale(&self) -> u8 {
175 self.scale()
176 }
177 }
178
179 impl fmt::Display for $struct_name {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 let integer = if self.scale == 0 {
182 self.integer
183 } else {
184 let divisor = <$native>::pow(10, self.scale as u32);
185 let remainder = self.integer % divisor;
186 if remainder != 0 {
187 let sign = if self.integer < 0 { "-" } else { "" };
189 let remainder =
191 format!("{:0width$}", remainder.abs(), width = self.scale as usize);
192 let remainder = remainder.trim_end_matches('0');
193 let quotient = (self.integer / divisor).abs();
194 return write!(f, "{sign}{quotient}.{remainder}");
195 }
196 self.integer / divisor
197 };
198 write!(f, "{integer}")
199 }
200 }
201 };
202}
203
204#[derive(Debug, Clone, Copy, PartialEq)]
219pub struct VariantDecimal4 {
220 integer: i32,
221 scale: u8,
222}
223
224impl VariantDecimal4 {
225 pub const MAX_PRECISION: u8 = arrow_schema::DECIMAL32_MAX_PRECISION;
227}
228
229impl_variant_decimal!(VariantDecimal4, i32);
230
231#[derive(Debug, Clone, Copy, PartialEq)]
247pub struct VariantDecimal8 {
248 integer: i64,
249 scale: u8,
250}
251
252impl VariantDecimal8 {
253 pub const MAX_PRECISION: u8 = arrow_schema::DECIMAL64_MAX_PRECISION;
255}
256
257impl_variant_decimal!(VariantDecimal8, i64);
258
259#[derive(Debug, Clone, Copy, PartialEq)]
275pub struct VariantDecimal16 {
276 integer: i128,
277 scale: u8,
278}
279
280impl VariantDecimal16 {
281 pub const MAX_PRECISION: u8 = arrow_schema::DECIMAL128_MAX_PRECISION;
283}
284
285impl_variant_decimal!(VariantDecimal16, i128);
286
287macro_rules! impl_from_decimal_for_decimal {
289 ($from_ty:ty, $for_ty:ty) => {
290 impl From<$from_ty> for $for_ty {
291 fn from(decimal: $from_ty) -> Self {
292 Self {
293 integer: decimal.integer.into(),
294 scale: decimal.scale,
295 }
296 }
297 }
298 };
299}
300
301impl_from_decimal_for_decimal!(VariantDecimal4, VariantDecimal8);
302impl_from_decimal_for_decimal!(VariantDecimal4, VariantDecimal16);
303impl_from_decimal_for_decimal!(VariantDecimal8, VariantDecimal16);
304
305macro_rules! impl_try_from_decimal_for_decimal {
307 ($from_ty:ty, $for_ty:ty) => {
308 impl TryFrom<$from_ty> for $for_ty {
309 type Error = ArrowError;
310
311 fn try_from(decimal: $from_ty) -> Result<Self, ArrowError> {
312 let Ok(integer) = decimal.integer.try_into() else {
313 return Err(ArrowError::InvalidArgumentError(format!(
314 "Value {} is wider than max precision {}",
315 decimal.integer,
316 Self::MAX_PRECISION
317 )));
318 };
319 Self::try_new(integer, decimal.scale)
320 }
321 }
322 };
323}
324
325impl_try_from_decimal_for_decimal!(VariantDecimal8, VariantDecimal4);
326impl_try_from_decimal_for_decimal!(VariantDecimal16, VariantDecimal4);
327impl_try_from_decimal_for_decimal!(VariantDecimal16, VariantDecimal8);
328
329macro_rules! impl_try_from_int_for_decimal {
331 ($from_ty:ty, $for_ty:ty) => {
332 impl TryFrom<$from_ty> for $for_ty {
333 type Error = ArrowError;
334
335 fn try_from(integer: $from_ty) -> Result<Self, ArrowError> {
336 Self::try_new(integer, 0)
337 }
338 }
339 };
340}
341
342impl_try_from_int_for_decimal!(i32, VariantDecimal4);
343impl_try_from_int_for_decimal!(i64, VariantDecimal8);
344impl_try_from_int_for_decimal!(i128, VariantDecimal16);
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[test]
351 fn test_variant_decimal_invalid_precision() {
352 let decimal4_too_large = VariantDecimal4::try_new(1_000_000_000_i32, 2);
354 assert!(
355 decimal4_too_large.is_err(),
356 "Decimal4 precision overflow should fail"
357 );
358 assert!(
359 decimal4_too_large
360 .unwrap_err()
361 .to_string()
362 .contains("wider than max precision")
363 );
364
365 let decimal4_too_small = VariantDecimal4::try_new(-1_000_000_000_i32, 2);
366 assert!(
367 decimal4_too_small.is_err(),
368 "Decimal4 precision underflow should fail"
369 );
370 assert!(
371 decimal4_too_small
372 .unwrap_err()
373 .to_string()
374 .contains("wider than max precision")
375 );
376
377 let decimal4_max_valid = VariantDecimal4::try_new(999_999_999_i32, 2);
379 assert!(
380 decimal4_max_valid.is_ok(),
381 "Decimal4 max valid value should succeed"
382 );
383
384 let decimal4_min_valid = VariantDecimal4::try_new(-999_999_999_i32, 2);
385 assert!(
386 decimal4_min_valid.is_ok(),
387 "Decimal4 min valid value should succeed"
388 );
389
390 let decimal8_too_large = VariantDecimal8::try_new(1_000_000_000_000_000_000_i64, 2);
392 assert!(
393 decimal8_too_large.is_err(),
394 "Decimal8 precision overflow should fail"
395 );
396 assert!(
397 decimal8_too_large
398 .unwrap_err()
399 .to_string()
400 .contains("wider than max precision")
401 );
402
403 let decimal8_too_small = VariantDecimal8::try_new(-1_000_000_000_000_000_000_i64, 2);
404 assert!(
405 decimal8_too_small.is_err(),
406 "Decimal8 precision underflow should fail"
407 );
408 assert!(
409 decimal8_too_small
410 .unwrap_err()
411 .to_string()
412 .contains("wider than max precision")
413 );
414
415 let decimal8_max_valid = VariantDecimal8::try_new(999_999_999_999_999_999_i64, 2);
417 assert!(
418 decimal8_max_valid.is_ok(),
419 "Decimal8 max valid value should succeed"
420 );
421
422 let decimal8_min_valid = VariantDecimal8::try_new(-999_999_999_999_999_999_i64, 2);
423 assert!(
424 decimal8_min_valid.is_ok(),
425 "Decimal8 min valid value should succeed"
426 );
427
428 let decimal16_too_large =
430 VariantDecimal16::try_new(100000000000000000000000000000000000000_i128, 2);
431 assert!(
432 decimal16_too_large.is_err(),
433 "Decimal16 precision overflow should fail"
434 );
435 assert!(
436 decimal16_too_large
437 .unwrap_err()
438 .to_string()
439 .contains("wider than max precision")
440 );
441
442 let decimal16_too_small =
443 VariantDecimal16::try_new(-100000000000000000000000000000000000000_i128, 2);
444 assert!(
445 decimal16_too_small.is_err(),
446 "Decimal16 precision underflow should fail"
447 );
448 assert!(
449 decimal16_too_small
450 .unwrap_err()
451 .to_string()
452 .contains("wider than max precision")
453 );
454
455 let decimal16_max_valid =
457 VariantDecimal16::try_new(99999999999999999999999999999999999999_i128, 2);
458 assert!(
459 decimal16_max_valid.is_ok(),
460 "Decimal16 max valid value should succeed"
461 );
462
463 let decimal16_min_valid =
464 VariantDecimal16::try_new(-99999999999999999999999999999999999999_i128, 2);
465 assert!(
466 decimal16_min_valid.is_ok(),
467 "Decimal16 min valid value should succeed"
468 );
469 }
470
471 #[test]
472 fn test_variant_decimal_invalid_scale() {
473 let decimal4_invalid_scale = VariantDecimal4::try_new(123_i32, 10);
475 assert!(
476 decimal4_invalid_scale.is_err(),
477 "Decimal4 with scale > 9 should fail"
478 );
479 assert!(
480 decimal4_invalid_scale
481 .unwrap_err()
482 .to_string()
483 .contains("larger than max precision")
484 );
485
486 let decimal4_invalid_scale_large = VariantDecimal4::try_new(123_i32, 20);
487 assert!(
488 decimal4_invalid_scale_large.is_err(),
489 "Decimal4 with scale > 9 should fail"
490 );
491
492 let decimal4_valid_scale = VariantDecimal4::try_new(123_i32, 9);
494 assert!(
495 decimal4_valid_scale.is_ok(),
496 "Decimal4 with scale = 9 should succeed"
497 );
498
499 let decimal8_invalid_scale = VariantDecimal8::try_new(123_i64, 19);
501 assert!(
502 decimal8_invalid_scale.is_err(),
503 "Decimal8 with scale > 18 should fail"
504 );
505 assert!(
506 decimal8_invalid_scale
507 .unwrap_err()
508 .to_string()
509 .contains("larger than max precision")
510 );
511
512 let decimal8_invalid_scale_large = VariantDecimal8::try_new(123_i64, 25);
513 assert!(
514 decimal8_invalid_scale_large.is_err(),
515 "Decimal8 with scale > 18 should fail"
516 );
517
518 let decimal8_valid_scale = VariantDecimal8::try_new(123_i64, 18);
520 assert!(
521 decimal8_valid_scale.is_ok(),
522 "Decimal8 with scale = 18 should succeed"
523 );
524
525 let decimal16_invalid_scale = VariantDecimal16::try_new(123_i128, 39);
527 assert!(
528 decimal16_invalid_scale.is_err(),
529 "Decimal16 with scale > 38 should fail"
530 );
531 assert!(
532 decimal16_invalid_scale
533 .unwrap_err()
534 .to_string()
535 .contains("larger than max precision")
536 );
537
538 let decimal16_invalid_scale_large = VariantDecimal16::try_new(123_i128, 50);
539 assert!(
540 decimal16_invalid_scale_large.is_err(),
541 "Decimal16 with scale > 38 should fail"
542 );
543
544 let decimal16_valid_scale = VariantDecimal16::try_new(123_i128, 38);
546 assert!(
547 decimal16_valid_scale.is_ok(),
548 "Decimal16 with scale = 38 should succeed"
549 );
550 }
551
552 #[test]
553 fn test_variant_decimal4_display() {
554 let d = VariantDecimal4::try_new(42, 0).unwrap();
556 assert_eq!(d.to_string(), "42");
557
558 let d = VariantDecimal4::try_new(-42, 0).unwrap();
559 assert_eq!(d.to_string(), "-42");
560
561 let d = VariantDecimal4::try_new(12345, 2).unwrap();
563 assert_eq!(d.to_string(), "123.45");
564
565 let d = VariantDecimal4::try_new(-12345, 2).unwrap();
566 assert_eq!(d.to_string(), "-123.45");
567
568 let d = VariantDecimal4::try_new(12300, 2).unwrap();
570 assert_eq!(d.to_string(), "123");
571
572 let d = VariantDecimal4::try_new(-12300, 2).unwrap();
573 assert_eq!(d.to_string(), "-123");
574
575 let d = VariantDecimal4::try_new(1005, 3).unwrap();
577 assert_eq!(d.to_string(), "1.005");
578
579 let d = VariantDecimal4::try_new(-1005, 3).unwrap();
580 assert_eq!(d.to_string(), "-1.005");
581
582 let d = VariantDecimal4::try_new(123, 4).unwrap();
584 assert_eq!(d.to_string(), "0.0123");
585
586 let d = VariantDecimal4::try_new(-123, 4).unwrap();
587 assert_eq!(d.to_string(), "-0.0123");
588
589 let d = VariantDecimal4::try_new(0, 0).unwrap();
591 assert_eq!(d.to_string(), "0");
592
593 let d = VariantDecimal4::try_new(0, 3).unwrap();
594 assert_eq!(d.to_string(), "0");
595
596 let d = VariantDecimal4::try_new(123456789, 9).unwrap();
598 assert_eq!(d.to_string(), "0.123456789");
599
600 let d = VariantDecimal4::try_new(-123456789, 9).unwrap();
601 assert_eq!(d.to_string(), "-0.123456789");
602
603 let d = VariantDecimal4::try_new(999999999, 0).unwrap();
605 assert_eq!(d.to_string(), "999999999");
606
607 let d = VariantDecimal4::try_new(-999999999, 0).unwrap();
608 assert_eq!(d.to_string(), "-999999999");
609
610 let d = VariantDecimal4::try_new(120050, 4).unwrap();
612 assert_eq!(d.to_string(), "12.005");
613
614 let d = VariantDecimal4::try_new(-120050, 4).unwrap();
615 assert_eq!(d.to_string(), "-12.005");
616 }
617
618 #[test]
619 fn test_variant_decimal8_display() {
620 let d = VariantDecimal8::try_new(42, 0).unwrap();
622 assert_eq!(d.to_string(), "42");
623
624 let d = VariantDecimal8::try_new(-42, 0).unwrap();
625 assert_eq!(d.to_string(), "-42");
626
627 let d = VariantDecimal8::try_new(1234567890, 3).unwrap();
629 assert_eq!(d.to_string(), "1234567.89");
630
631 let d = VariantDecimal8::try_new(-1234567890, 3).unwrap();
632 assert_eq!(d.to_string(), "-1234567.89");
633
634 let d = VariantDecimal8::try_new(123000000, 6).unwrap();
636 assert_eq!(d.to_string(), "123");
637
638 let d = VariantDecimal8::try_new(-123000000, 6).unwrap();
639 assert_eq!(d.to_string(), "-123");
640
641 let d = VariantDecimal8::try_new(100005, 6).unwrap();
643 assert_eq!(d.to_string(), "0.100005");
644
645 let d = VariantDecimal8::try_new(-100005, 6).unwrap();
646 assert_eq!(d.to_string(), "-0.100005");
647
648 let d = VariantDecimal8::try_new(123, 10).unwrap();
650 assert_eq!(d.to_string(), "0.0000000123");
651
652 let d = VariantDecimal8::try_new(-123, 10).unwrap();
653 assert_eq!(d.to_string(), "-0.0000000123");
654
655 let d = VariantDecimal8::try_new(0, 0).unwrap();
657 assert_eq!(d.to_string(), "0");
658
659 let d = VariantDecimal8::try_new(0, 10).unwrap();
660 assert_eq!(d.to_string(), "0");
661
662 let d = VariantDecimal8::try_new(123456789012345678, 18).unwrap();
664 assert_eq!(d.to_string(), "0.123456789012345678");
665
666 let d = VariantDecimal8::try_new(-123456789012345678, 18).unwrap();
667 assert_eq!(d.to_string(), "-0.123456789012345678");
668
669 let d = VariantDecimal8::try_new(999999999999999999, 0).unwrap();
671 assert_eq!(d.to_string(), "999999999999999999");
672
673 let d = VariantDecimal8::try_new(-999999999999999999, 0).unwrap();
674 assert_eq!(d.to_string(), "-999999999999999999");
675
676 let d = VariantDecimal8::try_new(1200000050000, 10).unwrap();
678 assert_eq!(d.to_string(), "120.000005");
679
680 let d = VariantDecimal8::try_new(-1200000050000, 10).unwrap();
681 assert_eq!(d.to_string(), "-120.000005");
682 }
683
684 #[test]
685 fn test_variant_decimal16_display() {
686 let d = VariantDecimal16::try_new(42, 0).unwrap();
688 assert_eq!(d.to_string(), "42");
689
690 let d = VariantDecimal16::try_new(-42, 0).unwrap();
691 assert_eq!(d.to_string(), "-42");
692
693 let d = VariantDecimal16::try_new(123456789012345, 4).unwrap();
695 assert_eq!(d.to_string(), "12345678901.2345");
696
697 let d = VariantDecimal16::try_new(-123456789012345, 4).unwrap();
698 assert_eq!(d.to_string(), "-12345678901.2345");
699
700 let d = VariantDecimal16::try_new(12300000000, 8).unwrap();
702 assert_eq!(d.to_string(), "123");
703
704 let d = VariantDecimal16::try_new(-12300000000, 8).unwrap();
705 assert_eq!(d.to_string(), "-123");
706
707 let d = VariantDecimal16::try_new(10000005, 8).unwrap();
709 assert_eq!(d.to_string(), "0.10000005");
710
711 let d = VariantDecimal16::try_new(-10000005, 8).unwrap();
712 assert_eq!(d.to_string(), "-0.10000005");
713
714 let d = VariantDecimal16::try_new(123, 20).unwrap();
716 assert_eq!(d.to_string(), "0.00000000000000000123");
717
718 let d = VariantDecimal16::try_new(-123, 20).unwrap();
719 assert_eq!(d.to_string(), "-0.00000000000000000123");
720
721 let d = VariantDecimal16::try_new(0, 0).unwrap();
723 assert_eq!(d.to_string(), "0");
724
725 let d = VariantDecimal16::try_new(0, 20).unwrap();
726 assert_eq!(d.to_string(), "0");
727
728 let d = VariantDecimal16::try_new(12345678901234567890123456789012345678_i128, 38).unwrap();
730 assert_eq!(d.to_string(), "0.12345678901234567890123456789012345678");
731
732 let d =
733 VariantDecimal16::try_new(-12345678901234567890123456789012345678_i128, 38).unwrap();
734 assert_eq!(d.to_string(), "-0.12345678901234567890123456789012345678");
735
736 let d = VariantDecimal16::try_new(99999999999999999999999999999999999999_i128, 0).unwrap();
738 assert_eq!(d.to_string(), "99999999999999999999999999999999999999");
739
740 let d = VariantDecimal16::try_new(-99999999999999999999999999999999999999_i128, 0).unwrap();
741 assert_eq!(d.to_string(), "-99999999999999999999999999999999999999");
742
743 let d = VariantDecimal16::try_new(12000000000000050000000000000_i128, 25).unwrap();
745 assert_eq!(d.to_string(), "1200.000000000005");
746
747 let d = VariantDecimal16::try_new(-12000000000000050000000000000_i128, 25).unwrap();
748 assert_eq!(d.to_string(), "-1200.000000000005");
749
750 let large_int = 12345678901234567890123456789_i128;
752 let d = VariantDecimal16::try_new(large_int, 0).unwrap();
753 assert_eq!(d.to_string(), "12345678901234567890123456789");
754
755 let d = VariantDecimal16::try_new(-large_int, 0).unwrap();
756 assert_eq!(d.to_string(), "-12345678901234567890123456789");
757 }
758}