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

jinwoo pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new b36cad49db GEODE-10526 - IndexTrackingQueryObserver.afterIndexLookup() 
throws NullPointerException when indexMap ThreadLocal is uninitialized in 
partitioned region queries (#7960)
b36cad49db is described below

commit b36cad49db4ccce3dc3da837b52d0775fe93e14b
Author: Leon Finker <[email protected]>
AuthorDate: Sun Nov 23 12:22:00 2025 -0500

    GEODE-10526 - IndexTrackingQueryObserver.afterIndexLookup() throws 
NullPointerException when indexMap ThreadLocal is uninitialized in partitioned 
region queries (#7960)
    
    Co-authored-by: Leon Finker <[email protected]>
---
 .../index/IndexTrackingQueryObserverJUnitTest.java | 56 ++++++++++++++++++++++
 .../query/internal/IndexTrackingQueryObserver.java |  9 ++++
 2 files changed, 65 insertions(+)

diff --git 
a/geode-core/src/integrationTest/java/org/apache/geode/cache/query/internal/index/IndexTrackingQueryObserverJUnitTest.java
 
b/geode-core/src/integrationTest/java/org/apache/geode/cache/query/internal/index/IndexTrackingQueryObserverJUnitTest.java
index bd7107e9c2..fc498dcd0c 100644
--- 
a/geode-core/src/integrationTest/java/org/apache/geode/cache/query/internal/index/IndexTrackingQueryObserverJUnitTest.java
+++ 
b/geode-core/src/integrationTest/java/org/apache/geode/cache/query/internal/index/IndexTrackingQueryObserverJUnitTest.java
@@ -17,7 +17,9 @@ package org.apache.geode.cache.query.internal.index;
 import static org.apache.geode.cache.Region.SEPARATOR;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
+import java.util.ArrayList;
 import java.util.Collection;
 
 import org.junit.After;
@@ -163,4 +165,58 @@ public class IndexTrackingQueryObserverJUnitTest {
     assertEquals(results.size(), ((Integer) rslts).intValue());
   }
 
+  /**
+   * Test for GEODE-10526: afterIndexLookup should handle null indexMap 
gracefully
+   *
+   * This test verifies that afterIndexLookup does not throw 
NullPointerException
+   * when the ThreadLocal indexMap has not been initialized. This can occur in
+   * partitioned region queries when afterIndexLookup is called without a
+   * corresponding beforeIndexLookup call, or when beforeIndexLookup fails
+   * before initializing the ThreadLocal.
+   */
+  @Test
+  public void testAfterIndexLookupWithUninitializedThreadLocal() {
+    // Create a new IndexTrackingQueryObserver without initializing its 
ThreadLocal
+    IndexTrackingQueryObserver observer = new IndexTrackingQueryObserver();
+
+    // Create a mock result collection
+    Collection<Object> results = new ArrayList<>();
+    results.add(new Object());
+
+    try {
+      // Call afterIndexLookup without calling beforeIndexLookup first
+      // This simulates the scenario where the ThreadLocal is not initialized
+      // Before the fix, this would throw NullPointerException at line 110
+      observer.afterIndexLookup(results);
+
+      // If we reach here, the fix is working correctly
+      // The method should return gracefully when indexMap is null
+    } catch (NullPointerException e) {
+      fail("GEODE-10526: afterIndexLookup should not throw 
NullPointerException when "
+          + "ThreadLocal is uninitialized. This indicates the null check is 
missing. "
+          + "Exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test for GEODE-10526: afterIndexLookup should handle null results 
parameter
+   *
+   * Verify that the existing null check for results parameter still works.
+   */
+  @Test
+  public void testAfterIndexLookupWithNullResults() {
+    IndexTrackingQueryObserver observer = new IndexTrackingQueryObserver();
+
+    try {
+      // Call afterIndexLookup with null results
+      // This should return early without any exceptions
+      observer.afterIndexLookup(null);
+
+      // Success - method handled null results correctly
+    } catch (Exception e) {
+      fail("afterIndexLookup should handle null results parameter gracefully. "
+          + "Exception: " + e.getMessage());
+    }
+  }
+
 }
diff --git 
a/geode-core/src/main/java/org/apache/geode/cache/query/internal/IndexTrackingQueryObserver.java
 
b/geode-core/src/main/java/org/apache/geode/cache/query/internal/IndexTrackingQueryObserver.java
index 71b9c10e49..a6abb50e06 100644
--- 
a/geode-core/src/main/java/org/apache/geode/cache/query/internal/IndexTrackingQueryObserver.java
+++ 
b/geode-core/src/main/java/org/apache/geode/cache/query/internal/IndexTrackingQueryObserver.java
@@ -105,6 +105,15 @@ public class IndexTrackingQueryObserver extends 
QueryObserverAdapter {
     // append the size of the lookup results (and bucket id if its an Index on 
bucket)
     // to IndexInfo results Map.
     Map indexMap = (Map) indexInfo.get();
+
+    // Guard against uninitialized ThreadLocal in partitioned queries
+    if (indexMap == null) {
+      // beforeIndexLookup was not called or did not complete successfully.
+      // This can occur in partitioned region query execution across buckets
+      // when exceptions occur or when query execution paths bypass 
beforeIndexLookup.
+      return;
+    }
+
     Index index = (Index) lastIndexUsed.get();
     if (index != null) {
       IndexInfo indexInfo = (IndexInfo) indexMap.get(getIndexName(index, 
lastKeyUsed.get()));

Reply via email to