This is an automated email from the ASF dual-hosted git repository.
xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-rust.git
The following commit(s) were added to refs/heads/main by this push:
new 2ed0a6f50 fix: Respect precision and scale for Decimal128 in value.rs
(#1921)
2ed0a6f50 is described below
commit 2ed0a6f509a5c6e7bdb143a74fa79c59a9df1bf2
Author: Matt Butrovich <[email protected]>
AuthorDate: Wed Dec 10 13:54:49 2025 -0500
fix: Respect precision and scale for Decimal128 in value.rs (#1921)
---
crates/iceberg/src/arrow/value.rs | 135 +++++++++++++++++++++++++++++++++-----
1 file changed, 119 insertions(+), 16 deletions(-)
diff --git a/crates/iceberg/src/arrow/value.rs
b/crates/iceberg/src/arrow/value.rs
index bc123d99e..190aba08e 100644
--- a/crates/iceberg/src/arrow/value.rs
+++ b/crates/iceberg/src/arrow/value.rs
@@ -663,18 +663,44 @@ pub(crate) fn create_primitive_array_single_element(
(DataType::Binary, None) => Ok(Arc::new(BinaryArray::from_opt_vec(vec![
Option::<&[u8]>::None,
]))),
- (DataType::Decimal128(_, _), Some(PrimitiveLiteral::Int128(v))) => {
- Ok(Arc::new(arrow_array::Decimal128Array::from(vec![{ *v }])))
+ (DataType::Decimal128(precision, scale),
Some(PrimitiveLiteral::Int128(v))) => {
+ let array = Decimal128Array::from(vec![{ *v }])
+ .with_precision_and_scale(*precision, *scale)
+ .map_err(|e| {
+ Error::new(
+ ErrorKind::DataInvalid,
+ format!(
+ "Failed to create Decimal128Array with precision
{precision} and scale {scale}: {e}"
+ ),
+ )
+ })?;
+ Ok(Arc::new(array))
}
- (DataType::Decimal128(_, _), Some(PrimitiveLiteral::UInt128(v))) => {
- Ok(Arc::new(arrow_array::Decimal128Array::from(vec![
- *v as i128,
- ])))
+ (DataType::Decimal128(precision, scale),
Some(PrimitiveLiteral::UInt128(v))) => {
+ let array = Decimal128Array::from(vec![*v as i128])
+ .with_precision_and_scale(*precision, *scale)
+ .map_err(|e| {
+ Error::new(
+ ErrorKind::DataInvalid,
+ format!(
+ "Failed to create Decimal128Array with precision
{precision} and scale {scale}: {e}"
+ ),
+ )
+ })?;
+ Ok(Arc::new(array))
}
- (DataType::Decimal128(_, _), None) => {
- Ok(Arc::new(arrow_array::Decimal128Array::from(vec![
- Option::<i128>::None,
- ])))
+ (DataType::Decimal128(precision, scale), None) => {
+ let array = Decimal128Array::from(vec![Option::<i128>::None])
+ .with_precision_and_scale(*precision, *scale)
+ .map_err(|e| {
+ Error::new(
+ ErrorKind::DataInvalid,
+ format!(
+ "Failed to create Decimal128Array with precision
{precision} and scale {scale}: {e}"
+ ),
+ )
+ })?;
+ Ok(Arc::new(array))
}
(DataType::Struct(fields), None) => {
// Create a single-element StructArray with nulls
@@ -795,15 +821,48 @@ pub(crate) fn create_primitive_array_repeated(
let vals: Vec<Option<&[u8]>> = vec![None; num_rows];
Arc::new(BinaryArray::from_opt_vec(vals))
}
- (DataType::Decimal128(_, _), Some(PrimitiveLiteral::Int128(value))) =>
{
- Arc::new(Decimal128Array::from(vec![*value; num_rows]))
+ (DataType::Decimal128(precision, scale),
Some(PrimitiveLiteral::Int128(value))) => {
+ Arc::new(
+ Decimal128Array::from(vec![*value; num_rows])
+ .with_precision_and_scale(*precision, *scale)
+ .map_err(|e| {
+ Error::new(
+ ErrorKind::DataInvalid,
+ format!(
+ "Failed to create Decimal128Array with
precision {precision} and scale {scale}: {e}"
+ ),
+ )
+ })?,
+ )
}
- (DataType::Decimal128(_, _), Some(PrimitiveLiteral::UInt128(value)))
=> {
- Arc::new(Decimal128Array::from(vec![*value as i128; num_rows]))
+ (DataType::Decimal128(precision, scale),
Some(PrimitiveLiteral::UInt128(value))) => {
+ Arc::new(
+ Decimal128Array::from(vec![*value as i128; num_rows])
+ .with_precision_and_scale(*precision, *scale)
+ .map_err(|e| {
+ Error::new(
+ ErrorKind::DataInvalid,
+ format!(
+ "Failed to create Decimal128Array with
precision {precision} and scale {scale}: {e}"
+ ),
+ )
+ })?,
+ )
}
- (DataType::Decimal128(_, _), None) => {
+ (DataType::Decimal128(precision, scale), None) => {
let vals: Vec<Option<i128>> = vec![None; num_rows];
- Arc::new(Decimal128Array::from(vals))
+ Arc::new(
+ Decimal128Array::from(vals)
+ .with_precision_and_scale(*precision, *scale)
+ .map_err(|e| {
+ Error::new(
+ ErrorKind::DataInvalid,
+ format!(
+ "Failed to create Decimal128Array with
precision {precision} and scale {scale}: {e}"
+ ),
+ )
+ })?,
+ )
}
(DataType::Struct(fields), None) => {
// Create a StructArray filled with nulls
@@ -1678,4 +1737,48 @@ mod test {
]))),
]);
}
+
+ #[test]
+ fn test_create_decimal_array_respects_precision() {
+ // Decimal128Array::from() uses Arrow's default precision (38) instead
of the
+ // target precision, causing RecordBatch construction to fail when
schemas don't match.
+ let target_precision = 18u8;
+ let target_scale = 10i8;
+ let target_type = DataType::Decimal128(target_precision, target_scale);
+ let value = PrimitiveLiteral::Int128(10000000000);
+
+ let array = create_primitive_array_single_element(&target_type,
&Some(value))
+ .expect("Failed to create decimal array");
+
+ match array.data_type() {
+ DataType::Decimal128(precision, scale) => {
+ assert_eq!(*precision, target_precision);
+ assert_eq!(*scale, target_scale);
+ }
+ other => panic!("Expected Decimal128, got {other:?}"),
+ }
+ }
+
+ #[test]
+ fn test_create_decimal_array_repeated_respects_precision() {
+ // Ensure repeated arrays also respect target precision, not Arrow's
default.
+ let target_precision = 18u8;
+ let target_scale = 10i8;
+ let target_type = DataType::Decimal128(target_precision, target_scale);
+ let value = PrimitiveLiteral::Int128(10000000000);
+ let num_rows = 5;
+
+ let array = create_primitive_array_repeated(&target_type,
&Some(value), num_rows)
+ .expect("Failed to create repeated decimal array");
+
+ match array.data_type() {
+ DataType::Decimal128(precision, scale) => {
+ assert_eq!(*precision, target_precision);
+ assert_eq!(*scale, target_scale);
+ }
+ other => panic!("Expected Decimal128, got {other:?}"),
+ }
+
+ assert_eq!(array.len(), num_rows);
+ }
}