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 7dac195327 [core] Handle null literals in IN and ARRAYS_OVERLAP 
predicates for file-indexed columns (#10718)
7dac195327 is described below

commit 7dac19532748cc2a634107d47030e77712f66423
Author: cxzl25 <[email protected]>
AuthorDate: Fri Oct 23 09:35:21 2026 +0800

    [core] Handle null literals in IN and ARRAYS_OVERLAP predicates for 
file-indexed columns (#10718)
---
 .../paimon/fileindex/FileIndexPredicate.java       | 25 ++++++++++++++++++----
 .../paimon/spark/sql/PaimonPushDownTestBase.scala  | 19 ++++++++++++++++
 2 files changed, 40 insertions(+), 4 deletions(-)

diff --git 
a/paimon-common/src/main/java/org/apache/paimon/fileindex/FileIndexPredicate.java
 
b/paimon-common/src/main/java/org/apache/paimon/fileindex/FileIndexPredicate.java
index 57ea87b003..b664cb4758 100644
--- 
a/paimon-common/src/main/java/org/apache/paimon/fileindex/FileIndexPredicate.java
+++ 
b/paimon-common/src/main/java/org/apache/paimon/fileindex/FileIndexPredicate.java
@@ -23,8 +23,11 @@ import org.apache.paimon.fs.ByteArraySeekableStream;
 import org.apache.paimon.fs.FileIO;
 import org.apache.paimon.fs.Path;
 import org.apache.paimon.fs.SeekableInputStream;
+import org.apache.paimon.predicate.ArraysOverlap;
 import org.apache.paimon.predicate.CompoundPredicate;
 import org.apache.paimon.predicate.FieldRef;
+import org.apache.paimon.predicate.In;
+import org.apache.paimon.predicate.LeafFunction;
 import org.apache.paimon.predicate.LeafPredicate;
 import org.apache.paimon.predicate.Or;
 import org.apache.paimon.predicate.Predicate;
@@ -46,10 +49,13 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 
+import static java.util.stream.Collectors.toList;
 import static org.apache.paimon.fileindex.FileIndexResult.REMAIN;
+import static org.apache.paimon.fileindex.FileIndexResult.SKIP;
 
 /** Utils to check secondary index (e.g. bloom filter) predicate. */
 public class FileIndexPredicate implements Closeable {
@@ -189,14 +195,25 @@ public class FileIndexPredicate implements Closeable {
                 return REMAIN;
             }
 
+            LeafFunction function = predicate.function();
+            List<Object> literals = predicate.literals();
+            if (literals.stream().anyMatch(Objects::isNull)) {
+                // Index readers do not expect a null literal. A null literal 
never matches a
+                // row: IN and ARRAYS_OVERLAP ignore it, and every other 
function is false on it.
+                if (function instanceof In || function instanceof 
ArraysOverlap) {
+                    literals = 
literals.stream().filter(Objects::nonNull).collect(toList());
+                    if (literals.isEmpty()) {
+                        return SKIP;
+                    }
+                } else {
+                    return SKIP;
+                }
+            }
             FileIndexResult compoundResult = REMAIN;
             FieldRef fieldRef = fieldRefOptional.get();
             for (FileIndexReader fileIndexReader : 
columnIndexReaders.get(fieldRef.name())) {
                 compoundResult =
-                        compoundResult.and(
-                                predicate
-                                        .function()
-                                        .visit(fileIndexReader, fieldRef, 
predicate.literals()));
+                        compoundResult.and(function.visit(fileIndexReader, 
fieldRef, literals));
 
                 if (!compoundResult.remain()) {
                     return compoundResult;
diff --git 
a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonPushDownTestBase.scala
 
b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonPushDownTestBase.scala
index 71fce5b4da..9ff06e54ca 100644
--- 
a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonPushDownTestBase.scala
+++ 
b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonPushDownTestBase.scala
@@ -611,6 +611,25 @@ abstract class PaimonPushDownTestBase extends 
PaimonSparkTestBase with AdaptiveS
     }
   }
 
+  test("Paimon pushDown: IN with a NULL literal on a file-indexed column") {
+    Seq("range-bitmap", "bsi", "bitmap").foreach {
+      indexType =>
+        withTable("T") {
+          sql(s"""
+                 |CREATE TABLE T (id INT, v STRING)
+                 |TBLPROPERTIES ('file-index.$indexType.columns' = 'id', 
'file-index.in-manifest-threshold' = '1 kb')
+                 |""".stripMargin)
+          sql("INSERT INTO T VALUES (1, 'a'), (NULL, 'b'), (3, 'c')")
+
+          checkAnswer(sql("SELECT v FROM T WHERE id IN (1, NULL)"), Row("a") 
:: Nil)
+          checkAnswer(sql("SELECT v FROM T WHERE id NOT IN (1, NULL)"), Nil)
+          checkAnswer(
+            sql("SELECT v FROM T WHERE id IN (3, NULL) OR id IS NULL ORDER BY 
v"),
+            Row("b") :: Row("c") :: Nil)
+        }
+    }
+  }
+
   test("Paimon pushDown: TopN for append-only tables") {
     assume(gteqSpark3_3)
     spark.sql("""

Reply via email to