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 4df39926b5 [common] Reject out-of-range bloom filter items and fpp 
options (#10108)
4df39926b5 is described below

commit 4df39926b5617fffb76a1f84b5db4cffbf246e83
Author: jackylee <[email protected]>
AuthorDate: Thu Sep 24 13:10:14 2026 +0800

    [common] Reject out-of-range bloom filter items and fpp options (#10108)
---
 .../bloomfilter/BloomFilterFileIndex.java          |  7 ++++
 .../org/apache/paimon/utils/BloomFilter64.java     | 12 ++++++-
 .../bloomfilter/BloomFilterFileIndexTest.java      | 41 ++++++++++++++++++++++
 3 files changed, 59 insertions(+), 1 deletion(-)

diff --git 
a/paimon-common/src/main/java/org/apache/paimon/fileindex/bloomfilter/BloomFilterFileIndex.java
 
b/paimon-common/src/main/java/org/apache/paimon/fileindex/bloomfilter/BloomFilterFileIndex.java
index 83d6b64b64..2364466dc8 100644
--- 
a/paimon-common/src/main/java/org/apache/paimon/fileindex/bloomfilter/BloomFilterFileIndex.java
+++ 
b/paimon-common/src/main/java/org/apache/paimon/fileindex/bloomfilter/BloomFilterFileIndex.java
@@ -36,6 +36,7 @@ import java.io.IOException;
 
 import static org.apache.paimon.fileindex.FileIndexResult.REMAIN;
 import static org.apache.paimon.fileindex.FileIndexResult.SKIP;
+import static org.apache.paimon.utils.Preconditions.checkArgument;
 
 /**
  * Bloom filter for file index.
@@ -86,6 +87,12 @@ public class BloomFilterFileIndex implements FileIndexer {
         private final FastHash hashFunction;
 
         public Writer(DataType type, int items, double fpp) {
+            checkArgument(
+                    fpp > 0 && fpp < 1,
+                    "Bloom filter '" + FPP + "' must be in range (0, 1), but 
was %s.",
+                    fpp);
+            checkArgument(
+                    items > 0, "Bloom filter '" + ITEMS + "' must be positive, 
but was %s.", items);
             this.filter = new BloomFilter64(items, fpp);
             this.hashFunction = FastHash.getHashFunction(type);
         }
diff --git 
a/paimon-common/src/main/java/org/apache/paimon/utils/BloomFilter64.java 
b/paimon-common/src/main/java/org/apache/paimon/utils/BloomFilter64.java
index 5a4be5a45f..0daf5e89c3 100644
--- a/paimon-common/src/main/java/org/apache/paimon/utils/BloomFilter64.java
+++ b/paimon-common/src/main/java/org/apache/paimon/utils/BloomFilter64.java
@@ -26,7 +26,17 @@ public final class BloomFilter64 {
     private final int numHashFunctions;
 
     public BloomFilter64(long items, double fpp) {
-        int nb = (int) (-items * Math.log(fpp) / (Math.log(2) * Math.log(2)));
+        Preconditions.checkArgument(
+                items > 0, "Bloom filter items must be positive, but was %s.", 
items);
+        long numBitsEstimate = (long) (-items * Math.log(fpp) / (Math.log(2) * 
Math.log(2)));
+        Preconditions.checkArgument(
+                numBitsEstimate >= 0 && numBitsEstimate <= Integer.MAX_VALUE - 
Byte.SIZE,
+                "Bloom filter needs %s bits for items=%s and fpp=%s, which is 
out of the "
+                        + "supported range; reduce items or increase fpp.",
+                numBitsEstimate,
+                items,
+                fpp);
+        int nb = (int) numBitsEstimate;
         this.numBits = nb + (Byte.SIZE - (nb % Byte.SIZE));
         this.numHashFunctions =
                 Math.max(1, (int) Math.round((double) numBits / items * 
Math.log(2)));
diff --git 
a/paimon-common/src/test/java/org/apache/paimon/fileindex/bloomfilter/BloomFilterFileIndexTest.java
 
b/paimon-common/src/test/java/org/apache/paimon/fileindex/bloomfilter/BloomFilterFileIndexTest.java
index 88ffc0daed..956783803c 100644
--- 
a/paimon-common/src/test/java/org/apache/paimon/fileindex/bloomfilter/BloomFilterFileIndexTest.java
+++ 
b/paimon-common/src/test/java/org/apache/paimon/fileindex/bloomfilter/BloomFilterFileIndexTest.java
@@ -126,6 +126,47 @@ public class BloomFilterFileIndexTest {
         Assertions.assertThat((double) errorCount / num).isLessThan(0.03);
     }
 
+    @Test
+    public void testRejectsInvalidOptions() {
+        // fpp must be a probability in (0, 1): a percentage-shaped value, 
zero, or >= 1 is rejected
+        // at write time with a message naming the option, instead of a bare
+        // NegativeArraySizeException
+        // or a silently useless one-byte filter.
+        Assertions.assertThatThrownBy(() -> createWriter("10000", "10"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("'fpp'");
+        Assertions.assertThatThrownBy(() -> createWriter("10000", "0"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("'fpp'");
+        Assertions.assertThatThrownBy(() -> createWriter("10000", "1.0"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("'fpp'");
+        Assertions.assertThatThrownBy(() -> createWriter("10000", "-0.1"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("'fpp'");
+
+        // items must be positive.
+        Assertions.assertThatThrownBy(() -> createWriter("0", "0.1"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("'items'");
+
+        // a huge items count would overflow the bit-set size; reject instead 
of allocating a
+        // negative-length array.
+        Assertions.assertThatThrownBy(() -> 
createWriter(String.valueOf(Integer.MAX_VALUE), "0.1"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("bits");
+
+        // a valid configuration still builds.
+        createWriter("10000", "0.02");
+    }
+
+    private static FileIndexWriter createWriter(String items, String fpp) {
+        Options options = new Options();
+        options.set("items", items);
+        options.set("fpp", fpp);
+        return new BloomFilterFileIndex(DataTypes.BYTES(), 
options).createWriter();
+    }
+
     private byte[] random() {
         byte[] b = new byte[Math.abs(RANDOM.nextInt(400) + 1)];
         RANDOM.nextBytes(b);

Reply via email to