You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2014/12/18 08:28:10 UTC

[1/3] incubator-calcite git commit: [CALCITE-532] Support for grouping sets in AggregateFilterTransposeRule (Jesus Camacho Rodriguez)

Repository: incubator-calcite
Updated Branches:
  refs/heads/master 2b3581923 -> 288254b22


[CALCITE-532] Support for grouping sets in AggregateFilterTransposeRule (Jesus Camacho Rodriguez)

Close apache/incubator-calcite#33


Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/e8ee2b31
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/e8ee2b31
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/e8ee2b31

Branch: refs/heads/master
Commit: e8ee2b31e7cec6bf26c7f0bb45ea870a9d4744d9
Parents: 2b35819
Author: Jesus Camacho Rodriguez <jc...@hortonworks.com>
Authored: Mon Dec 15 16:55:45 2014 +0100
Committer: Julian Hyde <jh...@apache.org>
Committed: Wed Dec 17 12:45:48 2014 -0800

----------------------------------------------------------------------
 .../rel/rules/AggregateFilterTransposeRule.java | 34 ++++++++++---
 .../apache/calcite/test/RelOptRulesTest.java    | 36 +++++++++++++
 .../org/apache/calcite/test/RelOptRulesTest.xml | 53 ++++++++++++++++++++
 3 files changed, 115 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/e8ee2b31/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
index 82c30f1..dfa325d 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
@@ -58,7 +58,7 @@ public class AggregateFilterTransposeRule extends RelOptRule {
 
   private AggregateFilterTransposeRule() {
     super(
-        operand(Aggregate.class, null, Aggregate.IS_SIMPLE,
+        operand(Aggregate.class,
             operand(Filter.class, any())));
   }
 
@@ -80,9 +80,11 @@ public class AggregateFilterTransposeRule extends RelOptRule {
       // the rule fires forever: A-F => A-F-A => A-A-F-A => A-A-A-F-A => ...
       return;
     }
+    boolean allColumnsInAggregate = aggregate.getGroupSet().
+        contains(filterColumns);
     final Aggregate newAggregate =
-        aggregate.copy(aggregate.getTraitSet(), input, false, newGroupSet, null,
-            aggregate.getAggCallList());
+        aggregate.copy(aggregate.getTraitSet(), input,
+                false, newGroupSet, null, aggregate.getAggCallList());
     final Mappings.TargetMapping mapping = Mappings.target(
         new Function<Integer, Integer>() {
           public Integer apply(Integer a0) {
@@ -95,17 +97,32 @@ public class AggregateFilterTransposeRule extends RelOptRule {
         RexUtil.apply(mapping, filter.getCondition());
     final Filter newFilter = filter.copy(filter.getTraitSet(),
         newAggregate, newCondition);
-    if (aggregate.getGroupSet().contains(filterColumns)) {
+    if (allColumnsInAggregate && !aggregate.indicator) {
       // Everything needed by the filter is returned by the aggregate.
       assert newGroupSet.equals(aggregate.getGroupSet());
       call.transformTo(newFilter);
     } else {
-      // The filter needs at least one extra column.
-      // Now aggregate it away.
+      // If aggregate uses grouping sets, we always need to split it.
+      // Otherwise, it means that grouping sets are not used, but the
+      // filter needs at least one extra column, and now aggregate it away.
       final ImmutableBitSet.Builder topGroupSet = ImmutableBitSet.builder();
       for (int c : aggregate.getGroupSet()) {
         topGroupSet.set(newGroupSet.indexOf(c));
       }
+      ImmutableList<ImmutableBitSet> newGroupingSets = null;
+      if (aggregate.indicator) {
+        ImmutableList.Builder<ImmutableBitSet> newGroupingSetsBuilder =
+                ImmutableList.builder();
+        for (ImmutableBitSet groupingSet: aggregate.getGroupSets()) {
+          final ImmutableBitSet.Builder newGroupingSet =
+                  ImmutableBitSet.builder();
+          for (int c : groupingSet) {
+            newGroupingSet.set(newGroupSet.indexOf(c));
+          }
+          newGroupingSetsBuilder.add(newGroupingSet.build());
+        }
+        newGroupingSets = newGroupingSetsBuilder.build();
+      }
       final List<AggregateCall> topAggCallList = Lists.newArrayList();
       int i = newGroupSet.cardinality();
       for (AggregateCall aggregateCall : aggregate.getAggCallList()) {
@@ -124,8 +141,9 @@ public class AggregateFilterTransposeRule extends RelOptRule {
                 ImmutableList.of(i++), aggregateCall.type, aggregateCall.name));
       }
       final Aggregate topAggregate =
-          aggregate.copy(aggregate.getTraitSet(), newFilter, false,
-              topGroupSet.build(), null, topAggCallList);
+          aggregate.copy(aggregate.getTraitSet(), newFilter,
+              aggregate.indicator, topGroupSet.build(),
+              newGroupingSets, topAggCallList);
       call.transformTo(topAggregate);
     }
   }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/e8ee2b31/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
----------------------------------------------------------------------
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 f1db459..bfa65c9 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -29,6 +29,7 @@ import org.apache.calcite.rel.metadata.ChainedRelMetadataProvider;
 import org.apache.calcite.rel.metadata.DefaultRelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelMetadataProvider;
 import org.apache.calcite.rel.rules.AggregateExpandDistinctAggregatesRule;
+import org.apache.calcite.rel.rules.AggregateFilterTransposeRule;
 import org.apache.calcite.rel.rules.AggregateProjectMergeRule;
 import org.apache.calcite.rel.rules.AggregateProjectPullUpConstantsRule;
 import org.apache.calcite.rel.rules.AggregateReduceFunctionsRule;
@@ -995,6 +996,41 @@ public class RelOptRulesTest extends RelOptTestBase {
     basePushAggThroughUnion();
   }
 
+  @Test public void testPullFilterThroughAggregate() throws Exception {
+    HepProgram preProgram = HepProgram.builder()
+        .addRuleInstance(ProjectMergeRule.INSTANCE)
+        .addRuleInstance(ProjectFilterTransposeRule.INSTANCE)
+        .build();
+    HepProgram program = HepProgram.builder()
+        .addRuleInstance(AggregateFilterTransposeRule.INSTANCE)
+        .build();
+    checkPlanning(tester, preProgram,
+        new HepPlanner(program),
+        "select empno, sal, deptno from ("
+            + "  select empno, sal, deptno"
+            + "  from emp"
+            + "  where sal > 5000)"
+            + "group by empno, sal, deptno");
+  }
+
+  @Test public void testPullFilterThroughAggregateGroupingSets()
+      throws Exception {
+    HepProgram preProgram = HepProgram.builder()
+        .addRuleInstance(ProjectMergeRule.INSTANCE)
+        .addRuleInstance(ProjectFilterTransposeRule.INSTANCE)
+        .build();
+    HepProgram program = HepProgram.builder()
+        .addRuleInstance(AggregateFilterTransposeRule.INSTANCE)
+        .build();
+    checkPlanning(tester, preProgram,
+        new HepPlanner(program),
+        "select empno, sal, deptno from ("
+            + "  select empno, sal, deptno"
+            + "  from emp"
+            + "  where sal > 5000)"
+            + "group by rollup(empno, sal, deptno)");
+  }
+
   private void basePullConstantTroughAggregate() throws Exception {
     HepProgram program = new HepProgramBuilder()
         .addRuleInstance(ProjectMergeRule.INSTANCE)

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/e8ee2b31/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
----------------------------------------------------------------------
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 1f20cc4..9ffdaf4 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -1468,6 +1468,59 @@ LogicalProject(DEPTNO=[CASE($2, null, $0)], JOB=[CASE($3, null, $1)], EXPR$2=[$4
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testPullFilterThroughAggregate">
+        <Resource name="sql">
+            <![CDATA[select empno, sal, deptno from (
+  select empno, sal, deptno
+   from emp
+   where sal > 5000)
+  group by empno, sal, deptno]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalAggregate(group=[{0, 1, 2}])
+  LogicalFilter(condition=[>($1, 5000)])
+    LogicalProject(EMPNO=[$0], SAL=[$5], DEPTNO=[$7])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalFilter(condition=[>($1, 5000)])
+  LogicalAggregate(group=[{0, 1, 2}])
+    LogicalProject(EMPNO=[$0], SAL=[$5], DEPTNO=[$7])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testPullFilterThroughAggregateGroupingSets">
+        <Resource name="sql">
+            <![CDATA[select empno, sal, deptno from (
+  select empno, sal, deptno
+   from emp
+   where sal > 5000)
+  group by rollup(empno, sal, deptno)]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(EMPNO=[CASE($3, null, $0)], SAL=[CASE($4, null, $1)], DEPTNO=[CASE($5, null, $2)])
+  LogicalAggregate(group=[{0, 1, 2}], groups=[[{0, 1, 2}, {0, 1}, {0}, {}]], indicator=[true])
+    LogicalFilter(condition=[>($1, 5000)])
+      LogicalProject(EMPNO=[$0], SAL=[$5], DEPTNO=[$7])
+        LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(EMPNO=[CASE($3, null, $0)], SAL=[CASE($4, null, $1)], DEPTNO=[CASE($5, null, $2)])
+  LogicalAggregate(group=[{0, 1, 2}], groups=[[{0, 1, 2}, {0, 1}, {0}, {}]], indicator=[true])
+    LogicalFilter(condition=[>($1, 5000)])
+      LogicalAggregate(group=[{0, 1, 2}])
+        LogicalProject(EMPNO=[$0], SAL=[$5], DEPTNO=[$7])
+          LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testPullConstantThroughConstLast">
         <Resource name="sql">
             <![CDATA[select deptno, max(mgr) from (


[3/3] incubator-calcite git commit: [CALCITE-479] Migrate RelNode.getChildExps to RelNode.accept(RexShuttle)

Posted by jh...@apache.org.
[CALCITE-479] Migrate RelNode.getChildExps to RelNode.accept(RexShuttle)

Close apache/incubator-calcite#31


Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/288254b2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/288254b2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/288254b2

Branch: refs/heads/master
Commit: 288254b22b270b9a6e893490e776a3ba4ff01fb6
Parents: 86c7c08
Author: Vladimir Sitnikov <si...@gmail.com>
Authored: Sun Dec 14 15:41:57 2014 +0300
Committer: Julian Hyde <jh...@apache.org>
Committed: Wed Dec 17 20:50:56 2014 -0800

----------------------------------------------------------------------
 .../enumerable/EnumerableTableFunctionScan.java | 10 ++-
 .../org/apache/calcite/plan/RelOptUtil.java     | 30 ++++---
 .../apache/calcite/plan/VisitorRelVisitor.java  | 55 -------------
 .../org/apache/calcite/rel/AbstractRelNode.java |  5 ++
 .../calcite/rel/RelHomogeneousShuttle.java      | 86 ++++++++++++++++++++
 .../java/org/apache/calcite/rel/RelNode.java    | 14 ++++
 .../java/org/apache/calcite/rel/core/Calc.java  | 28 +++++++
 .../org/apache/calcite/rel/core/Filter.java     |  9 ++
 .../java/org/apache/calcite/rel/core/Join.java  |  9 ++
 .../org/apache/calcite/rel/core/Project.java    |  9 ++
 .../java/org/apache/calcite/rel/core/Sort.java  | 15 ++++
 .../calcite/rel/core/TableFunctionScan.java     | 65 +++++++++++++--
 .../calcite/rel/externalize/RelWriterImpl.java  | 22 ++---
 .../rel/logical/LogicalTableFunctionScan.java   | 14 +++-
 .../org/apache/calcite/rel/rules/MultiJoin.java | 25 ++++++
 .../rel/rules/ReduceExpressionsRule.java        | 10 ++-
 .../java/org/apache/calcite/rex/RexShuttle.java |  5 +-
 .../sql2rel/DeduplicateCorrelateVariables.java  | 45 ++--------
 .../apache/calcite/sql2rel/RelFieldTrimmer.java |  4 +-
 19 files changed, 318 insertions(+), 142 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScan.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScan.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScan.java
index 936e7fc..d8baf19 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScan.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScan.java
@@ -45,9 +45,15 @@ public class EnumerableTableFunctionScan extends TableFunctionScan
       columnMappings);
   }
 
-  @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
+  @Override public EnumerableTableFunctionScan copy(
+      RelTraitSet traitSet,
+      List<RelNode> inputs,
+      RexNode rexCall,
+      Type elementType,
+      RelDataType rowType,
+      Set<RelColumnMapping> columnMappings) {
     return new EnumerableTableFunctionScan(getCluster(), traitSet, inputs,
-        getElementType(), getRowType(), getCall(), getColumnMappings());
+        elementType, rowType, rexCall, columnMappings);
   }
 
   public Result implement(EnumerableRelImplementor implementor, Prefer pref) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
index 3c31ed2..9cc2401 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -19,7 +19,9 @@ package org.apache.calcite.plan;
 import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelCollationImpl;
+import org.apache.calcite.rel.RelHomogeneousShuttle;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.RelVisitor;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.core.AggregateCall;
@@ -152,22 +154,18 @@ public abstract class RelOptUtil {
    */
   public static Set<String> getVariablesUsed(RelNode rel) {
     final VariableUsedVisitor vuv = new VariableUsedVisitor();
-    final VisitorRelVisitor visitor =
-        new VisitorRelVisitor(vuv) {
-          // implement RelVisitor
-          public void visit(
-              RelNode p,
-              int ordinal,
-              RelNode parent) {
-            p.collectVariablesUsed(vuv.variables);
-            super.visit(p, ordinal, parent);
-
-            // Important! Remove stopped variables AFTER we visit
-            // children. (which what super.visit() does)
-            vuv.variables.removeAll(p.getVariablesStopped());
-          }
-        };
-    visitor.go(rel);
+    RelShuttle visitor = new RelHomogeneousShuttle() {
+      @Override public RelNode visit(RelNode other) {
+        other.collectVariablesUsed(vuv.variables);
+        other.accept(vuv);
+        RelNode result = super.visit(other);
+        // Important! Remove stopped variables AFTER we visit
+        // children. (which what super.visit() does)
+        vuv.variables.removeAll(other.getVariablesStopped());
+        return result;
+      }
+    };
+    rel.accept(visitor);
     return vuv.variables;
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/plan/VisitorRelVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/VisitorRelVisitor.java b/core/src/main/java/org/apache/calcite/plan/VisitorRelVisitor.java
deleted file mode 100644
index d4cd0f5..0000000
--- a/core/src/main/java/org/apache/calcite/plan/VisitorRelVisitor.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.calcite.plan;
-
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.RelVisitor;
-import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.rex.RexVisitor;
-
-import java.util.List;
-
-/**
- * Walks over a tree of {@link RelNode relational expressions}, walking a
- * {@link RexVisitor} over every expression in that tree.
- */
-public class VisitorRelVisitor extends RelVisitor {
-  //~ Instance fields --------------------------------------------------------
-
-  protected final RexVisitor<?> visitor;
-
-  //~ Constructors -----------------------------------------------------------
-
-  public VisitorRelVisitor(RexVisitor<?> visitor) {
-    this.visitor = visitor;
-  }
-
-  //~ Methods ----------------------------------------------------------------
-
-  public void visit(
-      RelNode p,
-      int ordinal,
-      RelNode parent) {
-    List<RexNode> childExps = p.getChildExps();
-    for (RexNode childExp : childExps) {
-      childExp.accept(visitor);
-    }
-    p.childrenAccept(this);
-  }
-}
-
-// End VisitorRelVisitor.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java b/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java
index a1f78d4..7b24e46 100644
--- a/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java
+++ b/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java
@@ -31,6 +31,7 @@ import org.apache.calcite.rel.metadata.Metadata;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexShuttle;
 import org.apache.calcite.sql.SqlExplainLevel;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.Pair;
@@ -253,6 +254,10 @@ public abstract class AbstractRelNode implements RelNode {
     return shuttle.visit(this);
   }
 
+  public RelNode accept(RexShuttle shuttle) {
+    return this;
+  }
+
   public RelOptCost computeSelfCost(RelOptPlanner planner) {
     // by default, assume cost is proportional to number of rows
     double rowCount = RelMetadataQuery.getRowCount(this);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/rel/RelHomogeneousShuttle.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/RelHomogeneousShuttle.java b/core/src/main/java/org/apache/calcite/rel/RelHomogeneousShuttle.java
new file mode 100644
index 0000000..56fa883
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/RelHomogeneousShuttle.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel;
+
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.core.TableFunctionScan;
+import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalCorrelate;
+import org.apache.calcite.rel.logical.LogicalFilter;
+import org.apache.calcite.rel.logical.LogicalIntersect;
+import org.apache.calcite.rel.logical.LogicalJoin;
+import org.apache.calcite.rel.logical.LogicalMinus;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalUnion;
+import org.apache.calcite.rel.logical.LogicalValues;
+
+/**
+ * Visits all the relations in a homogeneous way: always redirects calls to
+ * {@code accept(RelNode)}.
+ */
+public class RelHomogeneousShuttle extends RelShuttleImpl {
+  @Override public RelNode visit(LogicalAggregate aggregate) {
+    return visit((RelNode) aggregate);
+  }
+
+  @Override public RelNode visit(TableScan scan) {
+    return visit((RelNode) scan);
+  }
+
+  @Override public RelNode visit(TableFunctionScan scan) {
+    return visit((RelNode) scan);
+  }
+
+  @Override public RelNode visit(LogicalValues values) {
+    return visit((RelNode) values);
+  }
+
+  @Override public RelNode visit(LogicalFilter filter) {
+    return visit((RelNode) filter);
+  }
+
+  @Override public RelNode visit(LogicalProject project) {
+    return visit((RelNode) project);
+  }
+
+  @Override public RelNode visit(LogicalJoin join) {
+    return visit((RelNode) join);
+  }
+
+  @Override public RelNode visit(LogicalCorrelate correlate) {
+    return visit((RelNode) correlate);
+  }
+
+  @Override public RelNode visit(LogicalUnion union) {
+    return visit((RelNode) union);
+  }
+
+  @Override public RelNode visit(LogicalIntersect intersect) {
+    return visit((RelNode) intersect);
+  }
+
+  @Override public RelNode visit(LogicalMinus minus) {
+    return visit((RelNode) minus);
+  }
+
+  @Override public RelNode visit(Sort sort) {
+    return visit((RelNode) sort);
+  }
+}
+
+// End RelHomogeneousShuttle.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/rel/RelNode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/RelNode.java b/core/src/main/java/org/apache/calcite/rel/RelNode.java
index 36e735e..c4a2aca 100644
--- a/core/src/main/java/org/apache/calcite/rel/RelNode.java
+++ b/core/src/main/java/org/apache/calcite/rel/RelNode.java
@@ -27,6 +27,7 @@ import org.apache.calcite.rel.metadata.Metadata;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexShuttle;
 import org.apache.calcite.util.ImmutableBitSet;
 
 import java.util.List;
@@ -85,8 +86,11 @@ public interface RelNode extends RelOptNode, Cloneable {
    * implementations will return an immutable list. If there are no
    * child expressions, returns an empty list, not <code>null</code>.
    *
+   * @deprecated use #accept(org.apache.calcite.rex.RexShuttle)
    * @return List of this relational expression's child expressions
+   * @see #accept(org.apache.calcite.rex.RexShuttle)
    */
+  @Deprecated
   List<RexNode> getChildExps();
 
   /**
@@ -369,6 +373,16 @@ public interface RelNode extends RelOptNode, Cloneable {
    * this node's children
    */
   RelNode accept(RelShuttle shuttle);
+
+  /**
+   * Accepts a visit from a shuttle. If the shuttle updates expression, then
+   * a copy of the relation should be created.
+   *
+   * @param shuttle Shuttle
+   * @return A copy of this node incorporating changes made by the shuttle to
+   * this node's children
+   */
+  RelNode accept(RexShuttle shuttle);
 }
 
 // End RelNode.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/rel/core/Calc.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Calc.java b/core/src/main/java/org/apache/calcite/rel/core/Calc.java
index 47240f0..b8b9b5c 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Calc.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Calc.java
@@ -28,7 +28,10 @@ import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.SingleRel;
 import org.apache.calcite.rel.logical.LogicalFilter;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rex.RexLocalRef;
+import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexProgram;
+import org.apache.calcite.rex.RexShuttle;
 
 import com.google.common.collect.ImmutableList;
 
@@ -144,6 +147,31 @@ public abstract class Calc extends SingleRel {
   public RelWriter explainTerms(RelWriter pw) {
     return program.explainCalc(super.explainTerms(pw));
   }
+
+  public RelNode accept(RexShuttle shuttle) {
+    List<RexNode> oldExprs = program.getExprList();
+    List<RexNode> exprs = shuttle.apply(oldExprs);
+    List<RexLocalRef> oldProjects = program.getProjectList();
+    List<RexLocalRef> projects = shuttle.apply(oldProjects);
+    RexLocalRef oldCondition = program.getCondition();
+    RexNode condition = shuttle.apply(oldCondition);
+    assert condition instanceof RexLocalRef
+        : "Invalid condition after rewrite. Expected RexLocalRef, got "
+          + condition;
+    if (exprs == oldExprs
+        && projects == oldProjects
+        && condition == oldCondition) {
+      return this;
+    }
+    return copy(traitSet, getInput(),
+        new RexProgram(
+            program.getInputRowType(),
+            exprs,
+            projects,
+            (RexLocalRef) condition,
+            program.getOutputRowType()),
+        collationList);
+  }
 }
 
 // End Calc.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/rel/core/Filter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Filter.java b/core/src/main/java/org/apache/calcite/rel/core/Filter.java
index 8fbdb0e..ca271db 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Filter.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Filter.java
@@ -29,6 +29,7 @@ import org.apache.calcite.rex.RexChecker;
 import org.apache.calcite.rex.RexLocalRef;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexProgram;
+import org.apache.calcite.rex.RexShuttle;
 import org.apache.calcite.rex.RexUtil;
 
 import com.google.common.collect.ImmutableList;
@@ -96,6 +97,14 @@ public abstract class Filter extends SingleRel {
     return ImmutableList.of(condition);
   }
 
+  public RelNode accept(RexShuttle shuttle) {
+    RexNode condition = shuttle.apply(this.condition);
+    if (this.condition == condition) {
+      return this;
+    }
+    return copy(traitSet, getInput(), condition);
+  }
+
   public RexNode getCondition() {
     return condition;
   }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/rel/core/Join.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Join.java b/core/src/main/java/org/apache/calcite/rel/core/Join.java
index 9405a6e..1a6b5c1 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Join.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Join.java
@@ -29,6 +29,7 @@ import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexChecker;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexShuttle;
 import org.apache.calcite.sql.type.SqlTypeName;
 
 import com.google.common.collect.ImmutableList;
@@ -97,6 +98,14 @@ public abstract class Join extends BiRel {
     return ImmutableList.of(condition);
   }
 
+  public RelNode accept(RexShuttle shuttle) {
+    RexNode condition = shuttle.apply(this.condition);
+    if (this.condition == condition) {
+      return this;
+    }
+    return copy(traitSet, condition, left, right, joinType, isSemiJoinDone());
+  }
+
   public RexNode getCondition() {
     return condition;
   }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/rel/core/Project.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Project.java b/core/src/main/java/org/apache/calcite/rel/core/Project.java
index 9032ca3..ec3978b 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Project.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Project.java
@@ -38,6 +38,7 @@ import org.apache.calcite.rex.RexFieldAccess;
 import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexLocalRef;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexShuttle;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.rex.RexVisitorImpl;
 import org.apache.calcite.sql.SqlExplainLevel;
@@ -151,6 +152,14 @@ public abstract class Project extends SingleRel {
     return exps;
   }
 
+  public RelNode accept(RexShuttle shuttle) {
+    List<RexNode> exps = shuttle.apply(this.exps);
+    if (this.exps == exps) {
+      return this;
+    }
+    return copy(traitSet, getInput(), exps, rowType);
+  }
+
   /**
    * Returns the project expressions.
    *

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/rel/core/Sort.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Sort.java b/core/src/main/java/org/apache/calcite/rel/core/Sort.java
index 5c91666..445afb2 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Sort.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Sort.java
@@ -32,6 +32,7 @@ import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.SingleRel;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexShuttle;
 import org.apache.calcite.util.Util;
 
 import com.google.common.collect.ImmutableList;
@@ -162,6 +163,20 @@ public class Sort extends SingleRel {
     return fieldExps;
   }
 
+  public RelNode accept(RexShuttle shuttle) {
+    RexNode offset = shuttle.apply(this.offset);
+    RexNode fetch = shuttle.apply(this.fetch);
+    List<RexNode> fieldExps = shuttle.apply(this.fieldExps);
+    assert fieldExps == this.fieldExps
+        : "Sort node does not support modification of input field expressions."
+          + " Old expressions: " + this.fieldExps + ", new ones: " + fieldExps;
+    if (offset == this.offset
+        && fetch == this.fetch) {
+      return this;
+    }
+    return copy(traitSet, getInput(), collation, offset, fetch);
+  }
+
   /**
    * Returns the array of {@link RelFieldCollation}s asked for by the sort
    * specification, from most significant to least significant.

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/rel/core/TableFunctionScan.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/TableFunctionScan.java b/core/src/main/java/org/apache/calcite/rel/core/TableFunctionScan.java
index 282b1f3..bffa50f 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/TableFunctionScan.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/TableFunctionScan.java
@@ -27,6 +27,7 @@ import org.apache.calcite.rel.metadata.RelColumnMapping;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexShuttle;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -63,18 +64,19 @@ public abstract class TableFunctionScan extends AbstractRelNode {
    *
    * @param cluster        Cluster that this relational expression belongs to
    * @param inputs         0 or more relational inputs
-   * @param rexCall        function invocation expression
-   * @param elementType    element type of the collection that will implement
+   * @param rexCall        Function invocation expression
+   * @param elementType    Element type of the collection that will implement
    *                       this table
-   * @param rowType        row type produced by function
-   * @param columnMappings column mappings associated with this function
+   * @param rowType        Row type produced by function
+   * @param columnMappings Column mappings associated with this function
    */
   protected TableFunctionScan(
       RelOptCluster cluster,
       RelTraitSet traits,
       List<RelNode> inputs,
       RexNode rexCall,
-      Type elementType, RelDataType rowType,
+      Type elementType,
+      RelDataType rowType,
       Set<RelColumnMapping> columnMappings) {
     super(cluster, traits);
     this.rexCall = rexCall;
@@ -98,6 +100,34 @@ public abstract class TableFunctionScan extends AbstractRelNode {
 
   //~ Methods ----------------------------------------------------------------
 
+  @Override public final TableFunctionScan copy(RelTraitSet traitSet,
+      List<RelNode> inputs) {
+    return copy(traitSet, inputs, rexCall, elementType, rowType,
+        columnMappings);
+  }
+
+  /**
+   * Copies this relational expression, substituting traits and
+   * inputs.
+   *
+   * @param traitSet       Traits
+   * @param inputs         0 or more relational inputs
+   * @param rexCall        Function invocation expression
+   * @param elementType    Element type of the collection that will implement
+   *                       this table
+   * @param rowType        Row type produced by function
+   * @param columnMappings Column mappings associated with this function
+   * @return Copy of this relational expression, substituting traits and
+   * inputs
+   */
+  public abstract TableFunctionScan copy(
+      RelTraitSet traitSet,
+      List<RelNode> inputs,
+      RexNode rexCall,
+      Type elementType,
+      RelDataType rowType,
+      Set<RelColumnMapping> columnMappings);
+
   @Override public List<RelNode> getInputs() {
     return inputs;
   }
@@ -106,6 +136,15 @@ public abstract class TableFunctionScan extends AbstractRelNode {
     return ImmutableList.of(rexCall);
   }
 
+  public RelNode accept(RexShuttle shuttle) {
+    RexNode rexCall = shuttle.apply(this.rexCall);
+    if (rexCall == this.rexCall) {
+      return this;
+    }
+    return copy(traitSet, inputs, rexCall, elementType, rowType,
+        columnMappings);
+  }
+
   @Override public void replaceInput(int ordinalInParent, RelNode p) {
     final List<RelNode> newInputs = new ArrayList<RelNode>(inputs);
     newInputs.set(ordinalInParent, p);
@@ -132,10 +171,16 @@ public abstract class TableFunctionScan extends AbstractRelNode {
     return nRows;
   }
 
+  /**
+   * Returns function invocation expression.
+   *
+   * <p>Within this rexCall, instances of
+   * {@link org.apache.calcite.rex.RexInputRef} refer to entire input
+   * {@link org.apache.calcite.rel.RelNode}s rather than their fields.
+   *
+   * @return function invocation expression
+   */
   public RexNode getCall() {
-    // NOTE jvs 7-May-2006:  Within this rexCall, instances
-    // of RexInputRef refer to entire input RelNodes rather
-    // than their fields.
     return rexCall;
   }
 
@@ -153,6 +198,9 @@ public abstract class TableFunctionScan extends AbstractRelNode {
   }
 
   /**
+   * Returns set of mappings known for this table function, or null if unknown
+   * (not the same as empty!).
+   *
    * @return set of mappings known for this table function, or null if unknown
    * (not the same as empty!)
    */
@@ -162,6 +210,7 @@ public abstract class TableFunctionScan extends AbstractRelNode {
 
   /**
    * Returns element type of the collection that will implement this table.
+   *
    * @return element type of the collection that will implement this table
    */
   public Type getElementType() {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java
index e39e421..63fb3ab 100644
--- a/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java
+++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java
@@ -21,7 +21,6 @@ import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
-import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.sql.SqlExplainLevel;
 import org.apache.calcite.util.Pair;
 
@@ -154,6 +153,16 @@ public class RelWriterImpl implements RelWriter {
   }
 
   public RelWriter done(RelNode node) {
+    assert checkInputsPresentInExplain(node);
+    final List<Pair<String, Object>> valuesCopy =
+        ImmutableList.copyOf(values);
+    values.clear();
+    explain_(node, valuesCopy);
+    pw.flush();
+    return this;
+  }
+
+  private boolean checkInputsPresentInExplain(RelNode node) {
     int i = 0;
     if (values.size() > 0 && values.get(0).left.equals("subset")) {
       ++i;
@@ -162,16 +171,7 @@ public class RelWriterImpl implements RelWriter {
       assert values.get(i).right == input;
       ++i;
     }
-    for (RexNode expr : node.getChildExps()) {
-      assert values.get(i).right == expr;
-      ++i;
-    }
-    final List<Pair<String, Object>> valuesCopy =
-        ImmutableList.copyOf(values);
-    values.clear();
-    explain_(node, valuesCopy);
-    pw.flush();
-    return this;
+    return true;
   }
 
   public boolean nest() {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableFunctionScan.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableFunctionScan.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableFunctionScan.java
index de0d1fa..e4e02f6 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableFunctionScan.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableFunctionScan.java
@@ -75,14 +75,20 @@ public class LogicalTableFunctionScan extends TableFunctionScan {
 
   //~ Methods ----------------------------------------------------------------
 
-  @Override public LogicalTableFunctionScan copy(RelTraitSet traitSet,
-      List<RelNode> inputs) {
+  @Override public LogicalTableFunctionScan copy(
+      RelTraitSet traitSet,
+      List<RelNode> inputs,
+      RexNode rexCall,
+      Type elementType,
+      RelDataType rowType,
+      Set<RelColumnMapping> columnMappings) {
     assert traitSet.containsIfApplicable(Convention.NONE);
     return new LogicalTableFunctionScan(
         getCluster(),
         inputs,
-        getCall(),
-        getElementType(), getRowType(),
+        rexCall,
+        elementType,
+        rowType,
         columnMappings);
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/rel/rules/MultiJoin.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoin.java b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoin.java
index ce651bc..153f31b 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoin.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoin.java
@@ -26,6 +26,7 @@ import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.core.JoinRelType;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexShuttle;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.ImmutableNullableList;
@@ -182,6 +183,30 @@ public final class MultiJoin extends AbstractRelNode {
     return ImmutableList.of(joinFilter);
   }
 
+  public RelNode accept(RexShuttle shuttle) {
+    RexNode joinFilter = shuttle.apply(this.joinFilter);
+    List<RexNode> outerJoinConditions = shuttle.apply(this.outerJoinConditions);
+    RexNode postJoinFilter = shuttle.apply(this.postJoinFilter);
+
+    if (joinFilter == this.joinFilter
+        && outerJoinConditions == this.outerJoinConditions
+        && postJoinFilter == this.postJoinFilter) {
+      return this;
+    }
+
+    return new MultiJoin(
+        getCluster(),
+        inputs,
+        joinFilter,
+        rowType,
+        isFullOuterJoin,
+        outerJoinConditions,
+        joinTypes,
+        projFields,
+        joinFieldRefCountsMap,
+        postJoinFilter);
+  }
+
   /**
    * @return join filters associated with this MultiJoin
    */

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
index c41a99d..f5a0ef7 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
@@ -88,7 +88,8 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
           "ReduceExpressionsRule(Filter)") {
         public void onMatch(RelOptRuleCall call) {
           LogicalFilter filter = call.rel(0);
-          List<RexNode> expList = new ArrayList<RexNode>(filter.getChildExps());
+          List<RexNode> expList = new ArrayList<RexNode>(1);
+          expList.add(filter.getCondition());
           RexNode newConditionExp;
           boolean reduced;
           if (reduceExpressions(filter, expList)) {
@@ -100,7 +101,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
             // predicate to see if it was already a constant,
             // in which case we don't need any runtime decision
             // about filtering.
-            newConditionExp = filter.getChildExps().get(0);
+            newConditionExp = filter.getCondition();
             reduced = false;
           }
           if (newConditionExp.isAlwaysTrue()) {
@@ -178,7 +179,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
         public void onMatch(RelOptRuleCall call) {
           LogicalProject project = call.rel(0);
           List<RexNode> expList =
-              new ArrayList<RexNode>(project.getChildExps());
+              new ArrayList<RexNode>(project.getProjects());
           if (reduceExpressions(project, expList)) {
             call.transformTo(
                 new LogicalProject(
@@ -200,7 +201,8 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
           "ReduceExpressionsRule(Join)") {
         public void onMatch(RelOptRuleCall call) {
           final Join join = call.rel(0);
-          List<RexNode> expList = new ArrayList<RexNode>(join.getChildExps());
+          List<RexNode> expList = new ArrayList<RexNode>(1);
+          expList.add(join.getCondition());
           if (reduceExpressions(join, expList)) {
             call.transformTo(
                 join.copy(

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexShuttle.java b/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
index bd3ce46..9c6e46d 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
@@ -218,7 +218,7 @@ public class RexShuttle implements RexVisitor<RexNode> {
     int changeCount = 0;
     for (int i = 0; i < exprList.size(); i++) {
       T expr = exprList.get(i);
-      T expr2 = (T) expr.accept(this);
+      T expr2 = (T) apply(expr); // Avoid NPE if expr is null
       if (expr != expr2) {
         ++changeCount;
         exprList.set(i, expr2);
@@ -232,6 +232,9 @@ public class RexShuttle implements RexVisitor<RexNode> {
    * resulting list. Does not modify the initial list.
    */
   public final <T extends RexNode> List<T> apply(List<T> exprList) {
+    if (exprList == null) {
+      return null;
+    }
     final List<T> list2 = new ArrayList<T>(exprList);
     if (mutate(list2)) {
       return list2;

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/sql2rel/DeduplicateCorrelateVariables.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/DeduplicateCorrelateVariables.java b/core/src/main/java/org/apache/calcite/sql2rel/DeduplicateCorrelateVariables.java
index 206ed2b..2ce1048 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/DeduplicateCorrelateVariables.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/DeduplicateCorrelateVariables.java
@@ -16,24 +16,20 @@
  */
 package org.apache.calcite.sql2rel;
 
+import org.apache.calcite.rel.RelHomogeneousShuttle;
 import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.RelShuttleImpl;
-import org.apache.calcite.rel.logical.LogicalFilter;
-import org.apache.calcite.rel.logical.LogicalJoin;
-import org.apache.calcite.rel.logical.LogicalProject;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexCorrelVariable;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexShuttle;
 
-import java.util.List;
 import java.util.Set;
 
 /**
  * Rewrites relations to ensure the same correlation is referenced by the same
  * correlation variable.
  */
-public class DeduplicateCorrelateVariables extends RelShuttleImpl {
+public class DeduplicateCorrelateVariables extends RelHomogeneousShuttle {
   private final RexShuttle dedupRex;
 
   /**
@@ -67,40 +63,9 @@ public class DeduplicateCorrelateVariables extends RelShuttleImpl {
         canonical, altNames);
   }
 
-  @Override
-  public RelNode visit(LogicalFilter filter) {
-    LogicalFilter newFilter = (LogicalFilter) super.visit(filter);
-    RexNode condition = filter.getCondition();
-    RexNode newCondition = condition.accept(dedupRex);
-    if (condition != newCondition) {
-      return newFilter.copy(newFilter.getTraitSet(), newFilter.getInput(),
-          newCondition);
-    }
-    return newFilter;
-  }
-
-  @Override
-  public RelNode visit(LogicalProject project) {
-    LogicalProject project2 = (LogicalProject) super.visit(project);
-    List<RexNode> childExps = project2.getChildExps();
-    List<RexNode> newExps = dedupRex.apply(childExps);
-    if (childExps != newExps) {
-      return project2.copy(project2.getTraitSet(), project2.getInput(),
-          newExps, project2.getRowType());
-    }
-    return project2;
-  }
-
-  @Override
-  public RelNode visit(LogicalJoin join) {
-    LogicalJoin join2 = (LogicalJoin) super.visit(join);
-    RexNode condition = join2.getCondition();
-    RexNode newCondition = condition.accept(dedupRex);
-    if (condition != newCondition) {
-      return join2.copy(join2.getTraitSet(), newCondition, join2.getLeft(),
-          join2.getRight(), join2.getJoinType(), join2.isSemiJoinDone());
-    }
-    return join2;
+  @Override public RelNode visit(RelNode other) {
+    RelNode next = super.visit(other);
+    return next.accept(dedupRex);
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/288254b2/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
index dbbfa3f..4bfe35b 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
@@ -925,7 +925,9 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
 
     LogicalTableFunctionScan newTabFun = tabFun;
     if (!tabFun.getInputs().equals(newInputs)) {
-      newTabFun = tabFun.copy(tabFun.getTraitSet(), newInputs);
+      newTabFun = tabFun.copy(tabFun.getTraitSet(), newInputs,
+          tabFun.getCall(), tabFun.getElementType(), tabFun.getRowType(),
+          tabFun.getColumnMappings());
     }
     assert newTabFun.getClass() == tabFun.getClass();
 


[2/3] incubator-calcite git commit: [CALCITE-533] Support for grouping sets in FilterAggregateTransposeRule (Jesus Camacho Rodriguez)

Posted by jh...@apache.org.
[CALCITE-533] Support for grouping sets in FilterAggregateTransposeRule (Jesus Camacho Rodriguez)

Close apache/incubator-calcite#34


Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/86c7c081
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/86c7c081
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/86c7c081

Branch: refs/heads/master
Commit: 86c7c081107524aa67a3edbf26b554f4d1c99456
Parents: e8ee2b3
Author: Jesus Camacho Rodriguez <jc...@hortonworks.com>
Authored: Tue Dec 16 16:53:54 2014 +0100
Committer: Julian Hyde <jh...@apache.org>
Committed: Wed Dec 17 13:37:42 2014 -0800

----------------------------------------------------------------------
 .../rel/rules/FilterAggregateTransposeRule.java | 14 ++++-
 .../apache/calcite/test/RelOptRulesTest.java    | 21 ++++++++
 .../org/apache/calcite/test/RelOptRulesTest.xml | 56 ++++++++++++++++++++
 3 files changed, 90 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/86c7c081/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
index 8af01e3..0d10081 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
@@ -89,7 +89,19 @@ public class FilterAggregateTransposeRule extends RelOptRule {
 
     for (RexNode condition : conditions) {
       ImmutableBitSet rCols = RelOptUtil.InputFinder.bits(condition);
-      if (groupKeys.contains(rCols)) {
+      boolean push = groupKeys.contains(rCols);
+      if (push && aggRel.indicator) {
+        // If grouping sets are used, the filter can be pushed if
+        // the columns referenced in the predicate are present in
+        // all the grouping sets.
+        for (ImmutableBitSet groupingSet: aggRel.getGroupSets()) {
+          if (!groupingSet.contains(rCols)) {
+            push = false;
+            break;
+          }
+        }
+      }
+      if (push) {
         pushedConditions.add(
             condition.accept(
                 new RelOptUtil.RexInputConverter(rexBuilder, origFields,

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/86c7c081/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
----------------------------------------------------------------------
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 bfa65c9..cf78de9 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -207,6 +207,27 @@ public class RelOptRulesTest extends RelOptTestBase {
             + " where dname = 'Charlie'");
   }
 
+  private void basePushFilterPastAggWithGroupingSets() throws Exception {
+    final HepProgram preProgram =
+            HepProgram.builder()
+                .addRuleInstance(ProjectMergeRule.INSTANCE)
+                .addRuleInstance(FilterProjectTransposeRule.INSTANCE)
+                .build();
+    final HepProgram program =
+            HepProgram.builder()
+                .addRuleInstance(FilterAggregateTransposeRule.INSTANCE)
+                .build();
+    checkPlanning(tester, preProgram, new HepPlanner(program), "${sql}");
+  }
+
+  @Test public void testPushFilterPastAggWithGroupingSets1() throws Exception {
+    basePushFilterPastAggWithGroupingSets();
+  }
+
+  @Test public void testPushFilterPastAggWithGroupingSets2() throws Exception {
+    basePushFilterPastAggWithGroupingSets();
+  }
+
   /** Test case for
    * <a href="https://issues.apache.org/jira/browse/CALCITE-434">[CALCITE-434],
    * FilterAggregateTransposeRule loses conditions that cannot be pushed</a>. */

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/86c7c081/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
----------------------------------------------------------------------
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 9ffdaf4..4ddefe1 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -283,6 +283,62 @@ LogicalProject(DNAME=[$0], C=[$1])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testPushFilterPastAggWithGroupingSets1">
+        <Resource name="sql">
+            <![CDATA[select ddeptno, dname, c from
+             (select deptno ddeptno, name dname, count(*) as c
+             from dept group by rollup(deptno,name)) t
+              where dname = 'Charlie']]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(DDEPTNO=[$0], DNAME=[$1], C=[$2])
+  LogicalProject(DDEPTNO=[CASE($2, null, $0)], DNAME=[CASE($3, null, $1)], C=[$4])
+    LogicalFilter(condition=[=(CASE($3, null, $1), 'Charlie')])
+      LogicalAggregate(group=[{0, 1}], groups=[[{0, 1}, {0}, {}]], indicator=[true], C=[COUNT()])
+        LogicalProject(DDEPTNO=[$0], DNAME=[$1])
+          LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(DDEPTNO=[$0], DNAME=[$1], C=[$2])
+  LogicalProject(DDEPTNO=[CASE($2, null, $0)], DNAME=[CASE($3, null, $1)], C=[$4])
+    LogicalFilter(condition=[=(CASE($3, null, $1), 'Charlie')])
+      LogicalAggregate(group=[{0, 1}], groups=[[{0, 1}, {0}, {}]], indicator=[true], C=[COUNT()])
+        LogicalProject(DDEPTNO=[$0], DNAME=[$1])
+          LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testPushFilterPastAggWithGroupingSets2">
+        <Resource name="sql">
+            <![CDATA[select dname, ddeptno, c from
+             (select name dname, deptno ddeptno, count(*) as c
+             from dept group by grouping sets ((name,deptno),(name))) t
+              where dname = 'Charlie']]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(DNAME=[$0], DDEPTNO=[$1], C=[$2])
+  LogicalProject(DNAME=[$0], DDEPTNO=[CASE($3, null, $1)], C=[$4])
+    LogicalFilter(condition=[=($0, 'Charlie')])
+      LogicalAggregate(group=[{0, 1}], groups=[[{0, 1}, {0}]], indicator=[true], C=[COUNT()])
+        LogicalProject(DNAME=[$1], DDEPTNO=[$0])
+          LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(DNAME=[$0], DDEPTNO=[$1], C=[$2])
+  LogicalProject(DNAME=[$0], DDEPTNO=[CASE($3, null, $1)], C=[$4])
+    LogicalAggregate(group=[{0, 1}], groups=[[{0, 1}, {0}]], indicator=[true], C=[COUNT()])
+      LogicalFilter(condition=[=($0, 'Charlie')])
+        LogicalProject(DNAME=[$1], DDEPTNO=[$0])
+          LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testReduceAverage">
         <Resource name="sql">
             <![CDATA[select name, max(name), avg(deptno), min(name) from sales.dept group by name]]>