diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index f0dc70c0f1c9..4d3ac54359e3 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -1235,9 +1235,19 @@ private void substituteSubQuery(Blackboard bb, SubQuery subQuery) {
// SET empno = 1 WHERE emp.empno IN (
// SELECT emp.empno FROM emp WHERE emp.empno = 2)
//
- // In such case, when converting SqlUpdate#condition, bb.root is null
- // and it makes no sense to do the sub-query substitution.
- if (bb.root == null) {
+ // In such case, when converting SqlUpdate#condition, the bb.root is null,
+ // but we should skip the expr like 'ON (...) AND (...) IN (...) within JOIN'
+ // because in this case, bb.root can also be null.
+ // for example:
+ // "SELECT e1.empno FROM emps as e1
+ // LEFT JOIN depts as d1
+ // ON e1.deptno=d1.deptno
+ // AND e1.deptno IN (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)"
+ // note: if we use 'WHERE e1.deptno IN (...)',
+ // the bb.root is not null, doesn't apply to the case here
+ //
+ // and SqlUpdate#condition makes no sense to do the sub-query substitution here, return it.
+ if (bb.root == null && !(bb.scope.getNode() instanceof SqlJoin)) {
return;
}
final RelDataType targetRowType =
@@ -5013,7 +5023,6 @@ public RexNode register(
requireNonNull(joinType, "joinType");
registered.add(new RegisterArgs(rel, joinType, leftKeys));
if (root == null) {
- assert leftKeys == null : "leftKeys must be null";
setRoot(rel, false);
return rexBuilder.makeRangeReference(
root().getRowType(),
diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index a38ebf0b3be2..0af4d8cfa67a 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -4934,6 +4934,30 @@ void checkUserDefinedOrderByOver(NullCollation nullCollation) {
.convertsTo("${planNotExpanded}");
}
+ /**
+ * Test case for
+ * [CALCITE-6028]
+ * SqlToRelConverter#substituteSubQuery gives NullPointerException when convert
+ * expr JOIN ... ON ... AND ... IN and if size of IN exceeds IN_SUBQUERY_THRESHOLD.
+ */
+ @Test void testInSubQueryWithinJoin0() {
+ String sql = "SELECT t1.x FROM (values (1, 'a')) as t1(x, y)\n"
+ + "left join (values (1, 'b')) as t2(x, y)\n"
+ + "ON t1.x=t2.x AND t1.x IN (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)";
+ sql(sql).ok();
+ }
+
+ /**
+ * As {@link #testInSubQueryWithinJoin0()} but value size of IN < 20.
+ */
+ @Test void testInSubQueryWithinJoin1() {
+ String sql = "SELECT t1.x FROM (values (1, 'a')) as t1(x, y)\n"
+ + "left join (values (1, 'b')) as t2(x, y)\n"
+ + "ON t1.x=t2.x AND t1.x IN (1,2,3,4,5)";
+ sql(sql).ok();
+ }
+
+
/**
* Test case for
* [CALCITE-4295]
diff --git a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index b914e14dc749..5564473fade4 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -2779,6 +2779,40 @@ FROM dept, emp WHERE emp.deptno = dept.deptno AND emp.sal < (
)]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
20
+SELECT e1.empno FROM emp as e1 LEFT JOIN dept as d1 ON e1.deptno=d1.deptno
+WHERE e1.deptno IN (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21);
++-------+
+| EMPNO |
++-------+
+| 7369 |
+| 7566 |
+| 7782 |
+| 7788 |
+| 7839 |
+| 7876 |
+| 7902 |
+| 7934 |
++-------+
+(8 rows)
+
+!ok
+
# End sub-query.iq