opensearch-project / opensearch-project/sql
[BUG] LEFT OUTER JOIN causes a NPE when left hand side join field is null
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 176
- Forks
- 229
- Avg merge
- 2d 21h
- Merged PRs (30d)
- 43
Description
What is the bug?
LEFT OUTER JOIN causes a NPE when left hand side join field is null. After removing ORDER BY, the error does not present
How can one reproduce the bug?
Execute the below code in the devtools window
DELETE /entity-a-idx
PUT /entity-a-idx
{
"settings": {
"index": {
"number_of_shards": 2,
"number_of_replicas": 1
}
},
"mappings": {
"properties": {
"product": {
"type": "keyword"
},
"category": {
"type": "keyword"
}
}
}
}
POST entity-a-idx/_create/1
{
"product": "Apples",
"category": "Fruit"
}
POST entity-a-idx/_create/2
{
"product": "Oranges",
"category": "Fruit"
}
POST entity-a-idx/_create/3
{
"product": "Potato",
"category": null
}
DELETE /entity-b-idx
PUT /entity-b-idx
{
"settings": {
"index": {
"number_of_shards": 2,
"number_of_replicas": 1
}
},
"mappings": {
"properties": {
"category": {
"type": "keyword"
}
}
}
}
POST entity-b-idx/_create/1
{
"category": "Fruit"
}
POST _plugins/_sql/
{
"query":"SELECT a.product, a.category, b.category from entity-a-idx as a left outer join entity-b-idx as b on a.category=b.category order by a.category"
}
What is the expected behavior?
No error is raised, right hand side is empty
What is your host/environment?
- Windows
- 3.1
- SQL Plugin
Do you have any additional context?
[2025-09-22T08:57:06,373][WARN ][o.o.s.l.e.f.PrettyFormatRestExecutor] [opensearch-node1] Error happened in pretty formatter
java.lang.IllegalStateException: Error occurred during join query run
at org.opensearch.sql.legacy.executor.join.ElasticJoinExecutor.run(ElasticJoinExecutor.java:97) ~[?:?]
at org.opensearch.sql.legacy.executor.QueryActionElasticExecutor.executeJoinSearchAction(QueryActionElasticExecutor.java:47) ~[?:?]
at org.opensearch.sql.legacy.executor.QueryActionElasticExecutor.executeAnyAction(QueryActionElasticExecutor.java:104) ~[?:?]
at org.opensearch.sql.legacy.executor.format.PrettyFormatRestExecutor.execute(PrettyFormatRestExecutor.java:72) ~[?:?]
at org.opensearch.sql.legacy.executor.format.PrettyFormatRestExecutor.execute(PrettyFormatRestExecutor.java:46) ~[?:?]
at org.opensearch.sql.legacy.executor.AsyncRestExecutor.doExecuteWithTimeMeasured(AsyncRestExecutor.java:154) ~[?:?]
at org.opensearch.sql.legacy.executor.AsyncRestExecutor.lambda$async$1(AsyncRestExecutor.java:110) ~[?:?]
at org.opensearch.sql.common.utils.QueryContext.lambda$withCurrentContext$0(QueryContext.java:61) ~[?:?]
at org.opensearch.common.util.concurrent.ThreadContext$ContextPreservingRunnable.run(ThreadContext.java:916) ~[opensearch-3.1.0.jar:3.1.0]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[?:?]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[?:?]
at java.base/java.lang.Thread.run(Thread.java:1583) [?:?]
Caused by: java.lang.IllegalStateException: Error happened during execution
at org.opensearch.sql.legacy.query.planner.physical.PhysicalPlan.execute(PhysicalPlan.java:60) ~[?:?]
at org.opensearch.sql.legacy.query.planner.core.QueryPlanner.execute(QueryPlanner.java:67) ~[?:?]
at org.opensearch.sql.legacy.executor.join.QueryPlanElasticExecutor.innerRun(QueryPlanElasticExecutor.java:30) ~[?:?]
at org.opensearch.sql.legacy.executor.join.ElasticJoinExecutor.run(ElasticJoinExecutor.java:92) ~[?:?]
... 11 more
Caused by: java.lang.IllegalStateException: Failed to prefetch next batch
at org.opensearch.sql.legacy.query.planner.physical.node.BatchPhysicalOperator.prefetchSafely(BatchPhysicalOperator.java:74) ~[?:?]
at org.opensearch.sql.legacy.query.planner.physical.node.BatchPhysicalOperator.hasNext(BatchPhysicalOperator.java:47) ~[?:?]
at org.opensearch.sql.legacy.query.planner.logical.node.Top.hasNext(Top.java:41) ~[?:?]
at org.opensearch.sql.legacy.query.planner.logical.node.Project.hasNext(Project.java:78) ~[?:?]
at org.opensearch.sql.legacy.query.planner.physical.PhysicalPlan.doExecutePlan(PhysicalPlan.java:76) ~[?:?]
at org.opensearch.sql.legacy.query.planner.physical.PhysicalPlan.execute(PhysicalPlan.java:56) ~[?:?]
at org.opensearch.sql.legacy.query.planner.core.QueryPlanner.execute(QueryPlanner.java:67) ~[?:?]
at org.opensearch.sql.legacy.executor.join.QueryPlanElasticExecutor.innerRun(QueryPlanElasticExecutor.java:30) ~[?:?]
at org.opensearch.sql.legacy.executor.join.ElasticJoinExecutor.run(ElasticJoinExecutor.java:92) ~[?:?]
... 11 more
Caused by: java.lang.NullPointerException: Cannot invoke "java.lang.Comparable.compareTo(Object)" because the return value of "java.util.function.Function.apply(Object)" is null
at java.base/java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:473) ~[?:?]
at java.base/java.util.TimSort.countRunAndMakeAscending(TimSort.java:360) ~[?:?]
at java.base/java.util.TimSort.sort(TimSort.java:220) ~[?:?]
at java.base/java.util.Arrays.sort(Arrays.java:1308) ~[?:?]
at java.base/java.util.ArrayList.sort(ArrayList.java:1804) ~[?:?]
at org.opensearch.sql.legacy.query.planner.physical.node.sort.QuickSort.prefetch(QuickSort.java:75) ~[?:?]
at org.opensearch.sql.legacy.query.planner.physical.node.BatchPhysicalOperator.prefetchSafely(BatchPhysicalOperator.java:72) ~[?:?]
at org.opensearch.sql.legacy.query.planner.physical.node.BatchPhysicalOperator.hasNext(BatchPhysicalOperator.java:47) ~[?:?]
at org.opensearch.sql.legacy.query.planner.logical.node.Top.hasNext(Top.java:41) ~[?:?]
at org.opensearch.sql.legacy.query.planner.logical.node.Project.hasNext(Project.java:78) ~[?:?]
at org.opensearch.sql.legacy.query.planner.physical.PhysicalPlan.doExecutePlan(PhysicalPlan.java:76) ~[?:?]
at org.opensearch.sql.legacy.query.planner.physical.PhysicalPlan.execute(PhysicalPlan.java:56) ~[?:?]
at org.opensearch.sql.legacy.query.planner.core.QueryPlanner.execute(QueryPlanner.java:67) ~[?:?]
at org.opensearch.sql.legacy.executor.join.QueryPlanElasticExecutor.innerRun(QueryPlanElasticExecutor.java:30) ~[?:?]
at org.opensearch.sql.legacy.executor.join.ElasticJoinExecutor.run(ElasticJoinExecutor.java:92) ~[?:?]
... 11 more
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the SQL join from the issue, then inspect org.opensearch.sql.legacy.query.planner.physical.node.sort.QuickSort, where the stack trace identifies the failure. Verify the behavior with a null left-hand join field and ORDER BY; done means the query completes without a NullPointerException and returns an empty right-hand value for that row.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, sql
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100