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()));