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 04ce1e6e2e [core] Never prune postpone buckets in BucketSelector 
(#10085)
04ce1e6e2e is described below

commit 04ce1e6e2e3fd22bca355cea64dee578048af011
Author: YangJie <[email protected]>
AuthorDate: Thu Sep 24 02:40:29 2026 -0400

    [core] Never prune postpone buckets in BucketSelector (#10085)
---
 .../apache/paimon/operation/BucketSelector.java    |  7 +++++
 .../paimon/operation/BucketSelectorTest.java       | 34 ++++++++++++++++++++++
 2 files changed, 41 insertions(+)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/operation/BucketSelector.java 
b/paimon-core/src/main/java/org/apache/paimon/operation/BucketSelector.java
index ee4c1c1ec2..0eeb0b314e 100644
--- a/paimon-core/src/main/java/org/apache/paimon/operation/BucketSelector.java
+++ b/paimon-core/src/main/java/org/apache/paimon/operation/BucketSelector.java
@@ -84,6 +84,13 @@ public class BucketSelector implements ManifestBucketFilter {
 
     @Override
     public boolean test(BinaryRow partition, Integer bucket, Integer 
numBucket) {
+        if (bucket == null || bucket < 0 || numBucket == null || numBucket <= 
0) {
+            // Postpone buckets (negative, see BucketMode#POSTPONE_BUCKET) 
hold pending rows
+            // whose bucket is not yet assigned, so bucket keys cannot prune 
them; the same
+            // guard mayContain applies at the manifest level. A non-positive 
bucket count
+            // carries no bucket information either.
+            return true;
+        }
         return partitionSelectors
                 .computeIfAbsent(partition, this::createPartitionSelector)
                 .map(selector -> selector.test(bucket, numBucket))
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/operation/BucketSelectorTest.java 
b/paimon-core/src/test/java/org/apache/paimon/operation/BucketSelectorTest.java
index cd5208753f..16da67c541 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/operation/BucketSelectorTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/operation/BucketSelectorTest.java
@@ -60,6 +60,40 @@ public class BucketSelectorTest {
         assertThat(selected).hasSize(1);
     }
 
+    @Test
+    public void testPostponeBucketIsNeverPruned() {
+        RowType rowType = DataTypes.ROW(DataTypes.FIELD(0, "k", 
DataTypes.INT()));
+        RowType partType = RowType.of();
+        RowType bucketKeyType = DataTypes.ROW(DataTypes.FIELD(0, "k", 
DataTypes.INT()));
+        PredicateBuilder pb = new PredicateBuilder(rowType);
+        BucketSelector selector =
+                new BucketSelector(
+                        pb.equal(0, 5),
+                        BucketFunctionType.DEFAULT,
+                        rowType,
+                        partType,
+                        bucketKeyType);
+
+        // postpone entries carry bucket -2 and no bucket count; their pending 
rows cannot
+        // be pruned by bucket keys, so the selector must keep them, also when 
composed
+        // into the entry-level BucketFilter without onlyReadRealBuckets 
(streaming reads)
+        assertThat(selector.test(BinaryRow.EMPTY_ROW, -2, -2)).isTrue();
+        BucketFilter entryFilter = BucketFilter.create(false, null, null, 
selector);
+        assertThat(entryFilter.test(BinaryRow.EMPTY_ROW, -2, -2)).isTrue();
+
+        // a non-positive bucket count carries no bucket information, so it 
cannot be pruned
+        // either, and the selector must not divide by that count
+        assertThat(selector.test(BinaryRow.EMPTY_ROW, 3, 0)).isTrue();
+
+        // real buckets are still pruned: exactly the selected bucket passes
+        Set<Integer> selected = selectedBuckets(selector, BinaryRow.EMPTY_ROW, 
NUM_BUCKETS);
+        assertThat(selected).hasSize(1);
+        for (int b = 0; b < NUM_BUCKETS; b++) {
+            assertThat(selector.test(BinaryRow.EMPTY_ROW, b, NUM_BUCKETS))
+                    .isEqualTo(selected.contains(b));
+        }
+    }
+
     @Test
     public void testManifestBucketRange() {
         RowType rowType = DataTypes.ROW(DataTypes.FIELD(0, "k", 
DataTypes.INT()));

Reply via email to