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 67435d2a40 [core] Fix unsupported predicate fallback for global 
indexes (#8790)
67435d2a40 is described below

commit 67435d2a4079f194949e62983d319597bd969960
Author: QuakeWang <[email protected]>
AuthorDate: Wed Jul 22 14:45:07 2026 +0800

    [core] Fix unsupported predicate fallback for global indexes (#8790)
---
 .../globalindex/ConstantGlobalIndexReader.java     | 11 ++++
 .../paimon/globalindex/GlobalIndexReader.java      | 18 ++++++
 .../globalindex/OffsetGlobalIndexReader.java       |  6 ++
 .../paimon/globalindex/UnionGlobalIndexReader.java |  6 ++
 .../globalindex/GlobalIndexEvaluatorTest.java      | 69 ++++++++++++++++++++++
 5 files changed, 110 insertions(+)

diff --git 
a/paimon-common/src/main/java/org/apache/paimon/globalindex/ConstantGlobalIndexReader.java
 
b/paimon-common/src/main/java/org/apache/paimon/globalindex/ConstantGlobalIndexReader.java
index fe610f9668..d30c758fd6 100644
--- 
a/paimon-common/src/main/java/org/apache/paimon/globalindex/ConstantGlobalIndexReader.java
+++ 
b/paimon-common/src/main/java/org/apache/paimon/globalindex/ConstantGlobalIndexReader.java
@@ -49,6 +49,11 @@ public class ConstantGlobalIndexReader implements 
GlobalIndexReader {
         return result;
     }
 
+    @Override
+    public CompletableFuture<Optional<GlobalIndexResult>> visitIsNaN(FieldRef 
fieldRef) {
+        return result;
+    }
+
     @Override
     public CompletableFuture<Optional<GlobalIndexResult>> visitStartsWith(
             FieldRef fieldRef, Object literal) {
@@ -127,6 +132,12 @@ public class ConstantGlobalIndexReader implements 
GlobalIndexReader {
         return result;
     }
 
+    @Override
+    public CompletableFuture<Optional<GlobalIndexResult>> visitNotBetween(
+            FieldRef fieldRef, Object from, Object to) {
+        return result;
+    }
+
     @Override
     public void close() {}
 }
diff --git 
a/paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexReader.java
 
b/paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexReader.java
index 5951d40726..807ecb8aa0 100644
--- 
a/paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexReader.java
+++ 
b/paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexReader.java
@@ -19,6 +19,7 @@
 package org.apache.paimon.globalindex;
 
 import org.apache.paimon.predicate.BatchVectorSearch;
+import org.apache.paimon.predicate.FieldRef;
 import org.apache.paimon.predicate.FullTextSearch;
 import org.apache.paimon.predicate.FunctionVisitor;
 import org.apache.paimon.predicate.LeafPredicate;
@@ -34,6 +35,23 @@ import java.util.concurrent.CompletableFuture;
 public interface GlobalIndexReader
         extends 
FunctionVisitor<CompletableFuture<Optional<GlobalIndexResult>>>, Closeable {
 
+    @Override
+    default CompletableFuture<Optional<GlobalIndexResult>> visitIsNaN(FieldRef 
fieldRef) {
+        return CompletableFuture.completedFuture(Optional.empty());
+    }
+
+    @Override
+    default CompletableFuture<Optional<GlobalIndexResult>> visitBetween(
+            FieldRef fieldRef, Object from, Object to) {
+        return CompletableFuture.completedFuture(Optional.empty());
+    }
+
+    @Override
+    default CompletableFuture<Optional<GlobalIndexResult>> visitNotBetween(
+            FieldRef fieldRef, Object from, Object to) {
+        return CompletableFuture.completedFuture(Optional.empty());
+    }
+
     @Override
     default CompletableFuture<Optional<GlobalIndexResult>> visitAnd(
             List<CompletableFuture<Optional<GlobalIndexResult>>> children) {
diff --git 
a/paimon-common/src/main/java/org/apache/paimon/globalindex/OffsetGlobalIndexReader.java
 
b/paimon-common/src/main/java/org/apache/paimon/globalindex/OffsetGlobalIndexReader.java
index 646e1f6ca2..7b513a2c39 100644
--- 
a/paimon-common/src/main/java/org/apache/paimon/globalindex/OffsetGlobalIndexReader.java
+++ 
b/paimon-common/src/main/java/org/apache/paimon/globalindex/OffsetGlobalIndexReader.java
@@ -133,6 +133,12 @@ public class OffsetGlobalIndexReader implements 
GlobalIndexReader {
         return wrapped.visitBetween(fieldRef, from, 
to).thenApply(this::applyOffset);
     }
 
+    @Override
+    public CompletableFuture<Optional<GlobalIndexResult>> visitNotBetween(
+            FieldRef fieldRef, Object from, Object to) {
+        return wrapped.visitNotBetween(fieldRef, from, 
to).thenApply(this::applyOffset);
+    }
+
     @Override
     public CompletableFuture<Optional<ScoredGlobalIndexResult>> 
visitVectorSearch(
             VectorSearch vectorSearch) {
diff --git 
a/paimon-common/src/main/java/org/apache/paimon/globalindex/UnionGlobalIndexReader.java
 
b/paimon-common/src/main/java/org/apache/paimon/globalindex/UnionGlobalIndexReader.java
index 57eb372591..c901d4905c 100644
--- 
a/paimon-common/src/main/java/org/apache/paimon/globalindex/UnionGlobalIndexReader.java
+++ 
b/paimon-common/src/main/java/org/apache/paimon/globalindex/UnionGlobalIndexReader.java
@@ -136,6 +136,12 @@ public class UnionGlobalIndexReader implements 
GlobalIndexReader {
         return unionAsync(reader -> reader.visitBetween(fieldRef, from, to));
     }
 
+    @Override
+    public CompletableFuture<Optional<GlobalIndexResult>> visitNotBetween(
+            FieldRef fieldRef, Object from, Object to) {
+        return unionAsync(reader -> reader.visitNotBetween(fieldRef, from, 
to));
+    }
+
     @Override
     public CompletableFuture<Optional<ScoredGlobalIndexResult>> 
visitVectorSearch(
             VectorSearch vectorSearch) {
diff --git 
a/paimon-common/src/test/java/org/apache/paimon/globalindex/GlobalIndexEvaluatorTest.java
 
b/paimon-common/src/test/java/org/apache/paimon/globalindex/GlobalIndexEvaluatorTest.java
index b2e75a4b6e..dcd36c8eaa 100644
--- 
a/paimon-common/src/test/java/org/apache/paimon/globalindex/GlobalIndexEvaluatorTest.java
+++ 
b/paimon-common/src/test/java/org/apache/paimon/globalindex/GlobalIndexEvaluatorTest.java
@@ -643,6 +643,75 @@ class GlobalIndexEvaluatorTest {
         evaluator.close();
     }
 
+    @Test
+    void testUnsupportedIsNaNFallsBack() {
+        RowType rowType =
+                new RowType(Collections.singletonList(new DataField(0, "a", 
DataTypes.DOUBLE())));
+        GlobalIndexEvaluator evaluator =
+                new GlobalIndexEvaluator(
+                        rowType,
+                        fieldId -> Collections.singletonList(new 
StubGlobalIndexReader(null)));
+
+        Optional<GlobalIndexResult> result =
+                evaluator.evaluate(new PredicateBuilder(rowType).isNaN(0));
+
+        assertThat(result).isEmpty();
+        evaluator.close();
+    }
+
+    @Test
+    void testNotBetweenThroughUnionAndOffset() {
+        RowType rowType = rowType();
+        GlobalIndexReader delegate =
+                new StubGlobalIndexReader(null) {
+                    @Override
+                    public CompletableFuture<Optional<GlobalIndexResult>> 
visitNotBetween(
+                            FieldRef fieldRef, Object from, Object to) {
+                        return 
CompletableFuture.completedFuture(Optional.of(resultOf(1, 3)));
+                    }
+                };
+        GlobalIndexReader wrapped =
+                new UnionGlobalIndexReader(
+                        Collections.singletonList(new 
OffsetGlobalIndexReader(delegate, 10L, 20L)));
+        GlobalIndexEvaluator evaluator =
+                new GlobalIndexEvaluator(rowType, fieldId -> 
Collections.singletonList(wrapped));
+        PredicateBuilder builder = new PredicateBuilder(rowType);
+
+        Optional<GlobalIndexResult> result =
+                evaluator.evaluate(builder.between(0, 1, 2).negate().get());
+
+        assertThat(result).isPresent();
+        assertBitmapContainsExactly(result.get().results(), 11L, 13L);
+        evaluator.close();
+    }
+
+    @Test
+    void testWrappedUnsupportedRangePredicatesFallBack() {
+        RowType rowType = rowType();
+        GlobalIndexReader wrapped =
+                new UnionGlobalIndexReader(
+                        Collections.singletonList(
+                                new OffsetGlobalIndexReader(
+                                        new StubGlobalIndexReader(null), 10L, 
20L)));
+        GlobalIndexEvaluator evaluator =
+                new GlobalIndexEvaluator(rowType, fieldId -> 
Collections.singletonList(wrapped));
+        PredicateBuilder builder = new PredicateBuilder(rowType);
+
+        assertThat(evaluator.evaluate(builder.between(0, 1, 
2).negate().get())).isEmpty();
+        assertThat(evaluator.evaluate(builder.between(0, 1, 2))).isEmpty();
+        evaluator.close();
+    }
+
+    @Test
+    void testConstantReaderReturnsFixedResultForIsNaNAndNotBetween() {
+        GlobalIndexResult expected = resultOf(1, 2);
+        GlobalIndexReader reader = new ConstantGlobalIndexReader(expected);
+        FieldRef fieldRef = new FieldRef(0, "a", DataTypes.DOUBLE());
+
+        assertThat(reader.visitIsNaN(fieldRef).join()).contains(expected);
+        assertThat(reader.visitNotBetween(fieldRef, 1, 
2).join()).contains(expected);
+    }
+
     @Test
     void testNullPredicate() {
         RowType rowType = rowType();

Reply via email to