This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-3997-e3fbd9440ad0bb25c45848797ff9ec5ad7758624
in repository https://gitbox.apache.org/repos/asf/iceberg-python.git

commit 16cbef094012efef4ce990675664f25ad5bb24db
Author: Rodrigo Stachlewski Palma 
<[email protected]>
AuthorDate: Thu Oct 22 21:08:01 2026 +0000

    fix(conversions): reject a Decimal whose scale is negative (#3997)
    
    to_bytes compared abs(exponent) against the type scale, so a Decimal 
carrying
    a negative scale passed as if it had the matching positive one. The 
exponent is
    then dropped by decimal_to_unscaled, and the value is written four orders of
    magnitude off without an error: Decimal('1E+2') stored in a decimal(10, 2)
    reads back as 0.01.
    
    Decimal('100').normalize() produces exactly that form, and to_bytes writes 
the
    lower and upper bounds of a data file, so a wrong bound prunes files that do
    hold matching rows.
    
    Compare the signed scale instead.
---
 pyiceberg/conversions.py  | 8 +++++---
 tests/test_conversions.py | 7 +++++++
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/pyiceberg/conversions.py b/pyiceberg/conversions.py
index 6dd964f43..fde188544 100644
--- a/pyiceberg/conversions.py
+++ b/pyiceberg/conversions.py
@@ -310,9 +310,11 @@ def _(primitive_type: DecimalType, value: Decimal) -> 
bytes:
         bytes: The byte representation of `value`.
     """
     _, digits, exponent = value.as_tuple()
-    exponent = abs(int(exponent))
-    if exponent != primitive_type.scale:
-        raise ValueError(f"Cannot serialize value, scale of value does not 
match type {primitive_type}: {exponent}")
+    # A Decimal carries the negated scale as its exponent, so a value with a 
negative
+    # scale (1E+2) must not be read as if it had the matching positive one
+    value_scale = -int(exponent)
+    if value_scale != primitive_type.scale:
+        raise ValueError(f"Cannot serialize value, scale of value does not 
match type {primitive_type}: {value_scale}")
     elif len(digits) > primitive_type.precision:
         raise ValueError(
             f"Cannot serialize value, precision of value is greater than 
precision of type {primitive_type}: {len(digits)}"
diff --git a/tests/test_conversions.py b/tests/test_conversions.py
index e786ae068..322e58a9b 100644
--- a/tests/test_conversions.py
+++ b/tests/test_conversions.py
@@ -511,6 +511,13 @@ def test_raise_on_unregistered_type() -> None:
     "primitive_type, value, expected_error_message",
     [
         (DecimalType(7, 3), Decimal("123.4567"), "Cannot serialize value, 
scale of value does not match type decimal(7, 3): 4"),
+        # A Decimal with a negative scale must not pass as its positive 
counterpart
+        (DecimalType(10, 2), Decimal("1E+2"), "Cannot serialize value, scale 
of value does not match type decimal(10, 2): -2"),
+        (
+            DecimalType(10, 2),
+            Decimal("100").normalize(),
+            "Cannot serialize value, scale of value does not match type 
decimal(10, 2): -2",
+        ),
         (
             DecimalType(18, 8),
             Decimal("123456789.123456789"),

Reply via email to