Skip to content

Commit 2ed0a6f

Browse files
authored
fix: Respect precision and scale for Decimal128 in value.rs (#1921)
1 parent bdb44ae commit 2ed0a6f

File tree

1 file changed

+119
-16
lines changed

1 file changed

+119
-16
lines changed

crates/iceberg/src/arrow/value.rs

Lines changed: 119 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -663,18 +663,44 @@ pub(crate) fn create_primitive_array_single_element(
663663
(DataType::Binary, None) => Ok(Arc::new(BinaryArray::from_opt_vec(vec![
664664
Option::<&[u8]>::None,
665665
]))),
666-
(DataType::Decimal128(_, _), Some(PrimitiveLiteral::Int128(v))) => {
667-
Ok(Arc::new(arrow_array::Decimal128Array::from(vec![{ *v }])))
666+
(DataType::Decimal128(precision, scale), Some(PrimitiveLiteral::Int128(v))) => {
667+
let array = Decimal128Array::from(vec![{ *v }])
668+
.with_precision_and_scale(*precision, *scale)
669+
.map_err(|e| {
670+
Error::new(
671+
ErrorKind::DataInvalid,
672+
format!(
673+
"Failed to create Decimal128Array with precision {precision} and scale {scale}: {e}"
674+
),
675+
)
676+
})?;
677+
Ok(Arc::new(array))
668678
}
669-
(DataType::Decimal128(_, _), Some(PrimitiveLiteral::UInt128(v))) => {
670-
Ok(Arc::new(arrow_array::Decimal128Array::from(vec![
671-
*v as i128,
672-
])))
679+
(DataType::Decimal128(precision, scale), Some(PrimitiveLiteral::UInt128(v))) => {
680+
let array = Decimal128Array::from(vec![*v as i128])
681+
.with_precision_and_scale(*precision, *scale)
682+
.map_err(|e| {
683+
Error::new(
684+
ErrorKind::DataInvalid,
685+
format!(
686+
"Failed to create Decimal128Array with precision {precision} and scale {scale}: {e}"
687+
),
688+
)
689+
})?;
690+
Ok(Arc::new(array))
673691
}
674-
(DataType::Decimal128(_, _), None) => {
675-
Ok(Arc::new(arrow_array::Decimal128Array::from(vec![
676-
Option::<i128>::None,
677-
])))
692+
(DataType::Decimal128(precision, scale), None) => {
693+
let array = Decimal128Array::from(vec![Option::<i128>::None])
694+
.with_precision_and_scale(*precision, *scale)
695+
.map_err(|e| {
696+
Error::new(
697+
ErrorKind::DataInvalid,
698+
format!(
699+
"Failed to create Decimal128Array with precision {precision} and scale {scale}: {e}"
700+
),
701+
)
702+
})?;
703+
Ok(Arc::new(array))
678704
}
679705
(DataType::Struct(fields), None) => {
680706
// Create a single-element StructArray with nulls
@@ -795,15 +821,48 @@ pub(crate) fn create_primitive_array_repeated(
795821
let vals: Vec<Option<&[u8]>> = vec![None; num_rows];
796822
Arc::new(BinaryArray::from_opt_vec(vals))
797823
}
798-
(DataType::Decimal128(_, _), Some(PrimitiveLiteral::Int128(value))) => {
799-
Arc::new(Decimal128Array::from(vec![*value; num_rows]))
824+
(DataType::Decimal128(precision, scale), Some(PrimitiveLiteral::Int128(value))) => {
825+
Arc::new(
826+
Decimal128Array::from(vec![*value; num_rows])
827+
.with_precision_and_scale(*precision, *scale)
828+
.map_err(|e| {
829+
Error::new(
830+
ErrorKind::DataInvalid,
831+
format!(
832+
"Failed to create Decimal128Array with precision {precision} and scale {scale}: {e}"
833+
),
834+
)
835+
})?,
836+
)
800837
}
801-
(DataType::Decimal128(_, _), Some(PrimitiveLiteral::UInt128(value))) => {
802-
Arc::new(Decimal128Array::from(vec![*value as i128; num_rows]))
838+
(DataType::Decimal128(precision, scale), Some(PrimitiveLiteral::UInt128(value))) => {
839+
Arc::new(
840+
Decimal128Array::from(vec![*value as i128; num_rows])
841+
.with_precision_and_scale(*precision, *scale)
842+
.map_err(|e| {
843+
Error::new(
844+
ErrorKind::DataInvalid,
845+
format!(
846+
"Failed to create Decimal128Array with precision {precision} and scale {scale}: {e}"
847+
),
848+
)
849+
})?,
850+
)
803851
}
804-
(DataType::Decimal128(_, _), None) => {
852+
(DataType::Decimal128(precision, scale), None) => {
805853
let vals: Vec<Option<i128>> = vec![None; num_rows];
806-
Arc::new(Decimal128Array::from(vals))
854+
Arc::new(
855+
Decimal128Array::from(vals)
856+
.with_precision_and_scale(*precision, *scale)
857+
.map_err(|e| {
858+
Error::new(
859+
ErrorKind::DataInvalid,
860+
format!(
861+
"Failed to create Decimal128Array with precision {precision} and scale {scale}: {e}"
862+
),
863+
)
864+
})?,
865+
)
807866
}
808867
(DataType::Struct(fields), None) => {
809868
// Create a StructArray filled with nulls
@@ -1678,4 +1737,48 @@ mod test {
16781737
]))),
16791738
]);
16801739
}
1740+
1741+
#[test]
1742+
fn test_create_decimal_array_respects_precision() {
1743+
// Decimal128Array::from() uses Arrow's default precision (38) instead of the
1744+
// target precision, causing RecordBatch construction to fail when schemas don't match.
1745+
let target_precision = 18u8;
1746+
let target_scale = 10i8;
1747+
let target_type = DataType::Decimal128(target_precision, target_scale);
1748+
let value = PrimitiveLiteral::Int128(10000000000);
1749+
1750+
let array = create_primitive_array_single_element(&target_type, &Some(value))
1751+
.expect("Failed to create decimal array");
1752+
1753+
match array.data_type() {
1754+
DataType::Decimal128(precision, scale) => {
1755+
assert_eq!(*precision, target_precision);
1756+
assert_eq!(*scale, target_scale);
1757+
}
1758+
other => panic!("Expected Decimal128, got {other:?}"),
1759+
}
1760+
}
1761+
1762+
#[test]
1763+
fn test_create_decimal_array_repeated_respects_precision() {
1764+
// Ensure repeated arrays also respect target precision, not Arrow's default.
1765+
let target_precision = 18u8;
1766+
let target_scale = 10i8;
1767+
let target_type = DataType::Decimal128(target_precision, target_scale);
1768+
let value = PrimitiveLiteral::Int128(10000000000);
1769+
let num_rows = 5;
1770+
1771+
let array = create_primitive_array_repeated(&target_type, &Some(value), num_rows)
1772+
.expect("Failed to create repeated decimal array");
1773+
1774+
match array.data_type() {
1775+
DataType::Decimal128(precision, scale) => {
1776+
assert_eq!(*precision, target_precision);
1777+
assert_eq!(*scale, target_scale);
1778+
}
1779+
other => panic!("Expected Decimal128, got {other:?}"),
1780+
}
1781+
1782+
assert_eq!(array.len(), num_rows);
1783+
}
16811784
}

0 commit comments

Comments
 (0)