You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by ru...@apache.org on 2020/02/12 09:27:44 UTC
[calcite] branch master updated: [CALCITE-3783]
PruneEmptyRules#JOIN_RIGHT_INSTANCE wrong behavior for JoinRelType.ANTI
This is an automated email from the ASF dual-hosted git repository.
rubenql pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/master by this push:
new 10a5b8a [CALCITE-3783] PruneEmptyRules#JOIN_RIGHT_INSTANCE wrong behavior for JoinRelType.ANTI
10a5b8a is described below
commit 10a5b8a89d319e6fed563e7e49518cfc960b93d6
Author: rubenada <ru...@gmail.com>
AuthorDate: Tue Feb 11 15:00:09 2020 +0100
[CALCITE-3783] PruneEmptyRules#JOIN_RIGHT_INSTANCE wrong behavior for JoinRelType.ANTI
---
.../apache/calcite/rel/rules/PruneEmptyRules.java | 15 ++
.../org/apache/calcite/test/RelOptRulesTest.java | 245 ++++++++++++++++++++-
.../org/apache/calcite/test/RelOptRulesTest.xml | 213 +++++++++++++++++-
3 files changed, 464 insertions(+), 9 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java b/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java
index 5e68a6f..a8838d8 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java
@@ -26,6 +26,7 @@ import org.apache.calcite.rel.SingleRel;
import org.apache.calcite.rel.core.Aggregate;
import org.apache.calcite.rel.core.Filter;
import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
import org.apache.calcite.rel.core.Project;
import org.apache.calcite.rel.core.RelFactories;
import org.apache.calcite.rel.core.Sort;
@@ -286,6 +287,9 @@ public abstract class PruneEmptyRules {
*
* <ul>
* <li>Join(Empty, Scan(Dept), INNER) becomes Empty
+ * <li>Join(Empty, Scan(Dept), LEFT) becomes Empty
+ * <li>Join(Empty, Scan(Dept), SEMI) becomes Empty
+ * <li>Join(Empty, Scan(Dept), ANTI) becomes Empty
* </ul>
*/
public static final RelOptRule JOIN_LEFT_INSTANCE =
@@ -314,6 +318,9 @@ public abstract class PruneEmptyRules {
*
* <ul>
* <li>Join(Scan(Emp), Empty, INNER) becomes Empty
+ * <li>Join(Scan(Emp), Empty, RIGHT) becomes Empty
+ * <li>Join(Scan(Emp), Empty, SEMI) becomes Empty
+ * <li>Join(Scan(Emp), Empty, ANTI) becomes Scan(Emp)
* </ul>
*/
public static final RelOptRule JOIN_RIGHT_INSTANCE =
@@ -330,6 +337,14 @@ public abstract class PruneEmptyRules {
// dept is empty
return;
}
+ if (join.getJoinType() == JoinRelType.ANTI) {
+ // "select * from emp anti join dept" is not necessarily empty if dept is empty
+ if (join.analyzeCondition().isEqui()) {
+ // In case of anti (equi) join: Join(X, Empty, ANTI) becomes X
+ call.transformTo(join.getLeft());
+ }
+ return;
+ }
call.transformTo(call.builder().push(join).empty().build());
}
};
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index 01bcff8..6b02ab4 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -3040,7 +3040,7 @@ public class RelOptRulesTest extends RelOptTestBase {
sql(sql).with(program).check();
}
- @Test public void testEmptyJoin() {
+ @Test public void testLeftEmptyInnerJoin() {
HepProgram program = new HepProgramBuilder()
.addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
.addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE)
@@ -3055,7 +3055,7 @@ public class RelOptRulesTest extends RelOptTestBase {
sql(sql).with(program).check();
}
- @Test public void testEmptyJoinLeft() {
+ @Test public void testLeftEmptyLeftJoin() {
HepProgram program = new HepProgramBuilder()
.addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
.addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE)
@@ -3070,7 +3070,7 @@ public class RelOptRulesTest extends RelOptTestBase {
sql(sql).with(program).check();
}
- @Test public void testEmptyJoinRight() {
+ @Test public void testLeftEmptyRightJoin() {
HepProgram program = new HepProgramBuilder()
.addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
.addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE)
@@ -3078,7 +3078,7 @@ public class RelOptRulesTest extends RelOptTestBase {
.addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE)
.build();
- // Plan should be equivalent to "select * from emp join dept".
+ // Plan should be equivalent to "select * from emp right join dept".
// Cannot optimize away the join because of RIGHT.
final String sql = "select * from (\n"
+ " select * from emp where false) e\n"
@@ -3086,6 +3086,243 @@ public class RelOptRulesTest extends RelOptTestBase {
sql(sql).with(program).check();
}
+ @Test public void testLeftEmptyFullJoin() {
+ HepProgram program = new HepProgramBuilder()
+ .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_LEFT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE)
+ .build();
+
+ // Plan should be equivalent to "select * from emp full join dept".
+ // Cannot optimize away the join because of FULL.
+ final String sql = "select * from (\n"
+ + " select * from emp where false) e\n"
+ + "full join dept d on e.deptno = d.deptno";
+ sql(sql).with(program).check();
+ }
+
+ @Test public void testLeftEmptySemiJoin() {
+ final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build());
+ final RelNode relNode = relBuilder
+ .scan("EMP").empty()
+ .scan("DEPT")
+ .semiJoin(relBuilder
+ .equals(
+ relBuilder.field(2, 0, "DEPTNO"),
+ relBuilder.field(2, 1, "DEPTNO")))
+ .project(relBuilder.field("EMPNO"))
+ .build();
+
+ HepProgram program = new HepProgramBuilder()
+ .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_LEFT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE)
+ .build();
+
+ final HepPlanner hepPlanner = new HepPlanner(program);
+ hepPlanner.setRoot(relNode);
+ final RelNode output = hepPlanner.findBestExp();
+
+ final String planBefore = NL + RelOptUtil.toString(relNode);
+ final String planAfter = NL + RelOptUtil.toString(output);
+ final DiffRepository diffRepos = getDiffRepos();
+ diffRepos.assertEquals("planBefore", "${planBefore}", planBefore);
+ // Plan should be empty
+ diffRepos.assertEquals("planAfter", "${planAfter}", planAfter);
+ }
+
+ @Test public void testLeftEmptyAntiJoin() {
+ final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build());
+ final RelNode relNode = relBuilder
+ .scan("EMP").empty()
+ .scan("DEPT")
+ .antiJoin(relBuilder
+ .equals(
+ relBuilder.field(2, 0, "DEPTNO"),
+ relBuilder.field(2, 1, "DEPTNO")))
+ .project(relBuilder.field("EMPNO"))
+ .build();
+
+ HepProgram program = new HepProgramBuilder()
+ .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_LEFT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE)
+ .build();
+
+ final HepPlanner hepPlanner = new HepPlanner(program);
+ hepPlanner.setRoot(relNode);
+ final RelNode output = hepPlanner.findBestExp();
+
+ final String planBefore = NL + RelOptUtil.toString(relNode);
+ final String planAfter = NL + RelOptUtil.toString(output);
+ final DiffRepository diffRepos = getDiffRepos();
+ diffRepos.assertEquals("planBefore", "${planBefore}", planBefore);
+ // Plan should be empty
+ diffRepos.assertEquals("planAfter", "${planAfter}", planAfter);
+ }
+
+ @Test public void testRightEmptyInnerJoin() {
+ HepProgram program = new HepProgramBuilder()
+ .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_LEFT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE)
+ .build();
+
+ // Plan should be empty
+ final String sql = "select * from emp e\n"
+ + "join (select * from dept where false) as d\n"
+ + "on e.deptno = d.deptno";
+ sql(sql).with(program).check();
+ }
+
+ @Test public void testRightEmptyLeftJoin() {
+ HepProgram program = new HepProgramBuilder()
+ .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_LEFT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE)
+ .build();
+
+ // Plan should be equivalent to "select * from emp left join dept".
+ // Cannot optimize away the join because of LEFT.
+ final String sql = "select * from emp e\n"
+ + "left join (select * from dept where false) as d\n"
+ + "on e.deptno = d.deptno";
+ sql(sql).with(program).check();
+ }
+
+ @Test public void testRightEmptyRightJoin() {
+ HepProgram program = new HepProgramBuilder()
+ .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_LEFT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE)
+ .build();
+
+ // Plan should be empty
+ final String sql = "select * from emp e\n"
+ + "right join (select * from dept where false) as d\n"
+ + "on e.deptno = d.deptno";
+ sql(sql).with(program).check();
+ }
+
+ @Test public void testRightEmptyFullJoin() {
+ HepProgram program = new HepProgramBuilder()
+ .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_LEFT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE)
+ .build();
+
+ // Plan should be equivalent to "select * from emp full join dept".
+ // Cannot optimize away the join because of FULL.
+ final String sql = "select * from emp e\n"
+ + "full join (select * from dept where false) as d\n"
+ + "on e.deptno = d.deptno";
+ sql(sql).with(program).check();
+ }
+
+ @Test public void testRightEmptySemiJoin() {
+ final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build());
+ final RelNode relNode = relBuilder
+ .scan("EMP")
+ .scan("DEPT").empty()
+ .semiJoin(relBuilder
+ .equals(
+ relBuilder.field(2, 0, "DEPTNO"),
+ relBuilder.field(2, 1, "DEPTNO")))
+ .project(relBuilder.field("EMPNO"))
+ .build();
+
+ HepProgram program = new HepProgramBuilder()
+ .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_LEFT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE)
+ .build();
+
+ final HepPlanner hepPlanner = new HepPlanner(program);
+ hepPlanner.setRoot(relNode);
+ final RelNode output = hepPlanner.findBestExp();
+
+ final String planBefore = NL + RelOptUtil.toString(relNode);
+ final String planAfter = NL + RelOptUtil.toString(output);
+ final DiffRepository diffRepos = getDiffRepos();
+ diffRepos.assertEquals("planBefore", "${planBefore}", planBefore);
+ // Plan should be empty
+ diffRepos.assertEquals("planAfter", "${planAfter}", planAfter);
+ }
+
+ @Test public void testRightEmptyAntiJoin() {
+ final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build());
+ final RelNode relNode = relBuilder
+ .scan("EMP")
+ .scan("DEPT").empty()
+ .antiJoin(relBuilder
+ .equals(
+ relBuilder.field(2, 0, "DEPTNO"),
+ relBuilder.field(2, 1, "DEPTNO")))
+ .project(relBuilder.field("EMPNO"))
+ .build();
+
+ HepProgram program = new HepProgramBuilder()
+ .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_LEFT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE)
+ .build();
+
+ final HepPlanner hepPlanner = new HepPlanner(program);
+ hepPlanner.setRoot(relNode);
+ final RelNode output = hepPlanner.findBestExp();
+
+ final String planBefore = NL + RelOptUtil.toString(relNode);
+ final String planAfter = NL + RelOptUtil.toString(output);
+ final DiffRepository diffRepos = getDiffRepos();
+ diffRepos.assertEquals("planBefore", "${planBefore}", planBefore);
+ // Plan should be scan("EMP") (i.e. join's left child)
+ diffRepos.assertEquals("planAfter", "${planAfter}", planAfter);
+ }
+
+ @Test public void testRightEmptyAntiJoinNonEqui() {
+ final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build());
+ final RelNode relNode = relBuilder
+ .scan("EMP")
+ .scan("DEPT").empty()
+ .antiJoin(relBuilder
+ .equals(
+ relBuilder.field(2, 0, "DEPTNO"),
+ relBuilder.field(2, 1, "DEPTNO")),
+ relBuilder
+ .equals(
+ relBuilder.field(2, 0, "SAL"),
+ relBuilder.literal(2000)))
+ .project(relBuilder.field("EMPNO"))
+ .build();
+
+ HepProgram program = new HepProgramBuilder()
+ .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_LEFT_INSTANCE)
+ .addRuleInstance(PruneEmptyRules.JOIN_RIGHT_INSTANCE)
+ .build();
+
+ final HepPlanner hepPlanner = new HepPlanner(program);
+ hepPlanner.setRoot(relNode);
+ final RelNode output = hepPlanner.findBestExp();
+
+ final String planBefore = NL + RelOptUtil.toString(relNode);
+ final String planAfter = NL + RelOptUtil.toString(output);
+ final DiffRepository diffRepos = getDiffRepos();
+ diffRepos.assertEquals("planBefore", "${planBefore}", planBefore);
+ // Cannot optimize away the join because it is not equi join.
+ diffRepos.assertEquals("planAfter", "${planAfter}", planAfter);
+ }
+
@Test public void testEmptySort() {
HepProgram program = new HepProgramBuilder()
.addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index 695c171..d1cefda 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -2836,7 +2836,7 @@ LogicalProject(MGR=[CAST(10):INTEGER])
]]>
</Resource>
</TestCase>
- <TestCase name="testEmptyJoin">
+ <TestCase name="testLeftEmptyInnerJoin">
<Resource name="sql">
<![CDATA[select * from (
select * from emp where false) as e
@@ -2859,7 +2859,30 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$
]]>
</Resource>
</TestCase>
- <TestCase name="testEmptyJoinRight">
+ <TestCase name="testLeftEmptyLeftJoin">
+ <Resource name="sql">
+ <![CDATA[select * from (
+ select * from emp where false) e
+left join dept d on e.deptno = d.deptno]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+ LogicalJoin(condition=[=($7, $9)], joinType=[left])
+ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+ LogicalFilter(condition=[false])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+ LogicalValues(tuples=[[]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testLeftEmptyRightJoin">
<Resource name="sql">
<![CDATA[select * from (
select * from emp where false) e
@@ -2884,16 +2907,16 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$
]]>
</Resource>
</TestCase>
- <TestCase name="testEmptyJoinLeft">
+ <TestCase name="testLeftEmptyFullJoin">
<Resource name="sql">
<![CDATA[select * from (
select * from emp where false) e
-left join dept d on e.deptno = d.deptno]]>
+full join dept d on e.deptno = d.deptno]]>
</Resource>
<Resource name="planBefore">
<![CDATA[
LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
- LogicalJoin(condition=[=($7, $9)], joinType=[left])
+ LogicalJoin(condition=[=($7, $9)], joinType=[full])
LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
LogicalFilter(condition=[false])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -2903,10 +2926,190 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$
<Resource name="planAfter">
<![CDATA[
LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+ LogicalJoin(condition=[=($7, $9)], joinType=[full])
+ LogicalValues(tuples=[[]])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testLeftEmptySemiJoin">
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(EMPNO=[$0])
+ LogicalJoin(condition=[=($7, $8)], joinType=[semi])
+ LogicalValues(tuples=[[]])
+ LogicalTableScan(table=[[scott, DEPT]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(EMPNO=[$0])
+ LogicalValues(tuples=[[]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testLeftEmptyAntiJoin">
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(EMPNO=[$0])
+ LogicalJoin(condition=[=($7, $8)], joinType=[anti])
+ LogicalValues(tuples=[[]])
+ LogicalTableScan(table=[[scott, DEPT]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(EMPNO=[$0])
+ LogicalValues(tuples=[[]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testRightEmptyInnerJoin">
+ <Resource name="sql">
+ <![CDATA[select * from emp e
+join (select * from dept where false) as d
+on e.deptno = d.deptno]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+ LogicalJoin(condition=[=($7, $9)], joinType=[inner])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+ LogicalProject(DEPTNO=[$0], NAME=[$1])
+ LogicalFilter(condition=[false])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+ LogicalValues(tuples=[[]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testRightEmptyLeftJoin">
+ <Resource name="sql">
+ <![CDATA[select * from emp e
+left join (select * from dept where false) as d
+on e.deptno = d.deptno]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+ LogicalJoin(condition=[=($7, $9)], joinType=[left])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+ LogicalProject(DEPTNO=[$0], NAME=[$1])
+ LogicalFilter(condition=[false])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+ LogicalJoin(condition=[=($7, $9)], joinType=[left])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+ LogicalValues(tuples=[[]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testRightEmptyRightJoin">
+ <Resource name="sql">
+ <![CDATA[select * from emp e
+right join (select * from dept where false) as d
+on e.deptno = d.deptno]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+ LogicalJoin(condition=[=($7, $9)], joinType=[right])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+ LogicalProject(DEPTNO=[$0], NAME=[$1])
+ LogicalFilter(condition=[false])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
LogicalValues(tuples=[[]])
]]>
</Resource>
</TestCase>
+ <TestCase name="testRightEmptyFullJoin">
+ <Resource name="sql">
+ <![CDATA[select * from emp e
+full join (select * from dept where false) as d
+on e.deptno = d.deptno]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+ LogicalJoin(condition=[=($7, $9)], joinType=[full])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+ LogicalProject(DEPTNO=[$0], NAME=[$1])
+ LogicalFilter(condition=[false])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
+ LogicalJoin(condition=[=($7, $9)], joinType=[full])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+ LogicalValues(tuples=[[]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testRightEmptySemiJoin">
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(EMPNO=[$0])
+ LogicalJoin(condition=[=($7, $8)], joinType=[semi])
+ LogicalTableScan(table=[[scott, EMP]])
+ LogicalValues(tuples=[[]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(EMPNO=[$0])
+ LogicalValues(tuples=[[]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testRightEmptyAntiJoin">
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(EMPNO=[$0])
+ LogicalJoin(condition=[=($7, $8)], joinType=[anti])
+ LogicalTableScan(table=[[scott, EMP]])
+ LogicalValues(tuples=[[]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(EMPNO=[$0])
+ LogicalTableScan(table=[[scott, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testRightEmptyAntiJoinNonEqui">
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(EMPNO=[$0])
+ LogicalJoin(condition=[AND(=($7, $8), =($5, 2000))], joinType=[anti])
+ LogicalTableScan(table=[[scott, EMP]])
+ LogicalValues(tuples=[[]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(EMPNO=[$0])
+ LogicalJoin(condition=[AND(=($7, $8), =($5, 2000))], joinType=[anti])
+ LogicalTableScan(table=[[scott, EMP]])
+ LogicalValues(tuples=[[]])
+]]>
+ </Resource>
+ </TestCase>
<TestCase name="testEmptyLimitZero">
<Resource name="sql">
<![CDATA[select * from emp order by deptno limit 0]]>