This is an automated email from the ASF dual-hosted git repository.

hyuan 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 39cb3e3  [CALCITE-3910] Enhance ProjectJoinTransposeRule to support 
SemiJoin and AntiJoin (Liya Fan)
39cb3e3 is described below

commit 39cb3e3619a528bb39a677598f16a26f10afbfaf
Author: liyafan82 <fan_li...@foxmail.com>
AuthorDate: Wed Apr 15 15:38:38 2020 +0800

    [CALCITE-3910] Enhance ProjectJoinTransposeRule to support SemiJoin and 
AntiJoin (Liya Fan)
    
    Close #1917
---
 .../rel/rules/ProjectJoinTransposeRule.java        |  4 --
 .../apache/calcite/rel/rules/PushProjector.java    | 33 ++++++---
 .../org/apache/calcite/test/RelOptRulesTest.java   | 78 ++++++++++++++++++++++
 .../org/apache/calcite/test/RelOptRulesTest.xml    | 58 ++++++++++++++++
 4 files changed, 158 insertions(+), 15 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/ProjectJoinTransposeRule.java 
b/core/src/main/java/org/apache/calcite/rel/rules/ProjectJoinTransposeRule.java
index b975a3b..5d6510c 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/ProjectJoinTransposeRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/ProjectJoinTransposeRule.java
@@ -93,10 +93,6 @@ public class ProjectJoinTransposeRule extends RelOptRule 
implements Transformati
     Project origProj = call.rel(0);
     final Join join = call.rel(1);
 
-    if (!join.getJoinType().projectsRight()) {
-      return; // TODO: support SemiJoin / AntiJoin
-    }
-
     // Normalize the join condition so we don't end up misidentified expanded
     // form of IS NOT DISTINCT FROM as PushProject also visit the filter 
condition
     // and push down expressions.
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/PushProjector.java 
b/core/src/main/java/org/apache/calcite/rel/rules/PushProjector.java
index 7ec5482..fcbd60e 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/PushProjector.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/PushProjector.java
@@ -25,6 +25,7 @@ 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.SetOp;
+import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexCall;
@@ -40,6 +41,7 @@ import org.apache.calcite.util.Pair;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
 
 import java.util.ArrayList;
 import java.util.BitSet;
@@ -47,6 +49,8 @@ import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 /**
  * PushProjector is a utility class used to perform operations used in push
@@ -216,9 +220,14 @@ public class PushProjector {
       origProjExprs = origProj.getProjects();
     }
 
-    childFields = childRel.getRowType().getFieldList();
+    if (childRel instanceof Join) {
+      Join join = (Join) childRel;
+      childFields = 
Lists.newArrayList(join.getLeft().getRowType().getFieldList());
+      childFields.addAll(join.getRight().getRowType().getFieldList());
+    } else {
+      childFields = childRel.getRowType().getFieldList();
+    }
     nChildFields = childFields.size();
-
     projRefs = new BitSet(nChildFields);
     if (childRel instanceof Join) {
       Join joinRel = (Join) childRel;
@@ -227,14 +236,7 @@ public class PushProjector {
       List<RelDataTypeField> rightFields =
           joinRel.getRight().getRowType().getFieldList();
       nFields = leftFields.size();
-      switch (joinRel.getJoinType()) {
-      case SEMI:
-      case ANTI:
-        nFieldsRight = 0;
-        break;
-      default:
-        nFieldsRight = rightFields.size();
-      }
+      nFieldsRight = rightFields.size();
       nSysFields = joinRel.getSystemFieldList().size();
       childBitmap =
           ImmutableBitSet.range(nSysFields, nFields + nSysFields);
@@ -469,7 +471,8 @@ public class PushProjector {
     // referenced and there are no special preserve expressions; note
     // that we need to do this check after we've handled the 0-column
     // project cases
-    if (projRefs.cardinality() == nChildFields
+    boolean allFieldsReferenced = IntStream.range(0, nChildFields).allMatch(i 
-> projRefs.get(i));
+    if (allFieldsReferenced
         && childPreserveExprs.size() == 0
         && rightPreserveExprs.size() == 0) {
       return true;
@@ -547,6 +550,14 @@ public class PushProjector {
       } else {
         newExpr = projExpr;
       }
+
+      List<RelDataType> typeList = projChild.getRowType().getFieldList()
+          .stream().map(field -> field.getType()).collect(Collectors.toList());
+      RexUtil.FixNullabilityShuttle fixer =
+          new RexUtil.FixNullabilityShuttle(
+              projChild.getCluster().getRexBuilder(), typeList);
+      newExpr = newExpr.accept(fixer);
+
       newProjects.add(
           Pair.of(
               newExpr,
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 12ef011..9324d78 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -868,6 +868,84 @@ class RelOptRulesTest extends RelOptTestBase {
         .check();
   }
 
+  @Test void testSemiJoinProjectTranspose() {
+    final RelBuilder relBuilder = 
RelBuilder.create(RelBuilderTest.config().build());
+    // build a rel equivalent to sql:
+    // select a.name from dept a
+    // where a.deptno in (select b.deptno * 2 from dept);
+
+    RelNode left = relBuilder.scan("DEPT").build();
+    RelNode right = relBuilder.scan("DEPT")
+        .project(
+            relBuilder.call(
+                SqlStdOperatorTable.MULTIPLY, relBuilder.literal(2), 
relBuilder.field(0)))
+        .aggregate(relBuilder.groupKey(ImmutableBitSet.of(0))).build();
+
+    RelNode plan = relBuilder.push(left)
+        .push(right)
+        .semiJoin(
+            relBuilder.call(SqlStdOperatorTable.EQUALS,
+                relBuilder.field(2, 0, 0),
+                relBuilder.field(2, 1, 0)))
+        .project(relBuilder.field(1))
+        .build();
+
+    final String planBefore = NL + RelOptUtil.toString(plan);
+
+    HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(ProjectJoinTransposeRule.INSTANCE)
+        .build();
+
+    HepPlanner hepPlanner = new HepPlanner(program);
+    hepPlanner.setRoot(plan);
+    RelNode output = hepPlanner.findBestExp();
+
+    final String planAfter = NL + RelOptUtil.toString(output);
+    final DiffRepository diffRepos = getDiffRepos();
+    diffRepos.assertEquals("planBefore", "${planBefore}", planBefore);
+    diffRepos.assertEquals("planAfter", "${planAfter}", planAfter);
+    SqlToRelTestBase.assertValid(output);
+  }
+
+  @Test void testAntiJoinProjectTranspose() {
+    final RelBuilder relBuilder = 
RelBuilder.create(RelBuilderTest.config().build());
+    // build a rel equivalent to sql:
+    // select a.name from dept a
+    // where a.deptno not in (select b.deptno * 2 from dept);
+
+    RelNode left = relBuilder.scan("DEPT").build();
+    RelNode right = relBuilder.scan("DEPT")
+        .project(
+            relBuilder.call(
+                SqlStdOperatorTable.MULTIPLY, relBuilder.literal(2), 
relBuilder.field(0)))
+        .aggregate(relBuilder.groupKey(ImmutableBitSet.of(0))).build();
+
+    RelNode plan = relBuilder.push(left)
+        .push(right)
+        .antiJoin(
+            relBuilder.call(SqlStdOperatorTable.EQUALS,
+                relBuilder.field(2, 0, 0),
+                relBuilder.field(2, 1, 0)))
+        .project(relBuilder.field(1))
+        .build();
+
+    final String planBefore = NL + RelOptUtil.toString(plan);
+
+    HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(ProjectJoinTransposeRule.INSTANCE)
+        .build();
+
+    HepPlanner hepPlanner = new HepPlanner(program);
+    hepPlanner.setRoot(plan);
+    RelNode output = hepPlanner.findBestExp();
+
+    final String planAfter = NL + RelOptUtil.toString(output);
+    final DiffRepository diffRepos = getDiffRepos();
+    diffRepos.assertEquals("planBefore", "${planBefore}", planBefore);
+    diffRepos.assertEquals("planAfter", "${planAfter}", planAfter);
+    SqlToRelTestBase.assertValid(output);
+  }
+
   @Test void testJoinProjectTranspose1() {
     final HepProgram preProgram =
         HepProgram.builder()
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 f5f3d3e..dd8a00a 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -7402,6 +7402,64 @@ LogicalProject(NAME=[$1])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testSemiJoinProjectTranspose">
+        <Resource name="sql">
+            <![CDATA[select a.name from dept a
+where a.deptno in (select b.deptno * 2 from dept);
+]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(DNAME=[$1])
+  LogicalJoin(condition=[=($0, $3)], joinType=[semi])
+    LogicalTableScan(table=[[scott, DEPT]])
+    LogicalAggregate(group=[{0}])
+      LogicalProject($f0=[*(2, $0)])
+        LogicalTableScan(table=[[scott, DEPT]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(DNAME=[$1])
+  LogicalJoin(condition=[=($0, $2)], joinType=[semi])
+    LogicalProject(DEPTNO=[$0], DNAME=[$1])
+      LogicalTableScan(table=[[scott, DEPT]])
+    LogicalProject($f0=[$0])
+      LogicalAggregate(group=[{0}])
+        LogicalProject($f0=[*(2, $0)])
+          LogicalTableScan(table=[[scott, DEPT]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testAntiJoinProjectTranspose">
+        <Resource name="sql">
+            <![CDATA[select a.name from dept a
+where a.deptno not in (select b.deptno * 2 from dept);
+]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(DNAME=[$1])
+  LogicalJoin(condition=[=($0, $3)], joinType=[anti])
+    LogicalTableScan(table=[[scott, DEPT]])
+    LogicalAggregate(group=[{0}])
+      LogicalProject($f0=[*(2, $0)])
+        LogicalTableScan(table=[[scott, DEPT]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(DNAME=[$1])
+  LogicalJoin(condition=[=($0, $2)], joinType=[anti])
+    LogicalProject(DEPTNO=[$0], DNAME=[$1])
+      LogicalTableScan(table=[[scott, DEPT]])
+    LogicalProject($f0=[$0])
+      LogicalAggregate(group=[{0}])
+        LogicalProject($f0=[*(2, $0)])
+          LogicalTableScan(table=[[scott, DEPT]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testJoinProjectTranspose1">
         <Resource name="sql">
             <![CDATA[select a.name

Reply via email to