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

wgtmac pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/parquet-java.git


The following commit(s) were added to refs/heads/master by this push:
     new 393727e6d GH-3632: Fix record-level notIn evaluation (#3631)
393727e6d is described below

commit 393727e6d14b8ce2a485659a635dca0a2996341a
Author: Gang Wu <[email protected]>
AuthorDate: Wed Jun 24 14:21:00 2026 +0800

    GH-3632: Fix record-level notIn evaluation (#3631)
---
 ...crementallyUpdatedFilterPredicateGenerator.java | 63 +++++++++++-----------
 .../recordlevel/TestRecordLevelFilters.java        | 37 ++++++++++++-
 2 files changed, 67 insertions(+), 33 deletions(-)

diff --git 
a/parquet-generator/src/main/java/org/apache/parquet/filter2/IncrementallyUpdatedFilterPredicateGenerator.java
 
b/parquet-generator/src/main/java/org/apache/parquet/filter2/IncrementallyUpdatedFilterPredicateGenerator.java
index 7f66ce382..f62ba56a8 100644
--- 
a/parquet-generator/src/main/java/org/apache/parquet/filter2/IncrementallyUpdatedFilterPredicateGenerator.java
+++ 
b/parquet-generator/src/main/java/org/apache/parquet/filter2/IncrementallyUpdatedFilterPredicateGenerator.java
@@ -281,51 +281,50 @@ public class IncrementallyUpdatedFilterPredicateGenerator 
{
   }
 
   private void addInNotInCase(TypeInfo info, boolean isEq, boolean 
expectMultipleResults) throws IOException {
-    add("    if (clazz.equals(" + info.className + ".class)) {\n" + "      if 
(pred.getValues().contains(null)) {\n"
-        + "        valueInspector = new ValueInspector() {\n"
-        + "          @Override\n"
-        + "          public void updateNull() {\n"
-        + "            setResult("
-        + isEq + ");\n" + "          }\n"
-        + "\n"
-        + "          @Override\n"
-        + "          public void update("
-        + info.primitiveName + " value) {\n" + "            setResult("
-        + !isEq + ");\n" + "          }\n"
-        + "        };\n"
-        + "      } else {\n"
-        + "        final Set<"
+    String nullResult = isEq ? "containsNull" : "!containsNull";
+    add("    if (clazz.equals(" + info.className + ".class)) {\n" + "      
final Set<"
         + info.className + "> target = (Set<" + info.className + ">) 
pred.getValues();\n"
-        + "        final PrimitiveComparator<"
+        + "      final boolean containsNull = target.contains(null);\n"
+        + "      final PrimitiveComparator<"
         + info.className + "> comparator = getComparator(columnPath);\n" + "\n"
-        + "        valueInspector = new ValueInspector() {\n"
-        + "          @Override\n"
-        + "          public void updateNull() {\n"
-        + "            setResult("
-        + !isEq + ");\n" + "          }\n"
+        + "      valueInspector = new ValueInspector() {\n"
+        + "        @Override\n"
+        + "        public void updateNull() {\n");
+    if (!expectMultipleResults) {
+      add("          setResult(" + nullResult + ");\n");
+    } else {
+      add("          if (" + nullResult + ") { setResult(true); }\n");
+    }
+    add("        }\n"
         + "\n"
-        + "          @Override\n"
-        + "          public void update("
+        + "        @Override\n"
+        + "        public void update("
         + info.primitiveName + " value) {\n");
 
     if (expectMultipleResults) {
-      add("            if (isKnown()) return;\n");
+      add("          if (isKnown()) return;\n");
     }
-    add("            for (" + info.primitiveName + " i : target) {\n");
-
-    add("              if(" + compareEquality("value", "i", isEq) + ") {\n");
+    add("          for (" + info.className + " i : target) {\n"
+        + "            if (i == null) { continue; }\n"
+        + "            "
+        + info.primitiveName + " targetValue = i;\n");
 
-    add("                 setResult(true);\n                 return;\n");
+    add("            if(" + compareEquality("value", "targetValue", true) + ") 
{\n");
 
-    add("               }\n");
+    if (!expectMultipleResults || isEq) {
+      add("               setResult(" + isEq + ");\n");
+    }
+    add("               return;\n");
 
     add("             }\n");
-    if (!expectMultipleResults) {
-      add("             setResult(false);\n");
-    }
+
     add("           }\n");
+    if (!expectMultipleResults || !isEq) {
+      add("           setResult(" + !isEq + ");\n");
+    }
+    add("         }\n");
 
-    add("         };\n" + "       }\n" + "    }\n\n");
+    add("       };\n" + "    }\n\n");
   }
 
   private void addUdpBegin() throws IOException {
diff --git 
a/parquet-hadoop/src/test/java/org/apache/parquet/filter2/recordlevel/TestRecordLevelFilters.java
 
b/parquet-hadoop/src/test/java/org/apache/parquet/filter2/recordlevel/TestRecordLevelFilters.java
index 0c03f548b..c69dce691 100644
--- 
a/parquet-hadoop/src/test/java/org/apache/parquet/filter2/recordlevel/TestRecordLevelFilters.java
+++ 
b/parquet-hadoop/src/test/java/org/apache/parquet/filter2/recordlevel/TestRecordLevelFilters.java
@@ -44,7 +44,9 @@ import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.LongStream;
 import org.apache.parquet.example.data.Group;
 import org.apache.parquet.filter2.compat.FilterCompat;
@@ -156,7 +158,11 @@ public class TestRecordLevelFilters {
   }
 
   private static void assertPredicate(FilterPredicate predicate, long... 
expectedIds) throws IOException {
-    List<Group> found = PhoneBookWriter.readFile(phonebookFile, 
FilterCompat.get(predicate));
+    assertPredicate(phonebookFile, predicate, expectedIds);
+  }
+
+  private static void assertPredicate(File file, FilterPredicate predicate, 
long... expectedIds) throws IOException {
+    List<Group> found = PhoneBookWriter.readFile(file, 
FilterCompat.get(predicate));
 
     assertEquals(expectedIds.length, found.size());
     for (int i = 0; i < expectedIds.length; i++) {
@@ -175,6 +181,35 @@ public class TestRecordLevelFilters {
     });
   }
 
+  @Test
+  public void testNotInChecksAllValues() throws Exception {
+    File file = PhoneBookWriter.writeToFile(List.of(
+        new User(300, "lon-1", null, new Location(1.0, null)),
+        new User(301, "lon-2", null, new Location(2.0, null)),
+        new User(302, "lon-3", null, new Location(3.0, null))));
+    DoubleColumn lon = doubleColumn("location.lon");
+
+    Set<Double> values = new LinkedHashSet<>();
+    values.add(1.0);
+    values.add(3.0);
+    assertPredicate(file, notIn(lon, values), 301L);
+  }
+
+  @Test
+  public void testInAndNotInCheckNullAndNonNullValues() throws Exception {
+    File file = PhoneBookWriter.writeToFile(List.of(
+        new User(300, "lon-null", null, new Location(null, null)),
+        new User(301, "lon-2", null, new Location(2.0, null)),
+        new User(302, "lon-3", null, new Location(3.0, null))));
+    DoubleColumn lon = doubleColumn("location.lon");
+
+    Set<Double> values = new LinkedHashSet<>();
+    values.add(null);
+    values.add(3.0);
+    assertPredicate(file, in(lon, values), 300L, 302L);
+    assertPredicate(file, notIn(lon, values), 301L);
+  }
+
   @Test
   public void testAllFilter() throws Exception {
     BinaryColumn name = binaryColumn("name");

Reply via email to