You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by hu...@apache.org on 2026/07/22 17:43:04 UTC

(iceberg) branch main updated: Parquet: Fix variant BINARY upper bound to truncate up (#16880)

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

huaxingao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg.git


The following commit(s) were added to refs/heads/main by this push:
     new 470f4642bd Parquet: Fix variant BINARY upper bound to truncate up (#16880)
470f4642bd is described below

commit 470f4642bdda336bac5372c79c762a2654dfe3f3
Author: vishnu prakash <nv...@gmail.com>
AuthorDate: Wed Jul 22 23:12:57 2026 +0530

    Parquet: Fix variant BINARY upper bound to truncate up (#16880)
    
    * Parquet: Fix variant BINARY upper bound to truncate up
    
    * Parquet: Add test for variant BINARY upper bound overflow
---
 .../apache/iceberg/parquet/ParquetVariantUtil.java |  2 +-
 .../apache/iceberg/parquet/TestVariantMetrics.java | 50 ++++++++++++++++++++++
 2 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/parquet/src/main/java/org/apache/iceberg/parquet/ParquetVariantUtil.java b/parquet/src/main/java/org/apache/iceberg/parquet/ParquetVariantUtil.java
index ac418a1127..a9b088aaf2 100644
--- a/parquet/src/main/java/org/apache/iceberg/parquet/ParquetVariantUtil.java
+++ b/parquet/src/main/java/org/apache/iceberg/parquet/ParquetVariantUtil.java
@@ -367,7 +367,7 @@ class ParquetVariantUtil {
           return truncatedString != null ? Variants.of(PhysicalType.STRING, truncatedString) : null;
         case BINARY:
           ByteBuffer truncatedBuffer =
-              BinaryUtil.truncateBinaryMin((ByteBuffer) value.asPrimitive().get(), 16);
+              BinaryUtil.truncateBinaryMax((ByteBuffer) value.asPrimitive().get(), 16);
           return truncatedBuffer != null ? Variants.of(PhysicalType.BINARY, truncatedBuffer) : null;
         default:
           return value;
diff --git a/parquet/src/test/java/org/apache/iceberg/parquet/TestVariantMetrics.java b/parquet/src/test/java/org/apache/iceberg/parquet/TestVariantMetrics.java
index b7b63d6f6b..8381052f58 100644
--- a/parquet/src/test/java/org/apache/iceberg/parquet/TestVariantMetrics.java
+++ b/parquet/src/test/java/org/apache/iceberg/parquet/TestVariantMetrics.java
@@ -223,6 +223,56 @@ public class TestVariantMetrics {
     assertThat(metrics).extracting("originalTypes").isEqualTo(Map.of(1, Types.LongType.get()));
   }
 
+  @Test
+  public void testShreddedBinaryBoundsTruncation() throws IOException {
+    // binary longer than the 16-byte truncation length so the bounds are truncated
+    byte[] bytes = new byte[20];
+    for (int i = 0; i < bytes.length; i += 1) {
+      bytes[i] = (byte) (i + 1);
+    }
+    VariantValue value = Variants.of(ByteBuffer.wrap(bytes));
+
+    Metrics metrics =
+        writeParquet(
+            (id, name) -> ParquetVariantUtil.toParquetSchema(value),
+            Variant.of(EMPTY, value),
+            Variant.of(EMPTY, Variants.ofNull()),
+            null);
+
+    assertThat(metrics.lowerBounds().get(2))
+        .extracting(b -> Variant.from(b).value().asObject().get(ROOT_FIELD))
+        .isEqualTo(Variants.of(BinaryUtil.truncateBinaryMin(ByteBuffer.wrap(bytes), 16)));
+
+    assertThat(metrics.upperBounds().get(2))
+        .extracting(b -> Variant.from(b).value().asObject().get(ROOT_FIELD))
+        .isEqualTo(Variants.of(BinaryUtil.truncateBinaryMax(ByteBuffer.wrap(bytes), 16)));
+  }
+
+  @Test
+  public void testShreddedBinaryUpperBoundOverflow() throws IOException {
+    // an all-0xFF binary cannot be truncated up so the upper bound is omitted
+    byte[] bytes = new byte[20];
+    for (int i = 0; i < bytes.length; i += 1) {
+      bytes[i] = (byte) 0xFF;
+    }
+    VariantValue value = Variants.of(ByteBuffer.wrap(bytes));
+
+    Metrics metrics =
+        writeParquet(
+            (id, name) -> ParquetVariantUtil.toParquetSchema(value),
+            Variant.of(EMPTY, value),
+            Variant.of(EMPTY, Variants.ofNull()),
+            null);
+
+    assertThat(metrics.lowerBounds().get(2))
+        .extracting(b -> Variant.from(b).value().asObject().get(ROOT_FIELD))
+        .isEqualTo(Variants.of(BinaryUtil.truncateBinaryMin(ByteBuffer.wrap(bytes), 16)));
+
+    assertThat(metrics.upperBounds().get(2))
+        .extracting(b -> Variant.from(b).value().asObject().get(ROOT_FIELD))
+        .isNull();
+  }
+
   @Test
   public void testVariantFloatNaN() throws IOException {
     // NaN values are not counted because there is no ID for FieldMetrics