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 b872137b841a..8e927896907f 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java @@ -4940,6 +4940,40 @@ ImmutableList retrieveCursors() { } } + /** Tries to simplify case: + * select t1.a FROM emps t1 JOIN emps t2 ON (SELECT t2.a) _compare opeartor_ val; + * The idea is to transform such a case into equivalent: + * select t1.a FROM emps t1 JOIN emps t2 ON t2.a _compare opeartor_ val; + */ + private @Nullable RexNode simplifyOnWithSelectInConditions(SqlNode expr) { + if (!(expr instanceof SqlBasicCall)) { + return null; + } + // (select t2.a) _compare opeartor_ val + SqlBasicCall call0 = (SqlBasicCall) expr; + if (call0.operandCount() != 2 || !(call0.operand(0) instanceof SqlBasicCall)) { + return null; + } + // left side i.e. (select t2.a) + SqlBasicCall selectCall = call0.operand(0); + if (selectCall.operandCount() != 1 || !(selectCall.getOperands()[0] instanceof SqlSelect)) { + return null; + } + // select t2.a + @Nullable SqlSelect colExpr = (SqlSelect) selectCall.getOperands()[0]; + // empty from case, only 1 operand in current case possible + if (colExpr == null || colExpr.getFrom() != null || colExpr.getSelectList().size() != 1) { + return null; + } + // a + SqlNode selectParam = colExpr.getSelectList().get(0); + SqlNode op2 = call0.operand(1); + RexNode exp0 = convertExpression(selectParam); + RexNode exp1 = convertExpression(op2); + RexNode rex0 = rexBuilder.makeCall(call0.getOperator(), exp0, exp1); + return rex0; + } + @Override public RexNode convertExpression(SqlNode expr) { // If we're in aggregation mode and this is an expression in the // GROUP BY clause, return a reference to the field. @@ -4974,6 +5008,16 @@ ImmutableList retrieveCursors() { final SqlNode query; final RelRoot root; switch (kind) { + case LESS_THAN: + case GREATER_THAN: + case LESS_THAN_OR_EQUAL: + case GREATER_THAN_OR_EQUAL: + case EQUALS: + RexNode res = simplifyOnWithSelectInConditions(expr); + if (res != null) { + return res; + } + break; case IN: case NOT_IN: case SOME: diff --git a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCorrelateTest.java b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCorrelateTest.java index 11820079b586..4a089b1b2df3 100644 --- a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCorrelateTest.java +++ b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCorrelateTest.java @@ -168,6 +168,102 @@ class EnumerableCorrelateTest { "empid=200"); } + /** Test case for + * [CALCITE-4833] + * Complex nested correlated subquery failed. + */ + @Test void complexNestedCorrelatedSubquery() { + String sql = "SELECT t1.empid FROM emps t1 LEFT JOIN emps t2 ON (SELECT t2.empid)<=101"; + + tester(false, new HrSchema()) + .query(sql) + .explainContains("" + + "EnumerableCalc(expr#0..1=[{inputs}], empid=[$t0])\n" + + " EnumerableNestedLoopJoin(condition=[true], joinType=[left])\n" + + " EnumerableCalc(expr#0..4=[{inputs}], empid=[$t0])\n" + + " EnumerableTableScan(table=[[s, emps]])\n" + + " EnumerableCalc(expr#0..4=[{inputs}], expr#5=[101], expr#6=[<=($t0, $t5)], $f5=[$t6], $condition=[$t6])\n" + + " EnumerableTableScan(table=[[s, emps]])\n\n") + .returnsUnordered( + "empid=100", + "empid=110", + "empid=150", + "empid=200"); + + sql = "SELECT t1.empid FROM emps t1 LEFT JOIN emps t2 ON (SELECT t2.empid)<101"; + + tester(false, new HrSchema()) + .query(sql) + .explainContains("" + + "EnumerableCalc(expr#0..1=[{inputs}], empid=[$t0])\n" + + " EnumerableNestedLoopJoin(condition=[true], joinType=[left])\n" + + " EnumerableCalc(expr#0..4=[{inputs}], empid=[$t0])\n" + + " EnumerableTableScan(table=[[s, emps]])\n" + + " EnumerableCalc(expr#0..4=[{inputs}], expr#5=[101], expr#6=[<($t0, $t5)], $f5=[$t6], $condition=[$t6])\n" + + " EnumerableTableScan(table=[[s, emps]])\n\n") + .returnsUnordered( + "empid=100", + "empid=110", + "empid=150", + "empid=200"); + + sql = "SELECT t1.empid FROM emps t1 LEFT JOIN emps t2 ON (SELECT t2.empid)>150"; + + tester(false, new HrSchema()) + .query(sql) + .explainContains("" + + "EnumerableCalc(expr#0..1=[{inputs}], empid=[$t0])\n" + + " EnumerableNestedLoopJoin(condition=[true], joinType=[left])\n" + + " EnumerableCalc(expr#0..4=[{inputs}], empid=[$t0])\n" + + " EnumerableTableScan(table=[[s, emps]])\n" + + " EnumerableCalc(expr#0..4=[{inputs}], expr#5=[150], expr#6=[>($t0, $t5)], $f5=[$t6], $condition=[$t6])\n" + + " EnumerableTableScan(table=[[s, emps]])\n\n") + .returnsUnordered( + "empid=100", + "empid=110", + "empid=150", + "empid=200"); + + sql = "SELECT t1.empid FROM emps t1 LEFT JOIN emps t2 ON (SELECT t2.empid)=150"; + + tester(false, new HrSchema()) + .query(sql) + .explainContains("" + + "EnumerableCalc(expr#0..1=[{inputs}], empid=[$t0])\n" + + " EnumerableNestedLoopJoin(condition=[true], joinType=[left])\n" + + " EnumerableCalc(expr#0..4=[{inputs}], empid=[$t0])\n" + + " EnumerableTableScan(table=[[s, emps]])\n" + + " EnumerableCalc(expr#0..4=[{inputs}], expr#5=[150], expr#6=[=($t0, $t5)], empid=[$t0], $condition=[$t6])\n" + + " EnumerableTableScan(table=[[s, emps]])\n\n") + .returnsUnordered( + "empid=100", + "empid=110", + "empid=150", + "empid=200"); + + sql = "SELECT t1.empid FROM emps t1 LEFT JOIN emps t2 ON (SELECT t2.empid)=(select 100 + 50)"; + + tester(false, new HrSchema()) + .query(sql) + .explainContains("" + + "EnumerableCalc(expr#0..2=[{inputs}], empid=[$t0])\n" + + " EnumerableNestedLoopJoin(condition=[true], joinType=[left])\n" + + " EnumerableCalc(expr#0..4=[{inputs}], empid=[$t0])\n" + + " EnumerableTableScan(table=[[s, emps]])\n" + + " EnumerableCalc(expr#0..1=[{inputs}], expr#2=[CAST($t0):INTEGER], empid=[$t1], $f0=[$t2])\n" + + " EnumerableHashJoin(condition=[=($0, $1)], joinType=[inner])\n" + + " EnumerableAggregate(group=[{}], agg#0=[SINGLE_VALUE($0)])\n" + + " EnumerableCalc(expr#0=[{inputs}], expr#1=[100], expr#2=[50], expr#3=[+($t1, $t2)], EXPR$0=[$t3])\n" + + " EnumerableValues(tuples=[[{ 0 }]])\n" + + " EnumerableCalc(expr#0..4=[{inputs}], empid=[$t0])\n" + + " EnumerableTableScan(table=[[s, emps]])\n\n") + .returnsUnordered( + "empid=100", + "empid=110", + "empid=150", + "empid=200"); + } + /** Test case for * [CALCITE-2920] * RelBuilder: new method to create an anti-join. */