hsyuan commented on a change in pull request #1451: [CALCITE-3334] Refinement 
for Substitution-Based MV Matching
URL: https://github.com/apache/calcite/pull/1451#discussion_r338190090
 
 

 ##########
 File path: core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
 ##########
 @@ -997,233 +1022,520 @@ public UnifyResult apply(UnifyRuleCall call) {
     }
   }
 
-  /** Implementation of {@link UnifyRule} that matches
-   * {@link org.apache.calcite.rel.core.TableScan}. */
-  private static class ScanToProjectUnifyRule extends AbstractUnifyRule {
-    public static final ScanToProjectUnifyRule INSTANCE =
-        new ScanToProjectUnifyRule();
+  /**
+   * A {@link SubstitutionVisitor.UnifyRule} that matches a
+   * {@link MutableScan} to a {@link MutableCalc} which has {@link 
MutableScan} as child.
+   */
+  private static class ScanToCalcUnifyRule extends AbstractUnifyRule {
+
+    public static final ScanToCalcUnifyRule INSTANCE = new 
ScanToCalcUnifyRule();
 
-    private ScanToProjectUnifyRule() {
-      super(any(MutableScan.class),
-          any(MutableProject.class), 0);
+    private ScanToCalcUnifyRule() {
+      super(
+          any(MutableScan.class),
+          operand(MutableCalc.class,
+              any(MutableScan.class)),
+          0);
     }
 
-    public UnifyResult apply(UnifyRuleCall call) {
-      final MutableProject target = (MutableProject) call.target;
+    @Override protected UnifyResult apply(UnifyRuleCall call) {
+
       final MutableScan query = (MutableScan) call.query;
-      // We do not need to check query's parent type to avoid duplication
-      // of ProjectToProjectUnifyRule or FilterToProjectUnifyRule, since
-      // SubstitutionVisitor performs a top-down match.
-      if (!query.equals(target.getInput())) {
-        return null;
-      }
-      final RexShuttle shuttle = getRexShuttle(target);
-      final RexBuilder rexBuilder = target.cluster.getRexBuilder();
-      final List<RexNode> newProjects;
-      try {
-        newProjects = (List<RexNode>)
-            shuttle.apply(rexBuilder.identityProjects(query.rowType));
-      } catch (MatchFailed e) {
-        return null;
-      }
-      final MutableProject newProject =
-          MutableProject.of(query.rowType, target, newProjects);
-      final MutableRel newProject2 = MutableRels.strip(newProject);
-      return call.result(newProject2);
-    }
-  }
 
-  /** Implementation of {@link UnifyRule} that matches
-   * {@link org.apache.calcite.rel.core.Project}. */
-  private static class ProjectToProjectUnifyRule extends AbstractUnifyRule {
-    public static final ProjectToProjectUnifyRule INSTANCE =
-        new ProjectToProjectUnifyRule();
+      final MutableCalc target = (MutableCalc) call.target;
+      final MutableScan targetInput = (MutableScan) target.getInput();
+      final Pair<RexNode, List<RexNode>> targetExplained = explainCalc(target);
+      final RexNode targetCond = targetExplained.left;
+      final List<RexNode> targetProjs = targetExplained.right;
 
-    private ProjectToProjectUnifyRule() {
-      super(operand(MutableProject.class, query(0)),
-          operand(MutableProject.class, target(0)), 1);
-    }
+      final RexBuilder rexBuilder = call.getCluster().getRexBuilder();
 
-    public UnifyResult apply(UnifyRuleCall call) {
-      final MutableProject target = (MutableProject) call.target;
-      final MutableProject query = (MutableProject) call.query;
-      final RexShuttle shuttle = getRexShuttle(target);
-      final List<RexNode> newProjects;
-      try {
-        newProjects = shuttle.apply(query.projects);
-      } catch (MatchFailed e) {
-        return null;
+      if (query.equals(targetInput) && targetCond.isAlwaysTrue()) {
+        final RexShuttle shuttle = getRexShuttle(targetProjs);
+        final List<RexNode> compenProjs;
+        try {
+          compenProjs =
+              (List<RexNode>) 
shuttle.apply(rexBuilder.identityProjects(query.rowType));
+        } catch (MatchFailed e) {
+          return null;
+        }
+        if (RexUtil.isIdentity(compenProjs, target.rowType)) {
+          return call.result(target);
+        } else {
+          RexProgram compenRexProgram = RexProgram.create(
+              target.rowType, compenProjs, null, query.rowType, rexBuilder);
+          MutableCalc compenCalc = MutableCalc.of(target, compenRexProgram);
+          return tryMergeParentCalcAndGenResult(call, compenCalc);
+        }
       }
-      final MutableProject newProject =
-          MutableProject.of(query.rowType, target, newProjects);
-      final MutableRel newProject2 = MutableRels.strip(newProject);
-      return call.result(newProject2);
+      return null;
     }
   }
 
+  /**
+   * A {@link SubstitutionVisitor.UnifyRule} that matches a
+   * {@link MutableCalc} to a {@link MutableCalc}.
+   * The matching condition is as below:
+   * 1. All columns of query can be expressed by target;
+   * 2. The filtering condition of query must equals to or be weaker than 
target.
+   */
+  private static class CalcToCalcUnifyRule extends AbstractUnifyRule {
 
-  /** Implementation of {@link UnifyRule} that matches a {@link MutableFilter}
-   * to a {@link MutableProject}. */
-  private static class FilterToProjectUnifyRule extends AbstractUnifyRule {
-    public static final FilterToProjectUnifyRule INSTANCE =
-        new FilterToProjectUnifyRule();
+    public static final CalcToCalcUnifyRule INSTANCE =
+        new CalcToCalcUnifyRule();
 
-    private FilterToProjectUnifyRule() {
-      super(operand(MutableFilter.class, query(0)),
-          operand(MutableProject.class, target(0)), 1);
+    private CalcToCalcUnifyRule() {
+      super(
+          operand(MutableCalc.class,
+              query(0)),
+          operand(MutableCalc.class,
+              target(0)),
+          1);
     }
 
     public UnifyResult apply(UnifyRuleCall call) {
-      // Child of projectTarget is equivalent to child of filterQuery.
+      final MutableCalc query = (MutableCalc) call.query;
+      final Pair<RexNode, List<RexNode>> queryExplained = explainCalc(query);
+      final RexNode queryCond = queryExplained.left;
+      final List<RexNode> queryProjs = queryExplained.right;
+
+      final MutableCalc target = (MutableCalc) call.target;
+      final Pair<RexNode, List<RexNode>> targetExplained = explainCalc(target);
+      final RexNode targetCond = targetExplained.left;
+      final List<RexNode> targetProjs = targetExplained.right;
+
+      final RexBuilder rexBuilder = call.getCluster().getRexBuilder();
+
       try {
-        // TODO: make sure that constants are ok
-        final MutableProject target = (MutableProject) call.target;
-        final RexShuttle shuttle = getRexShuttle(target);
-        final RexNode newCondition;
-        final MutableFilter query = (MutableFilter) call.query;
-        try {
-          newCondition = query.condition.accept(shuttle);
-        } catch (MatchFailed e) {
+        final RexShuttle shuttle = getRexShuttle(targetProjs);
+        final RexNode splitted = splitFilter(call.getSimplify(), queryCond, 
targetCond);
+
+        final RexNode compenCond;
+        if (splitted != null) {
+          if (splitted.isAlwaysTrue()) {
+            compenCond = null;
+          } else {
+            // Compensate the residual filtering condition.
+            compenCond = shuttle.apply(splitted);
+          }
+        } else if (implies(
+            call.getCluster(), queryCond, targetCond, 
query.getInput().rowType)) {
+          // Fail to split filtering condition, but implies that target 
contains
+          // all lines of query, thus just set compensating filtering condition
+          // as the filtering condition of query.
+          compenCond = shuttle.apply(queryCond);
+        } else {
           return null;
         }
-        final MutableFilter newFilter = MutableFilter.of(target, newCondition);
-        if (query.getParent() instanceof MutableProject) {
-          final MutableRel inverse =
-              invert(((MutableProject) query.getParent()).getNamedProjects(),
-                  newFilter, shuttle);
-          return call.create(query.getParent()).result(inverse);
+
+        final List<RexNode> compenProjs = shuttle.apply(queryProjs);
+        if (compenCond == null
+            && RexUtil.isIdentity(compenProjs, target.rowType)) {
+          return call.result(target);
         } else {
-          final MutableRel inverse = invert(query, newFilter, target);
-          return call.result(inverse);
+          RexProgram compenRexProgram = RexProgram.create(
+              target.rowType, compenProjs, compenCond,
+              query.rowType, rexBuilder);
+          MutableCalc compenCalc = MutableCalc.of(target, compenRexProgram);
+          return tryMergeParentCalcAndGenResult(call, compenCalc);
         }
       } catch (MatchFailed e) {
         return null;
       }
     }
+  }
 
-    protected MutableRel invert(List<Pair<RexNode, String>> namedProjects,
-        MutableRel input,
-        RexShuttle shuttle) {
-      LOGGER.trace("SubstitutionVisitor: invert:\nprojects: {}\ninput: 
{}\nproject: {}\n",
-          namedProjects, input, shuttle);
-      final List<RexNode> exprList = new ArrayList<>();
-      final RexBuilder rexBuilder = input.cluster.getRexBuilder();
-      final List<RexNode> projects = Pair.left(namedProjects);
-      for (RexNode expr : projects) {
-        exprList.add(rexBuilder.makeZeroLiteral(expr.getType()));
-      }
-      for (Ord<RexNode> expr : Ord.zip(projects)) {
-        final RexNode node = expr.e.accept(shuttle);
-        if (node == null) {
-          throw MatchFailed.INSTANCE;
-        }
-        exprList.set(expr.i, node);
-      }
-      return MutableProject.of(input, exprList, Pair.right(namedProjects));
-    }
+  /**
+   * A {@link SubstitutionVisitor.UnifyRule} that matches a {@link MutableJoin}
+   * which has {@link MutableCalc} as left child to a {@link MutableJoin}.
+   * We try to pull up the {@link MutableCalc} to top of {@link MutableJoin},
+   * then match the {@link MutableJoin} in query to {@link MutableJoin} in 
target.
+   */
+  private static class JoinOnLeftCalcToJoinUnifyRule extends AbstractUnifyRule 
{
 
-    protected MutableRel invert(MutableRel model, MutableRel input,
-        MutableProject project) {
-      LOGGER.trace("SubstitutionVisitor: invert:\nmodel: {}\ninput: 
{}\nproject: {}\n",
-          model, input, project);
-      if (project.projects.size() < model.rowType.getFieldCount()) {
-        throw MatchFailed.INSTANCE;
-      }
-      final List<RexNode> exprList = new ArrayList<>();
-      final RexBuilder rexBuilder = model.cluster.getRexBuilder();
-      for (int i = 0; i < model.rowType.getFieldCount(); i++) {
-        exprList.add(null);
+    public static final JoinOnLeftCalcToJoinUnifyRule INSTANCE =
+        new JoinOnLeftCalcToJoinUnifyRule();
+
+    private JoinOnLeftCalcToJoinUnifyRule() {
+      super(
+        operand(MutableJoin.class,
+            operand(MutableCalc.class,
+                query(0)),
+            query(1)),
+        operand(MutableJoin.class,
+            target(0),
+            target(1)),
+        2);
+    }
+
+    @Override protected UnifyResult apply(UnifyRuleCall call) {
+      final MutableJoin query = (MutableJoin) call.query;
+      final MutableCalc qInput0 = (MutableCalc) query.getLeft();
+      final MutableRel qInput1 = query.getRight();
+      final Pair<RexNode, List<RexNode>> qInput0Explained = 
explainCalc(qInput0);
+      final RexNode qInput0Cond = qInput0Explained.left;
+      final List<RexNode> qInput0Projs = qInput0Explained.right;
+
+      final MutableJoin target = (MutableJoin) call.target;
+
+      final RexBuilder rexBuilder = call.getCluster().getRexBuilder();
+
+      // Try pulling up MutableCalc only when:
+      // 1. it's inner join;
+      // 2. it's outer join but no filttering condition from MutableCalc.
+      JoinRelType joinRelType = sameJoinType(query.joinType, target.joinType);
+      if (joinRelType == null) {
+        return null;
       }
-      for (Ord<RexNode> expr : Ord.zip(project.projects)) {
-        if (expr.e instanceof RexInputRef) {
-          final int target = ((RexInputRef) expr.e).getIndex();
-          if (exprList.get(target) == null) {
-            exprList.set(target,
-                rexBuilder.ensureType(expr.e.getType(),
-                    RexInputRef.of(expr.i, input.rowType),
-                    false));
+      if (joinRelType == JoinRelType.INNER
+          || joinRelType.isOuterJoin() && qInput0Cond.isAlwaysTrue()) {
+        // Try pulling up MutableCalc only when Join condition references 
mapping.
+        if (referenceByMapping(query.condition, qInput0Projs,
+            (List<RexNode>) rexBuilder.identityProjects(qInput1.rowType))) {
+          RexNode newQueryJoinCond = new RexShuttle() {
+            @Override public RexNode visitInputRef(RexInputRef inputRef) {
+              int idx = inputRef.getIndex();
+              if (idx < fieldCnt(qInput0)) {
+                return new RexInputRef(((RexInputRef) 
qInput0Projs.get(idx)).getIndex(),
+                    inputRef.getType());
+              } else {
+                return new RexInputRef(idx - fieldCnt(qInput0)
+                    + fieldCnt(qInput0.getInput()), inputRef.getType());
+              }
+            }
+          }.apply(query.condition);
+
+          RexNode splitted =
+              splitFilter(call.getSimplify(), newQueryJoinCond, 
target.condition);
+          // MutableJoin matches only when the conditions are analyzed to be 
same.
+          if (splitted != null && splitted.isAlwaysTrue()) {
+            final RexNode compenCond = qInput0Cond;
+            final List<RexNode> compenProjs = new ArrayList<>();
+            for (int i = 0; i < fieldCnt(query); i++) {
+              if (i < fieldCnt(qInput0)) {
+                compenProjs.add(qInput0Projs.get(i));
+              } else {
+                compenProjs.add(
+                    new RexInputRef(i - fieldCnt(qInput0) + 
fieldCnt(qInput0.getInput()),
+                    query.rowType.getFieldList().get(i).getType()));
+              }
+            }
+            RexProgram compenRexProgram = RexProgram.create(
+                target.rowType, compenProjs, compenCond,
+                query.rowType, rexBuilder);
+            MutableCalc compenCalc = MutableCalc.of(target, compenRexProgram);
+            return tryMergeParentCalcAndGenResult(call, compenCalc);
           }
         }
       }
-      if (exprList.indexOf(null) != -1) {
-        throw MatchFailed.INSTANCE;
-      }
-      return MutableProject.of(model.rowType, input, exprList);
+      return null;
     }
   }
 
-  /** Implementation of {@link UnifyRule} that matches a
-   * {@link MutableFilter}. */
-  private static class FilterToFilterUnifyRule extends AbstractUnifyRule {
-    public static final FilterToFilterUnifyRule INSTANCE =
-        new FilterToFilterUnifyRule();
+  /**
+   * A {@link SubstitutionVisitor.UnifyRule} that matches a {@link MutableJoin}
+   * which has {@link MutableCalc} as right child to a {@link MutableJoin}.
+   * We try to pull up the {@link MutableCalc} to top of {@link MutableJoin},
+   * then match the {@link MutableJoin} in query to {@link MutableJoin} in 
target.
+   */
+  private static class JoinOnRightCalcToJoinUnifyRule extends 
AbstractUnifyRule {
 
-    private FilterToFilterUnifyRule() {
-      super(operand(MutableFilter.class, query(0)),
-          operand(MutableFilter.class, target(0)), 1);
-    }
+    public static final JoinOnRightCalcToJoinUnifyRule INSTANCE =
+        new JoinOnRightCalcToJoinUnifyRule();
 
-    public UnifyResult apply(UnifyRuleCall call) {
-      // in.query can be rewritten in terms of in.target if its condition
-      // is weaker. For example:
-      //   query: SELECT * FROM t WHERE x = 1 AND y = 2
-      //   target: SELECT * FROM t WHERE x = 1
-      // transforms to
-      //   result: SELECT * FROM (target) WHERE y = 2
-      final MutableFilter query = (MutableFilter) call.query;
-      final MutableFilter target = (MutableFilter) call.target;
-      final MutableFilter newFilter =
-          createFilter(call, query, target);
-      if (newFilter == null) {
+    private JoinOnRightCalcToJoinUnifyRule() {
+      super(
+          operand(MutableJoin.class,
+              query(0),
+              operand(MutableCalc.class,
+                  query(1))),
+          operand(MutableJoin.class,
+              target(0),
+              target(1)),
+          2);
+    }
+
+    @Override protected UnifyResult apply(UnifyRuleCall call) {
+      final MutableJoin query = (MutableJoin) call.query;
+      final MutableRel qInput0 = query.getLeft();
+      final MutableCalc qInput1 = (MutableCalc) query.getRight();
+      final Pair<RexNode, List<RexNode>> qInput1Explained = 
explainCalc(qInput1);
+      final RexNode qInput1Cond = qInput1Explained.left;
+      final List<RexNode> qInput1Projs = qInput1Explained.right;
+
+      final MutableJoin target = (MutableJoin) call.target;
+
+      final RexBuilder rexBuilder = call.getCluster().getRexBuilder();
+
+      // Try pulling up MutableCalc only when:
+      // 1. it's inner join;
+      // 2. it's outer join but no filttering condition from MutableCalc.
+      JoinRelType joinRelType = sameJoinType(query.joinType, target.joinType);
+      if (joinRelType == null) {
         return null;
       }
-      return call.result(newFilter);
+      if (joinRelType == JoinRelType.INNER
+          || joinRelType.isOuterJoin() && qInput1Cond.isAlwaysTrue()) {
+        // Try pulling up MutableCalc only when Join condition references 
mapping.
+        if (referenceByMapping(query.condition,
+            (List<RexNode>) 
call.getCluster().getRexBuilder().identityProjects(qInput0.rowType),
+            qInput1Projs)) {
+          RexNode newQueryJoinCond = new RexShuttle() {
+            @Override public RexNode visitInputRef(RexInputRef inputRef) {
+              int idx = inputRef.getIndex();
+              if (idx < fieldCnt(qInput0)) {
+                return inputRef;
+              } else {
+                return new RexInputRef(
+                    ((RexInputRef) qInput1Projs.get(idx - 
fieldCnt(qInput0))).getIndex()
+                        + fieldCnt(qInput0),
 
 Review comment:
   use a var

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

Reply via email to