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

JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new eb9b95a92b [common] Fix invalid DecimalType when extracting a variant 
decimal (#9672)
eb9b95a92b is described below

commit eb9b95a92b02ec2ee915a4e76a5c0b19315afb30
Author: jackylee <[email protected]>
AuthorDate: Fri Sep 11 14:02:58 2026 +0800

    [common] Fix invalid DecimalType when extracting a variant decimal (#9672)
---
 .../org/apache/paimon/data/variant/VariantGet.java | 10 ++++++-
 .../paimon/data/variant/GenericVariantTest.java    | 32 ++++++++++++++++++++++
 .../data/variant/PaimonShreddingUtilsTest.java     | 21 ++++++++++++++
 3 files changed, 62 insertions(+), 1 deletion(-)

diff --git 
a/paimon-common/src/main/java/org/apache/paimon/data/variant/VariantGet.java 
b/paimon-common/src/main/java/org/apache/paimon/data/variant/VariantGet.java
index e053f330f4..37aa20b4af 100644
--- a/paimon-common/src/main/java/org/apache/paimon/data/variant/VariantGet.java
+++ b/paimon-common/src/main/java/org/apache/paimon/data/variant/VariantGet.java
@@ -138,8 +138,16 @@ public class VariantGet {
                     break;
                 case DECIMAL:
                     BigDecimal decimal = v.getDecimal();
-                    int precision = decimal.precision();
+                    if (decimal.scale() < 0) {
+                        // stripTrailingZeros folds trailing zeros into a 
negative exponent,
+                        // and a negative scale is not a Paimon decimal
+                        decimal = decimal.setScale(0);
+                    }
                     int scale = decimal.scale();
+                    // precision() counts the digits of the unscaled value, so 
it is smaller than
+                    // the scale for a value below 0.1, which DecimalType 
rejects. The variant
+                    // writer caps both at MAX_DECIMAL16_PRECISION, so this 
stays in range.
+                    int precision = Math.max(decimal.precision(), scale);
                     input = Decimal.fromBigDecimal(decimal, precision, scale);
                     inputType = DataTypes.DECIMAL(precision, scale);
                     break;
diff --git 
a/paimon-common/src/test/java/org/apache/paimon/data/variant/GenericVariantTest.java
 
b/paimon-common/src/test/java/org/apache/paimon/data/variant/GenericVariantTest.java
index be8051cc72..e5e507c455 100644
--- 
a/paimon-common/src/test/java/org/apache/paimon/data/variant/GenericVariantTest.java
+++ 
b/paimon-common/src/test/java/org/apache/paimon/data/variant/GenericVariantTest.java
@@ -276,6 +276,38 @@ public class GenericVariantTest {
         assertThat(variant.variantGet("$.nullField", DataTypes.BOOLEAN(), 
castArgs)).isNull();
     }
 
+    @Test
+    public void testVariantGetDecimalWithScaleAbovePrecision() {
+        // precision() counts the digits of the unscaled value, so it is below 
the scale for any
+        // value under 0.1. The scale 38 case is the widest the reader admits, 
and it is the
+        // first one here that needs a non compact Decimal.
+        String tiny = "0.00000000000000000000000000000000000001";
+        Variant variant = GenericVariant.fromJson("{\"small\": 0.05, \"tiny\": 
" + tiny + "}");
+        VariantCastArgs castArgs = new VariantCastArgs(false, ZoneOffset.UTC);
+
+        assertThat(variant.variantGet("$.small", DataTypes.STRING(), castArgs))
+                .isEqualTo(BinaryString.fromString("0.05"));
+        assertThat(variant.variantGet("$.small", DataTypes.DECIMAL(5, 3), 
castArgs))
+                .isEqualTo(Decimal.fromBigDecimal(new BigDecimal("0.050"), 5, 
3));
+        assertThat(variant.variantGet("$.tiny", DataTypes.STRING(), castArgs))
+                .isEqualTo(BinaryString.fromString(tiny));
+        assertThat(variant.variantGet("$.tiny", DataTypes.DECIMAL(38, 38), 
castArgs))
+                .isEqualTo(Decimal.fromBigDecimal(new BigDecimal(tiny), 38, 
38));
+    }
+
+    @Test
+    public void testVariantGetDecimalWithNegativeScale() {
+        // getDecimal() strips trailing zeros, which turns 100.00 into 1E+2, a 
negative scale
+        Variant variant = GenericVariant.fromJson("{\"round\": 100.00}");
+        VariantCastArgs castArgs = new VariantCastArgs(false, ZoneOffset.UTC);
+
+        // rescaling rather than un-stripping keeps this in step with toJson
+        assertThat(variant.variantGet("$.round", DataTypes.STRING(), castArgs))
+                .isEqualTo(BinaryString.fromString("100"));
+        assertThat(variant.variantGet("$.round", DataTypes.DECIMAL(5, 1), 
castArgs))
+                .isEqualTo(Decimal.fromBigDecimal(new BigDecimal("100.0"), 5, 
1));
+    }
+
     @Test
     public void testObjectFieldOrderingCompatibility() {
         String bmpKey = "\uE000";
diff --git 
a/paimon-common/src/test/java/org/apache/paimon/data/variant/PaimonShreddingUtilsTest.java
 
b/paimon-common/src/test/java/org/apache/paimon/data/variant/PaimonShreddingUtilsTest.java
index 19ebca4fbf..b1952357d7 100644
--- 
a/paimon-common/src/test/java/org/apache/paimon/data/variant/PaimonShreddingUtilsTest.java
+++ 
b/paimon-common/src/test/java/org/apache/paimon/data/variant/PaimonShreddingUtilsTest.java
@@ -291,6 +291,27 @@ public class PaimonShreddingUtilsTest {
                                         })));
     }
 
+    @Test
+    public void testAssembleDecimalWithScaleAbovePrecision() {
+        // the unshredded leg extracts through VariantGet, which used to build 
an invalid
+        // DecimalType for a value below 0.1 or one whose trailing zeros were 
stripped off
+        GenericVariant v = GenericVariant.fromJson("{\"round\": 100.00, 
\"small\": 0.05}");
+        VariantCastArgs castArgs = new VariantCastArgs(true, ZoneOffset.UTC);
+
+        VariantSchema variantSchema = 
buildVariantSchema(variantShreddingSchema(RowType.of()));
+        FieldToExtract[] fieldsToExtract = {
+            buildFieldsToExtract(DataTypes.STRING(), "$.round", castArgs, 
variantSchema),
+            buildFieldsToExtract(DataTypes.STRING(), "$.small", castArgs, 
variantSchema)
+        };
+
+        assertThat(
+                        assembleVariantStruct(
+                                castShredded(v, variantSchema), variantSchema, 
fieldsToExtract))
+                .isEqualTo(
+                        GenericRow.of(
+                                BinaryString.fromString("100"), 
BinaryString.fromString("0.05")));
+    }
+
     private static void assertVariantStructEquals(
             RowType shreddedType, RowType allTypes, GenericVariant v, 
GenericRow expected) {
         VariantCastArgs castArgs = new VariantCastArgs(true, ZoneOffset.UTC);

Reply via email to