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/08/17 02:22:27 UTC

[4/4] git commit: [OPTIQ-343] RelDecorrelator should build its own mappings, not inherit from SqlToRelConverter

[OPTIQ-343] RelDecorrelator should build its own mappings, not inherit from SqlToRelConverter

Factor out CorrelatorRel.Correlation as a top-level class.

Add SqlExplainLevel.NON_COST_ATTRIBUTES, which includes "id".

Add RelShuttle.visit(CorrelatorRel).


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

Branch: refs/heads/master
Commit: 3055cc32fe752069e0ed45fff0d46081514a2d69
Parents: 0ad346f
Author: Julian Hyde <jh...@apache.org>
Authored: Fri Aug 15 13:42:32 2014 -0700
Committer: Julian Hyde <ju...@gmail.com>
Committed: Sat Aug 16 17:05:23 2014 -0700

----------------------------------------------------------------------
 .../optiq/prepare/OptiqMaterializer.java        |   3 +
 .../java/org/eigenbase/rel/Correlation.java     |  76 +++
 .../java/org/eigenbase/rel/CorrelatorRel.java   |  53 +-
 .../main/java/org/eigenbase/rel/RelJson.java    |   6 +-
 .../main/java/org/eigenbase/rel/RelShuttle.java |   2 +
 .../java/org/eigenbase/rel/RelShuttleImpl.java  |   4 +
 .../java/org/eigenbase/rel/RelWriterImpl.java   |   8 +-
 .../rel/rules/NestedLoopsJoinRule.java          |   8 +-
 .../rel/rules/PushFilterPastJoinRule.java       |  61 +-
 .../org/eigenbase/relopt/hep/HepRelVertex.java  |   4 +
 .../java/org/eigenbase/sql/SqlExplainLevel.java |   5 +
 .../org/eigenbase/sql2rel/RelDecorrelator.java  | 575 ++++++++++++-------
 .../sql2rel/RelStructuredTypeFlattener.java     |  31 +-
 .../eigenbase/sql2rel/SqlToRelConverter.java    | 162 +-----
 .../net/hydromatic/optiq/test/OptiqAssert.java  |  18 +-
 .../test/resources/mysql-foodmart-model.json    |  15 +
 .../hydromatic/optiq/impl/tpch/TpchTest.java    |  53 +-
 17 files changed, 593 insertions(+), 491 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/main/java/net/hydromatic/optiq/prepare/OptiqMaterializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/prepare/OptiqMaterializer.java b/core/src/main/java/net/hydromatic/optiq/prepare/OptiqMaterializer.java
index c00f307..ae57392 100644
--- a/core/src/main/java/net/hydromatic/optiq/prepare/OptiqMaterializer.java
+++ b/core/src/main/java/net/hydromatic/optiq/prepare/OptiqMaterializer.java
@@ -146,6 +146,9 @@ class OptiqMaterializer extends OptiqPrepareImpl.OptiqPreparingStmt {
     public RelNode visit(JoinRel join) {
       return join;
     }
+    public RelNode visit(CorrelatorRel correlator) {
+      return correlator;
+    }
     public RelNode visit(UnionRel union) {
       return union;
     }

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/main/java/org/eigenbase/rel/Correlation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/Correlation.java b/core/src/main/java/org/eigenbase/rel/Correlation.java
new file mode 100644
index 0000000..fef30ee
--- /dev/null
+++ b/core/src/main/java/org/eigenbase/rel/Correlation.java
@@ -0,0 +1,76 @@
+/*
+// 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.eigenbase.rel;
+
+/**
+ * Describes the necessary parameters for an implementation in order to
+ * identify and set dynamic variables
+ */
+public class Correlation implements Cloneable, Comparable<Correlation> {
+  private final int id;
+  private final int offset;
+
+  /**
+   * Creates a correlation.
+   *
+   * @param id     Identifier
+   * @param offset Offset
+   */
+  public Correlation(int id, int offset) {
+    this.id = id;
+    this.offset = offset;
+  }
+
+  /**
+   * Returns the identifier.
+   *
+   * @return identifier
+   */
+  public int getId() {
+    return id;
+  }
+
+  /**
+   * Returns this correlation's offset.
+   *
+   * @return offset
+   */
+  public int getOffset() {
+    return offset;
+  }
+
+  public String toString() {
+    return "var" + id + "=offset" + offset;
+  }
+
+  public int compareTo(Correlation other) {
+    return id - other.id;
+  }
+
+  @Override public int hashCode() {
+    return id;
+  }
+
+  @Override public boolean equals(Object obj) {
+    return this == obj
+        || obj instanceof Correlation
+        && this.id == ((Correlation) obj).id
+        && this.offset == ((Correlation) obj).offset;
+  }
+}
+
+// End Correlation.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/main/java/org/eigenbase/rel/CorrelatorRel.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/CorrelatorRel.java b/core/src/main/java/org/eigenbase/rel/CorrelatorRel.java
index cd25cf1..df9b2e3 100644
--- a/core/src/main/java/org/eigenbase/rel/CorrelatorRel.java
+++ b/core/src/main/java/org/eigenbase/rel/CorrelatorRel.java
@@ -133,6 +133,10 @@ public final class CorrelatorRel extends JoinRelBase {
         this.joinType);
   }
 
+  @Override public RelNode accept(RelShuttle shuttle) {
+    return shuttle.visit(this);
+  }
+
   public RelWriter explainTerms(RelWriter pw) {
     return super.explainTerms(pw)
         .item("correlations", correlations);
@@ -146,55 +150,6 @@ public final class CorrelatorRel extends JoinRelBase {
   public List<Correlation> getCorrelations() {
     return correlations;
   }
-
-  //~ Inner Classes ----------------------------------------------------------
-
-  /**
-   * Describes the neccessary parameters for an implementation in order to
-   * identify and set dynamic variables
-   */
-  public static class Correlation
-      implements Cloneable, Comparable<Correlation> {
-    private final int id;
-    private final int offset;
-
-    /**
-     * Creates a correlation.
-     *
-     * @param id     Identifier
-     * @param offset Offset
-     */
-    public Correlation(int id, int offset) {
-      this.id = id;
-      this.offset = offset;
-    }
-
-    /**
-     * Returns the identifier.
-     *
-     * @return identifier
-     */
-    public int getId() {
-      return id;
-    }
-
-    /**
-     * Returns this correlation's offset.
-     *
-     * @return offset
-     */
-    public int getOffset() {
-      return offset;
-    }
-
-    public String toString() {
-      return "var" + id + "=offset" + offset;
-    }
-
-    public int compareTo(Correlation other) {
-      return id - other.id;
-    }
-  }
 }
 
 // End CorrelatorRel.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/main/java/org/eigenbase/rel/RelJson.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/RelJson.java b/core/src/main/java/org/eigenbase/rel/RelJson.java
index 740dcee..6305270 100644
--- a/core/src/main/java/org/eigenbase/rel/RelJson.java
+++ b/core/src/main/java/org/eigenbase/rel/RelJson.java
@@ -205,8 +205,8 @@ public class RelJson {
       return value;
     } else if (value instanceof RexNode) {
       return toJson((RexNode) value);
-    } else if (value instanceof CorrelatorRel.Correlation) {
-      return toJson((CorrelatorRel.Correlation) value);
+    } else if (value instanceof Correlation) {
+      return toJson((Correlation) value);
     } else if (value instanceof List) {
       final List<Object> list = jsonBuilder.list();
       for (Object o : (List) value) {
@@ -261,7 +261,7 @@ public class RelJson {
     return map;
   }
 
-  private Object toJson(CorrelatorRel.Correlation node) {
+  private Object toJson(Correlation node) {
     final Map<String, Object> map = jsonBuilder.map();
     map.put("correlation", node.getId());
     map.put("offset", node.getOffset());

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/main/java/org/eigenbase/rel/RelShuttle.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/RelShuttle.java b/core/src/main/java/org/eigenbase/rel/RelShuttle.java
index e505ced..7f0b76f 100644
--- a/core/src/main/java/org/eigenbase/rel/RelShuttle.java
+++ b/core/src/main/java/org/eigenbase/rel/RelShuttle.java
@@ -32,6 +32,8 @@ public interface RelShuttle {
 
   RelNode visit(JoinRel join);
 
+  RelNode visit(CorrelatorRel correlator);
+
   RelNode visit(UnionRel union);
 
   RelNode visit(IntersectRel intersect);

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/main/java/org/eigenbase/rel/RelShuttleImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/RelShuttleImpl.java b/core/src/main/java/org/eigenbase/rel/RelShuttleImpl.java
index c5c06ef..2bba485 100644
--- a/core/src/main/java/org/eigenbase/rel/RelShuttleImpl.java
+++ b/core/src/main/java/org/eigenbase/rel/RelShuttleImpl.java
@@ -86,6 +86,10 @@ public class RelShuttleImpl implements RelShuttle {
     return visitChildren(join);
   }
 
+  public RelNode visit(CorrelatorRel correlator) {
+    return visitChildren(correlator);
+  }
+
   public RelNode visit(UnionRel union) {
     return visitChildren(union);
   }

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/main/java/org/eigenbase/rel/RelWriterImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/RelWriterImpl.java b/core/src/main/java/org/eigenbase/rel/RelWriterImpl.java
index 178f64c..4816f40 100644
--- a/core/src/main/java/org/eigenbase/rel/RelWriterImpl.java
+++ b/core/src/main/java/org/eigenbase/rel/RelWriterImpl.java
@@ -98,16 +98,22 @@ public class RelWriterImpl implements RelWriter {
         s.append(")");
       }
     }
-    if (detailLevel == SqlExplainLevel.ALL_ATTRIBUTES) {
+    switch (detailLevel) {
+    case ALL_ATTRIBUTES:
       s.append(": rowcount = ")
           .append(RelMetadataQuery.getRowCount(rel))
           .append(", cumulative cost = ")
           .append(RelMetadataQuery.getCumulativeCost(rel));
+    }
+    switch (detailLevel) {
+    case NON_COST_ATTRIBUTES:
+    case ALL_ATTRIBUTES:
       if (!withIdPrefix) {
         // If we didn't print the rel id at the start of the line, print
         // it at the end.
         s.append(", id = ").append(rel.getId());
       }
+      break;
     }
     pw.println(s);
     spacer.add(2);

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/main/java/org/eigenbase/rel/rules/NestedLoopsJoinRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/rules/NestedLoopsJoinRule.java b/core/src/main/java/org/eigenbase/rel/rules/NestedLoopsJoinRule.java
index 9f1cd39..e31f427 100644
--- a/core/src/main/java/org/eigenbase/rel/rules/NestedLoopsJoinRule.java
+++ b/core/src/main/java/org/eigenbase/rel/rules/NestedLoopsJoinRule.java
@@ -25,6 +25,8 @@ import org.eigenbase.sql.fun.*;
 import org.eigenbase.util.*;
 import org.eigenbase.util.mapping.IntPair;
 
+import com.google.common.collect.Lists;
+
 /**
  * Rule which converts a {@link JoinRel} into a {@link CorrelatorRel}, which can
  * then be implemented using nested loops.
@@ -83,8 +85,7 @@ public class NestedLoopsJoinRule extends RelOptRule {
     RelNode right = join.getRight();
     final RelNode left = join.getLeft();
     final JoinInfo joinInfo = join.analyzeCondition();
-    final List<CorrelatorRel.Correlation> correlationList =
-        new ArrayList<CorrelatorRel.Correlation>();
+    final List<Correlation> correlationList = Lists.newArrayList();
     if (joinInfo.leftKeys.size() > 0) {
       final RelOptCluster cluster = join.getCluster();
       final RexBuilder rexBuilder = cluster.getRexBuilder();
@@ -95,8 +96,7 @@ public class NestedLoopsJoinRule extends RelOptRule {
 
         // Create correlation to say 'each row, set variable #id
         // to the value of column #leftKey'.
-        correlationList.add(
-            new CorrelatorRel.Correlation(dynInId, p.source));
+        correlationList.add(new Correlation(dynInId, p.source));
         condition =
             RelOptUtil.andJoinFilters(
                 rexBuilder,

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/main/java/org/eigenbase/rel/rules/PushFilterPastJoinRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/rules/PushFilterPastJoinRule.java b/core/src/main/java/org/eigenbase/rel/rules/PushFilterPastJoinRule.java
index c1a19bd..7ecbd5b 100644
--- a/core/src/main/java/org/eigenbase/rel/rules/PushFilterPastJoinRule.java
+++ b/core/src/main/java/org/eigenbase/rel/rules/PushFilterPastJoinRule.java
@@ -21,7 +21,6 @@ import java.util.*;
 import org.eigenbase.rel.*;
 import org.eigenbase.relopt.*;
 import org.eigenbase.rex.*;
-import org.eigenbase.util.Bug;
 import org.eigenbase.util.Holder;
 
 import com.google.common.collect.ImmutableList;
@@ -71,22 +70,12 @@ public abstract class PushFilterPastJoinRule extends RelOptRule {
     final List<RexNode> joinFilters =
         RelOptUtil.conjunctions(join.getCondition());
 
-    if (filter == null) {
-      // There is only the joinRel
-      // make sure it does not match a cartesian product joinRel
-      // (with "true" condition) otherwise this rule will be applied
-      // again on the new cartesian product joinRel.
-      boolean onlyTrueFilter = true;
-      for (RexNode joinFilter : joinFilters) {
-        if (!joinFilter.isAlwaysTrue()) {
-          onlyTrueFilter = false;
-          break;
-        }
-      }
-
-      if (onlyTrueFilter) {
-        return;
-      }
+    // If there is only the joinRel,
+    // make sure it does not match a cartesian product joinRel
+    // (with "true" condition), otherwise this rule will be applied
+    // again on the new cartesian product joinRel.
+    if (filter == null && joinFilters.isEmpty()) {
+      return;
     }
 
     final List<RexNode> aboveFilters =
@@ -145,6 +134,14 @@ public abstract class PushFilterPastJoinRule extends RelOptRule {
       return;
     }
 
+    // if nothing actually got pushed and there is nothing leftover,
+    // then this rule is a no-op
+    if (joinFilters.isEmpty()
+        && leftFilters.isEmpty()
+        && rightFilters.isEmpty()) {
+      return;
+    }
+
     // create FilterRels on top of the children if any filters were
     // pushed to them
     final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
@@ -161,21 +158,18 @@ public abstract class PushFilterPastJoinRule extends RelOptRule {
 
     // create the new join node referencing the new children and
     // containing its new join filters (if there are any)
-    RexNode joinFilter;
-
-    if (joinFilters.size() == 0) {
-      // if nothing actually got pushed and there is nothing leftover,
-      // then this rule is a no-op
-      if (leftFilters.isEmpty()
-          && rightFilters.isEmpty()
-          && joinTypeHolder.get() == join.getJoinType()) {
-        return;
-      }
-      joinFilter = rexBuilder.makeLiteral(true);
-    } else {
-      joinFilter =
-          RexUtil.composeConjunction(rexBuilder, joinFilters, true);
+    final RexNode joinFilter =
+        RexUtil.composeConjunction(rexBuilder, joinFilters, false);
+
+    // If nothing actually got pushed and there is nothing leftover,
+    // then this rule is a no-op
+    if (joinFilter.isAlwaysTrue()
+        && leftFilters.isEmpty()
+        && rightFilters.isEmpty()
+        && joinTypeHolder.get() == join.getJoinType()) {
+      return;
     }
+
     RelNode newJoinRel =
         join.copy(
             join.getCluster().traitSetOf(Convention.NONE),
@@ -185,11 +179,6 @@ public abstract class PushFilterPastJoinRule extends RelOptRule {
             joinTypeHolder.get(),
             join.isSemiJoinDone());
     call.getPlanner().onCopy(join, newJoinRel);
-
-    // The pushed filters are not exact copies of the original filter, but
-    // telling the planner about them seems to help the RelDecorrelator more
-    // often than not. The real solution is to fix OPTIQ-443.
-    Bug.upgrade("OPTIQ-443");
     if (!leftFilters.isEmpty()) {
       call.getPlanner().onCopy(filter, leftRel);
     }

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/main/java/org/eigenbase/relopt/hep/HepRelVertex.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/relopt/hep/HepRelVertex.java b/core/src/main/java/org/eigenbase/relopt/hep/HepRelVertex.java
index 61053c8..dc44875 100644
--- a/core/src/main/java/org/eigenbase/relopt/hep/HepRelVertex.java
+++ b/core/src/main/java/org/eigenbase/relopt/hep/HepRelVertex.java
@@ -48,6 +48,10 @@ public class HepRelVertex extends AbstractRelNode {
 
   //~ Methods ----------------------------------------------------------------
 
+  @Override public void explain(RelWriter pw) {
+    currentRel.explain(pw);
+  }
+
   @Override
   public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
     assert traitSet.equals(this.traitSet);

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/main/java/org/eigenbase/sql/SqlExplainLevel.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/SqlExplainLevel.java b/core/src/main/java/org/eigenbase/sql/SqlExplainLevel.java
index d75a2ee..c50b5aa 100644
--- a/core/src/main/java/org/eigenbase/sql/SqlExplainLevel.java
+++ b/core/src/main/java/org/eigenbase/sql/SqlExplainLevel.java
@@ -38,6 +38,11 @@ public enum SqlExplainLevel implements SqlLiteral.SqlSymbol {
   DIGEST_ATTRIBUTES,
 
   /**
+   * Display all attributes, including id, except cost.
+   */
+  NON_COST_ATTRIBUTES,
+
+  /**
    * Display all attributes, including cost.
    */
   ALL_ATTRIBUTES;

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/main/java/org/eigenbase/sql2rel/RelDecorrelator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql2rel/RelDecorrelator.java b/core/src/main/java/org/eigenbase/sql2rel/RelDecorrelator.java
index 6d9a853..c47b760 100644
--- a/core/src/main/java/org/eigenbase/sql2rel/RelDecorrelator.java
+++ b/core/src/main/java/org/eigenbase/sql2rel/RelDecorrelator.java
@@ -21,7 +21,7 @@ import java.util.*;
 import java.util.logging.*;
 
 import org.eigenbase.rel.*;
-import org.eigenbase.rel.CorrelatorRel.Correlation;
+import org.eigenbase.rel.Correlation;
 import org.eigenbase.rel.metadata.*;
 import org.eigenbase.rel.rules.PushFilterPastJoinRule;
 import org.eigenbase.relopt.*;
@@ -39,13 +39,24 @@ import net.hydromatic.linq4j.function.Function2;
 
 import net.hydromatic.optiq.util.BitSets;
 
-import com.google.common.collect.ImmutableSet;
+import com.google.common.base.Supplier;
+import com.google.common.collect.*;
 
 /**
- * RelDecorrelator replaces all correlated expressions(corExp) in a relational
+ * RelDecorrelator replaces all correlated expressions (corExp) in a relational
  * expression (RelNode) tree with non-correlated expressions that are produced
  * from joining the RelNode that produces the corExp with the RelNode that
  * references it.
+ *
+ * <p>TODO:</p>
+ * <ul>
+ *   <li>replace {@code CorelMap} constructor parameter with a RelNode
+ *   <li>make {@link #currentRel} immutable (would require a fresh
+ *      RelDecorrelator for each node being decorrelated)</li>
+ *   <li>make fields of {@link CorelMap} immutable</li>
+ *   <li>make sub-class rules static, and have them create their own
+ *   de-correlator</li>
+ * </ul>
  */
 public class RelDecorrelator implements ReflectiveVisitor {
   //~ Static fields/initializers ---------------------------------------------
@@ -55,15 +66,8 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
   //~ Instance fields --------------------------------------------------------
 
-  // maps built during translation
-  private final Map<RelNode, SortedSet<CorrelatorRel.Correlation>>
-  mapRefRelToCorVar;
-
-  private final SortedMap<CorrelatorRel.Correlation, CorrelatorRel>
-  mapCorVarToCorRel;
-
-  private final Map<RexFieldAccess, CorrelatorRel.Correlation>
-  mapFieldAccessToCorVar;
+  // map built during translation
+  private CorelMap cm;
 
   private final DecorrelateRelVisitor decorrelateVisitor;
 
@@ -75,46 +79,80 @@ public class RelDecorrelator implements ReflectiveVisitor {
   private final Context context;
 
   // maps built during decorrelation
-  private final Map<RelNode, RelNode> mapOldToNewRel;
+  private final Map<RelNode, RelNode> mapOldToNewRel = Maps.newHashMap();
 
   // map rel to all the newly created correlated variables in its output
-  private final Map<RelNode, SortedMap<CorrelatorRel.Correlation, Integer>>
-  mapNewRelToMapCorVarToOutputPos;
+  private final Map<RelNode, SortedMap<Correlation, Integer>>
+  mapNewRelToMapCorVarToOutputPos = Maps.newHashMap();
 
   // another map to map old input positions to new input positions
   // this is from the view point of the parent rel of a new rel.
   private final Map<RelNode, Map<Integer, Integer>>
-  mapNewRelToMapOldToNewOutputPos;
+  mapNewRelToMapOldToNewOutputPos = Maps.newHashMap();
 
-  private final HashSet<CorrelatorRel> generatedCorRels =
-      new HashSet<CorrelatorRel>();
+  private final HashSet<CorrelatorRel> generatedCorRels = Sets.newHashSet();
 
   //~ Constructors -----------------------------------------------------------
 
-  public RelDecorrelator(
+  private RelDecorrelator(
       RexBuilder rexBuilder,
-      Map<RelNode, SortedSet<CorrelatorRel.Correlation>> mapRefRelToCorVar,
-      SortedMap<CorrelatorRel.Correlation, CorrelatorRel> mapCorVarToCorRel,
-      Map<RexFieldAccess, CorrelatorRel.Correlation> mapFieldAccessToCorVar,
+      CorelMap cm,
       Context context) {
+    this.cm = cm;
     this.rexBuilder = rexBuilder;
-    this.mapRefRelToCorVar = mapRefRelToCorVar;
-    this.mapCorVarToCorRel = mapCorVarToCorRel;
-    this.mapFieldAccessToCorVar = mapFieldAccessToCorVar;
     this.context = context;
 
     decorrelateVisitor = new DecorrelateRelVisitor();
-    mapOldToNewRel = new HashMap<RelNode, RelNode>();
-    mapNewRelToMapCorVarToOutputPos =
-        new HashMap<RelNode,
-            SortedMap<CorrelatorRel.Correlation, Integer>>();
-    mapNewRelToMapOldToNewOutputPos =
-        new HashMap<RelNode, Map<Integer, Integer>>();
   }
 
   //~ Methods ----------------------------------------------------------------
 
-  public RelNode decorrelate(RelNode root) {
+  /** Decorrelates a query.
+   *
+   * <p>This is the main entry point to {@code RelDecorrelator}.
+   *
+   * @param rootRel Root node of the query
+   * @return Equivalent query with all {@link CorrelatorRel} instances removed
+   */
+  public static RelNode decorrelateQuery(RelNode rootRel) {
+    final CorelMap corelMap = CorelMap.build(rootRel);
+    if (!corelMap.hasCorrelation()) {
+      return rootRel;
+    }
+
+    final RelOptCluster cluster = rootRel.getCluster();
+    final RexBuilder rexBuilder = cluster.getRexBuilder();
+    final RelDecorrelator decorrelator =
+        new RelDecorrelator(rexBuilder, corelMap,
+            cluster.getPlanner().getContext());
+
+
+    RelNode newRootRel = decorrelator.removeCorrelationViaRule(rootRel);
+
+    if (SQL2REL_LOGGER.isLoggable(Level.FINE)) {
+      SQL2REL_LOGGER.fine(
+          RelOptUtil.dumpPlan(
+              "Plan after removing CorrelatorRel",
+              newRootRel,
+              false,
+              SqlExplainLevel.EXPPLAN_ATTRIBUTES));
+    }
+
+    if (!decorrelator.cm.mapCorVarToCorRel.isEmpty()) {
+      newRootRel = decorrelator.decorrelate(newRootRel);
+    }
+
+    return newRootRel;
+  }
+
+  private void setCurrent(RelNode root, CorrelatorRel corRel) {
+    currentRel = corRel;
+    if (corRel != null) {
+      cm = CorelMap.build(Util.first(root, corRel));
+    }
+  }
+
+  private RelNode decorrelate(RelNode root) {
     // first adjust count() expression if any
     HepProgram program = HepProgram.builder()
         .addRuleInstance(new AdjustProjectForCountAggregateRule(false))
@@ -146,16 +184,16 @@ public class RelDecorrelator implements ReflectiveVisitor {
   private Function2<RelNode, RelNode, Void> createCopyHook() {
     return new Function2<RelNode, RelNode, Void>() {
       public Void apply(RelNode oldNode, RelNode newNode) {
-        if (mapRefRelToCorVar.containsKey(oldNode)) {
-          mapRefRelToCorVar.put(
-              newNode, mapRefRelToCorVar.get(oldNode));
+        if (cm.mapRefRelToCorVar.containsKey(oldNode)) {
+          cm.mapRefRelToCorVar.putAll(newNode,
+              cm.mapRefRelToCorVar.get(oldNode));
         }
         if (oldNode instanceof CorrelatorRel
             && newNode instanceof CorrelatorRel) {
           CorrelatorRel oldCor = (CorrelatorRel) oldNode;
           for (Correlation c : oldCor.getCorrelations()) {
-            if (mapCorVarToCorRel.get(c) == oldNode) {
-              mapCorVarToCorRel.put(c, (CorrelatorRel) newNode);
+            if (cm.mapCorVarToCorRel.get(c) == oldNode) {
+              cm.mapCorVarToCorRel.put(c, (CorrelatorRel) newNode);
             }
           }
 
@@ -238,7 +276,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
     if (rel.getInputs().size() > 0) {
       List<RelNode> oldInputs = rel.getInputs();
-      List<RelNode> newInputs = new ArrayList<RelNode>();
+      List<RelNode> newInputs = Lists.newArrayList();
       for (int i = 0; i < oldInputs.size(); ++i) {
         RelNode newInputRel = mapOldToNewRel.get(oldInputs.get(i));
         if ((newInputRel == null)
@@ -257,10 +295,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
       }
     }
 
-    // the output position should not change since there're no corVars
+    // the output position should not change since there are no corVars
     // coming from below.
-    Map<Integer, Integer> mapOldToNewOutputPos =
-        new HashMap<Integer, Integer>();
+    Map<Integer, Integer> mapOldToNewOutputPos = Maps.newHashMap();
     for (int i = 0; i < rel.getRowType().getFieldCount(); i++) {
       mapOldToNewOutputPos.put(i, i);
     }
@@ -281,7 +318,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
     //
 
     // SortRel itself should not reference cor vars.
-    assert !mapRefRelToCorVar.containsKey(rel);
+    assert !cm.mapRefRelToCorVar.containsKey(rel);
 
     // SortRel only references field positions in collations field.
     // The collations field in the newRel now need to refer to the
@@ -340,7 +377,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
     //
 
     // AggregaterRel itself should not reference cor vars.
-    assert !mapRefRelToCorVar.containsKey(rel);
+    assert !cm.mapRefRelToCorVar.containsKey(rel);
 
     RelNode oldChildRel = rel.getChild();
 
@@ -355,14 +392,12 @@ public class RelDecorrelator implements ReflectiveVisitor {
     assert childMapOldToNewOutputPos != null;
 
     // map from newChildRel
-    Map<Integer, Integer> mapNewChildToProjOutputPos =
-        new HashMap<Integer, Integer>();
+    Map<Integer, Integer> mapNewChildToProjOutputPos = Maps.newHashMap();
     final int oldGroupKeyCount = rel.getGroupSet().cardinality();
 
     // ProjectRel projects the original expressions,
     // plus any correlated variables the child wants to pass along.
-    final List<Pair<RexNode, String>> projects =
-        new ArrayList<Pair<RexNode, String>>();
+    final List<Pair<RexNode, String>> projects = Lists.newArrayList();
 
     List<RelDataTypeField> newChildOutput =
         newChildRel.getRowType().getFieldList();
@@ -376,8 +411,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
       mapNewChildToProjOutputPos.put(newChildPos, newPos);
     }
 
-    SortedMap<CorrelatorRel.Correlation, Integer> mapCorVarToOutputPos =
-        new TreeMap<CorrelatorRel.Correlation, Integer>();
+    SortedMap<Correlation, Integer> mapCorVarToOutputPos = Maps.newTreeMap();
 
     boolean produceCorVar =
         mapNewRelToMapCorVarToOutputPos.containsKey(newChildRel);
@@ -385,13 +419,12 @@ public class RelDecorrelator implements ReflectiveVisitor {
       // If child produces correlated variables, move them to the front,
       // right after any existing groupby fields.
 
-      SortedMap<CorrelatorRel.Correlation, Integer>
-          childMapCorVarToOutputPos =
+      SortedMap<Correlation, Integer> childMapCorVarToOutputPos =
           mapNewRelToMapCorVarToOutputPos.get(newChildRel);
 
       // Now add the corVars from the child, starting from
       // position oldGroupKeyCount.
-      for (CorrelatorRel.Correlation corVar
+      for (Correlation corVar
           : childMapCorVarToOutputPos.keySet()) {
         int newChildPos = childMapCorVarToOutputPos.get(corVar);
         projects.add(RexInputRef.of2(newChildPos, newChildOutput));
@@ -431,7 +464,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
     // oldChildRel ----> newProjectRel
     //                        |
     //                   newChildRel
-    Map<Integer, Integer> combinedMap = new HashMap<Integer, Integer>();
+    Map<Integer, Integer> combinedMap = Maps.newHashMap();
 
     for (Integer oldChildPos : childMapOldToNewOutputPos.keySet()) {
       combinedMap.put(
@@ -450,7 +483,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
     }
 
     // now it's time to rewrite AggregateRel
-    List<AggregateCall> newAggCalls = new ArrayList<AggregateCall>();
+    List<AggregateCall> newAggCalls = Lists.newArrayList();
     List<AggregateCall> oldAggCalls = rel.getAggCallList();
 
     // AggregateRel.Call oldAggCall;
@@ -463,7 +496,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
       ++i;
       List<Integer> oldAggArgs = oldAggCall.getArgList();
 
-      List<Integer> aggArgs = new ArrayList<Integer>();
+      List<Integer> aggArgs = Lists.newArrayList();
 
       // Adjust the aggregator argument positions.
       // Note aggregator does not change input ordering, so the child
@@ -473,8 +506,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
         aggArgs.add(combinedMap.get(oldPos));
       }
 
-      newAggCalls.add(oldAggCall.adaptTo(newProjectRel, aggArgs,
-          oldGroupKeyCount, newGroupKeyCount));
+      newAggCalls.add(
+          oldAggCall.adaptTo(newProjectRel, aggArgs,
+              oldGroupKeyCount, newGroupKeyCount));
 
       // The old to new output position mapping will be the same as that
       // of newProjectRel, plus any aggregates that the oldAgg produces.
@@ -529,20 +563,18 @@ public class RelDecorrelator implements ReflectiveVisitor {
         mapNewRelToMapOldToNewOutputPos.get(newChildRel);
     assert childMapOldToNewOutputPos != null;
 
-    Map<Integer, Integer> mapOldToNewOutputPos =
-        new HashMap<Integer, Integer>();
+    Map<Integer, Integer> mapOldToNewOutputPos = Maps.newHashMap();
 
     boolean produceCorVar =
         mapNewRelToMapCorVarToOutputPos.containsKey(newChildRel);
 
     // ProjectRel projects the original expressions,
     // plus any correlated variables the child wants to pass along.
-    final List<Pair<RexNode, String>> projects =
-        new ArrayList<Pair<RexNode, String>>();
+    final List<Pair<RexNode, String>> projects = Lists.newArrayList();
 
     // If this ProjectRel has correlated reference, create value generator
     // and produce the correlated variables in the new output.
-    if (mapRefRelToCorVar.containsKey(rel)) {
+    if (cm.mapRefRelToCorVar.containsKey(rel)) {
       decorrelateInputWithValueGenerator(rel);
 
       // The old child should be mapped to the JoinRel created by
@@ -562,19 +594,17 @@ public class RelDecorrelator implements ReflectiveVisitor {
       mapOldToNewOutputPos.put(newPos, newPos);
     }
 
-    SortedMap<CorrelatorRel.Correlation, Integer> mapCorVarToOutputPos =
-        new TreeMap<CorrelatorRel.Correlation, Integer>();
+    SortedMap<Correlation, Integer> mapCorVarToOutputPos = Maps.newTreeMap();
 
     // Project any correlated variables the child wants to pass along.
     if (produceCorVar) {
-      SortedMap<CorrelatorRel.Correlation, Integer>
-          childMapCorVarToOutputPos =
+      SortedMap<Correlation, Integer> childMapCorVarToOutputPos =
           mapNewRelToMapCorVarToOutputPos.get(newChildRel);
 
       // propagate cor vars from the new child
       List<RelDataTypeField> newChildOutput =
           newChildRel.getRowType().getFieldList();
-      for (CorrelatorRel.Correlation corVar
+      for (Correlation corVar
           : childMapCorVarToOutputPos.keySet()) {
         int corVarPos = childMapCorVarToOutputPos.get(corVar);
         projects.add(RexInputRef.of2(corVarPos, newChildOutput));
@@ -609,16 +639,14 @@ public class RelDecorrelator implements ReflectiveVisitor {
    * @return RelNode the root of the resultant RelNode tree
    */
   private RelNode createValueGenerator(
-      SortedSet<CorrelatorRel.Correlation> correlations,
+      Iterable<Correlation> correlations,
       int valueGenFieldOffset,
-      SortedMap<CorrelatorRel.Correlation, Integer> mapCorVarToOutputPos) {
+      SortedMap<Correlation, Integer> mapCorVarToOutputPos) {
     RelNode resultRel = null;
 
-    Map<RelNode, List<Integer>> mapNewInputRelToOutputPos =
-        new HashMap<RelNode, List<Integer>>();
+    Map<RelNode, List<Integer>> mapNewInputRelToOutputPos = Maps.newHashMap();
 
-    Map<RelNode, Integer> mapNewInputRelToNewOffset =
-        new HashMap<RelNode, Integer>();
+    Map<RelNode, Integer> mapNewInputRelToNewOffset = Maps.newHashMap();
 
     RelNode oldInputRel;
     RelNode newInputRel;
@@ -626,16 +654,16 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
     // inputRel provides the definition of a correlated variable.
     // Add to map all the referenced positions(relative to each input rel)
-    for (CorrelatorRel.Correlation corVar : correlations) {
+    for (Correlation corVar : correlations) {
       int oldCorVarOffset = corVar.getOffset();
 
-      oldInputRel = mapCorVarToCorRel.get(corVar).getInput(0);
+      oldInputRel = cm.mapCorVarToCorRel.get(corVar).getInput(0);
       assert oldInputRel != null;
       newInputRel = mapOldToNewRel.get(oldInputRel);
       assert newInputRel != null;
 
       if (!mapNewInputRelToOutputPos.containsKey(newInputRel)) {
-        newLocalOutputPosList = new ArrayList<Integer>();
+        newLocalOutputPosList = Lists.newArrayList();
       } else {
         newLocalOutputPosList =
             mapNewInputRelToOutputPos.get(newInputRel);
@@ -659,12 +687,12 @@ public class RelDecorrelator implements ReflectiveVisitor {
     // Project only the correlated fields out of each inputRel
     // and join the projectRel together.
     // To make sure the plan does not change in terms of join order,
-    // join these rels based on their occurance in cor var list which
+    // join these rels based on their occurrence in cor var list which
     // is sorted.
-    Set<RelNode> joinedInputRelSet = new HashSet<RelNode>();
+    Set<RelNode> joinedInputRelSet = Sets.newHashSet();
 
-    for (CorrelatorRel.Correlation corVar : correlations) {
-      oldInputRel = mapCorVarToCorRel.get(corVar).getInput(0);
+    for (Correlation corVar : correlations) {
+      oldInputRel = cm.mapCorVarToCorRel.get(corVar).getInput(0);
       assert oldInputRel != null;
       newInputRel = mapOldToNewRel.get(oldInputRel);
       assert newInputRel != null;
@@ -702,11 +730,11 @@ public class RelDecorrelator implements ReflectiveVisitor {
     // referencing correlated variables.
     int newOutputPos;
     int newLocalOutputPos;
-    for (CorrelatorRel.Correlation corVar : correlations) {
+    for (Correlation corVar : correlations) {
       // The first child of a correlatorRel is always the rel defining
       // the correlated variables.
       newInputRel =
-          mapOldToNewRel.get(mapCorVarToCorRel.get(corVar).getInput(0));
+          mapOldToNewRel.get(cm.mapCorVarToCorRel.get(corVar).getInput(0));
       newLocalOutputPosList = mapNewInputRelToOutputPos.get(newInputRel);
 
       Map<Integer, Integer> mapOldToNewOutputPos =
@@ -743,16 +771,14 @@ public class RelDecorrelator implements ReflectiveVisitor {
         mapNewRelToMapOldToNewOutputPos.get(newChildRel);
     assert childMapOldToNewOutputPos != null;
 
-    SortedMap<CorrelatorRel.Correlation, Integer> mapCorVarToOutputPos =
-        new TreeMap<CorrelatorRel.Correlation, Integer>();
+    SortedMap<Correlation, Integer> mapCorVarToOutputPos = Maps.newTreeMap();
 
     if (mapNewRelToMapCorVarToOutputPos.containsKey(newChildRel)) {
       mapCorVarToOutputPos.putAll(
           mapNewRelToMapCorVarToOutputPos.get(newChildRel));
     }
 
-    SortedSet<CorrelatorRel.Correlation> corVarList =
-        mapRefRelToCorVar.get(rel);
+    final Collection<Correlation> corVarList = cm.mapRefRelToCorVar.get(rel);
 
     RelNode newLeftChildRel = newChildRel;
 
@@ -824,7 +850,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
     // If this FilterRel has correlated reference, create value generator
     // and produce the correlated variables in the new output.
-    if (mapRefRelToCorVar.containsKey(rel)) {
+    if (cm.mapRefRelToCorVar.containsKey(rel)) {
       decorrelateInputWithValueGenerator(rel);
 
       // The old child should be mapped to the newly created JoinRel by
@@ -883,8 +909,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
       return;
     }
 
-    SortedMap<CorrelatorRel.Correlation, Integer>
-        rightChildMapCorVarToOutputPos =
+    SortedMap<Correlation, Integer> rightChildMapCorVarToOutputPos =
         mapNewRelToMapCorVarToOutputPos.get(newRightRel);
 
     if (rightChildMapCorVarToOutputPos == null) {
@@ -900,7 +925,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
     assert rightChildMapOldToNewOutputPos != null;
 
-    SortedMap<CorrelatorRel.Correlation, Integer> mapCorVarToOutputPos =
+    SortedMap<Correlation, Integer> mapCorVarToOutputPos =
         rightChildMapCorVarToOutputPos;
 
     assert rel.getCorrelations().size()
@@ -919,7 +944,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
     int newLeftPos;
     int newRightPos;
-    for (CorrelatorRel.Correlation corVar : rel.getCorrelations()) {
+    for (Correlation corVar : rel.getCorrelations()) {
       newLeftPos = leftChildMapOldToNewOutputPos.get(corVar.getOffset());
       newRightPos = rightChildMapCorVarToOutputPos.get(corVar);
       RexNode equi =
@@ -945,7 +970,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
     // Update the output position for the cor vars: only pass on the cor
     // vars that are not used in the join key.
-    for (CorrelatorRel.Correlation corVar : mapCorVarToOutputPos.keySet()) {
+    for (Correlation corVar : mapCorVarToOutputPos.keySet()) {
       int newPos = mapCorVarToOutputPos.get(corVar) + newLeftFieldCount;
       mapCorVarToOutputPos.put(corVar, newPos);
     }
@@ -959,8 +984,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
     // Create the mapping between the output of the old correlation rel
     // and the new join rel
-    Map<Integer, Integer> mapOldToNewOutputPos =
-        new HashMap<Integer, Integer>();
+    Map<Integer, Integer> mapOldToNewOutputPos = Maps.newHashMap();
 
     int oldLeftFieldCount = oldLeftRel.getRowType().getFieldCount();
 
@@ -1028,8 +1052,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
         mapNewRelToMapOldToNewOutputPos.get(newRightRel);
     assert rightChildMapOldToNewOutputPos != null;
 
-    SortedMap<CorrelatorRel.Correlation, Integer> mapCorVarToOutputPos =
-        new TreeMap<CorrelatorRel.Correlation, Integer>();
+    SortedMap<Correlation, Integer> mapCorVarToOutputPos = Maps.newTreeMap();
 
     final Set<String> variablesStopped = Collections.emptySet();
     RelNode newRel =
@@ -1043,8 +1066,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
     // Create the mapping between the output of the old correlation rel
     // and the new join rel
-    Map<Integer, Integer> mapOldToNewOutputPos =
-        new HashMap<Integer, Integer>();
+    Map<Integer, Integer> mapOldToNewOutputPos = Maps.newHashMap();
 
     int oldLeftFieldCount = oldLeftRel.getRowType().getFieldCount();
     int newLeftFieldCount = newLeftRel.getRowType().getFieldCount();
@@ -1071,11 +1093,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
     // Right input positions are shifted by newLeftFieldCount.
     int oldRightPos;
     if (mapNewRelToMapCorVarToOutputPos.containsKey(newRightRel)) {
-      SortedMap<CorrelatorRel.Correlation, Integer>
-          rightChildMapCorVarToOutputPos =
+      SortedMap<Correlation, Integer> rightChildMapCorVarToOutputPos =
           mapNewRelToMapCorVarToOutputPos.get(newRightRel);
-      for (CorrelatorRel.Correlation corVar
-          : rightChildMapCorVarToOutputPos.keySet()) {
+      for (Correlation corVar : rightChildMapCorVarToOutputPos.keySet()) {
         oldRightPos = rightChildMapCorVarToOutputPos.get(corVar);
         mapCorVarToOutputPos.put(
             corVar,
@@ -1133,10 +1153,8 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
     newOrdinal += newLocalOrdinal;
 
-    return new RexInputRef(
-        newOrdinal,
-        newInputRel.getRowType().getFieldList().get(newLocalOrdinal)
-            .getType());
+    return new RexInputRef(newOrdinal,
+        newInputRel.getRowType().getFieldList().get(newLocalOrdinal).getType());
   }
 
   /**
@@ -1144,7 +1162,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
    * for join output.
    *
    * @param joinRel          Join
-   * @param projRel          the orginal projRel as the RHS input of the join.
+   * @param projRel          the original projRel as the RHS input of the join.
    * @param nullIndicatorPos Position of null indicator
    * @return the subtree with the new ProjectRel at the root
    */
@@ -1165,8 +1183,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
                 true));
 
     // now create the new project
-    List<Pair<RexNode, String>> newProjExprs =
-        new ArrayList<Pair<RexNode, String>>();
+    List<Pair<RexNode, String>> newProjExprs = Lists.newArrayList();
 
     // project everything from the LHS and then those from the original
     // projRel
@@ -1204,7 +1221,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
    * for join output.
    *
    * @param corRel  Correlator
-   * @param projRel the orginal ProjectRel as the RHS input of the join
+   * @param projRel the original ProjectRel as the RHS input of the join
    * @param isCount Positions which are calls to the <code>COUNT</code>
    *                aggregation function
    * @return the subtree with the new ProjectRel at the root
@@ -1217,8 +1234,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
     JoinRelType joinType = corRel.getJoinType();
 
     // now create the new project
-    List<Pair<RexNode, String>> newProjects =
-        new ArrayList<Pair<RexNode, String>>();
+    List<Pair<RexNode, String>> newProjects = Lists.newArrayList();
 
     // project everything from the LHS and then those from the original
     // projRel
@@ -1252,7 +1268,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
    * the correlated variables provided by corRel.
    *
    * @param corRel             Correlator
-   * @param projRel            the orginal ProjectRel as the RHS input of the
+   * @param projRel            the original ProjectRel as the RHS input of the
    *                           join
    * @param filterRel          Filter
    * @param correlatedJoinKeys Correlated join keys
@@ -1268,13 +1284,12 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
       // check that all correlated refs in the filter condition are
       // used in the join(as field access).
-      HashSet<CorrelatorRel.Correlation> corVarInFilter =
-          new HashSet<CorrelatorRel.Correlation>();
-      corVarInFilter.addAll(mapRefRelToCorVar.get(filterRel));
+      Set<Correlation> corVarInFilter =
+          Sets.newHashSet(cm.mapRefRelToCorVar.get(filterRel));
 
       for (RexFieldAccess correlatedJoinKey : correlatedJoinKeys) {
         corVarInFilter.remove(
-            mapFieldAccessToCorVar.get(correlatedJoinKey));
+            cm.mapFieldAccessToCorVar.get(correlatedJoinKey));
       }
 
       if (!corVarInFilter.isEmpty()) {
@@ -1283,10 +1298,10 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
       // Check that the correlated variables referenced in these
       // comparisons do come from the correlatorRel.
-      corVarInFilter.addAll(mapRefRelToCorVar.get(filterRel));
+      corVarInFilter.addAll(cm.mapRefRelToCorVar.get(filterRel));
 
-      for (CorrelatorRel.Correlation corVar : corVarInFilter) {
-        if (mapCorVarToCorRel.get(corVar) != corRel) {
+      for (Correlation corVar : corVarInFilter) {
+        if (cm.mapCorVarToCorRel.get(corVar) != corRel) {
           return false;
         }
       }
@@ -1295,11 +1310,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
     // if projRel has any correlated reference, make sure they are also
     // provided by the current corRel. They will be projected out of the LHS
     // of the corRel.
-    if ((projRel != null) && mapRefRelToCorVar.containsKey(projRel)) {
-      SortedSet<CorrelatorRel.Correlation> corVarInProj =
-          mapRefRelToCorVar.get(projRel);
-      for (CorrelatorRel.Correlation corVar : corVarInProj) {
-        if (mapCorVarToCorRel.get(corVar) != corRel) {
+    if ((projRel != null) && cm.mapRefRelToCorVar.containsKey(projRel)) {
+      for (Correlation corVar : cm.mapRefRelToCorVar.get(projRel)) {
+        if (cm.mapCorVarToCorRel.get(corVar) != corRel) {
           return false;
         }
       }
@@ -1313,15 +1326,10 @@ public class RelDecorrelator implements ReflectiveVisitor {
    *
    * @param corRel Correlator
    */
-  private void removeCorVarFromTree(
-      CorrelatorRel corRel) {
-    HashSet<CorrelatorRel.Correlation> corVarSet =
-        new HashSet<CorrelatorRel.Correlation>();
-    corVarSet.addAll(mapCorVarToCorRel.keySet());
-
-    for (CorrelatorRel.Correlation corVar : corVarSet) {
-      if (mapCorVarToCorRel.get(corVar) == corRel) {
-        mapCorVarToCorRel.remove(corVar);
+  private void removeCorVarFromTree(CorrelatorRel corRel) {
+    for (Correlation c : Lists.newArrayList(cm.mapCorVarToCorRel.keySet())) {
+      if (cm.mapCorVarToCorRel.get(c) == corRel) {
+        cm.mapCorVarToCorRel.remove(c);
       }
     }
   }
@@ -1338,8 +1346,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
       List<Pair<RexNode, String>> additionalExprs) {
     final List<RelDataTypeField> fieldList =
         childRel.getRowType().getFieldList();
-    List<Pair<RexNode, String>> projects =
-        new ArrayList<Pair<RexNode, String>>();
+    List<Pair<RexNode, String>> projects = Lists.newArrayList();
     for (Ord<RelDataTypeField> field : Ord.zip(fieldList)) {
       projects.add(
           Pair.of(
@@ -1373,7 +1380,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
               RelDecorrelator.this,
               currentRel,
               visitMethodName);
-      currentRel = null;
+      setCurrent(null, null);
 
       if (!found) {
         decorrelateRelGeneric(p);
@@ -1399,14 +1406,12 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
         if ((newInputRel != null)
             && mapNewRelToMapCorVarToOutputPos.containsKey(newInputRel)) {
-          SortedMap<CorrelatorRel.Correlation, Integer>
-              childMapCorVarToOutputPos =
+          SortedMap<Correlation, Integer> childMapCorVarToOutputPos =
               mapNewRelToMapCorVarToOutputPos.get(newInputRel);
 
           if (childMapCorVarToOutputPos != null) {
             // try to find in this input rel the position of cor var
-            CorrelatorRel.Correlation corVar =
-                mapFieldAccessToCorVar.get(fieldAccess);
+            Correlation corVar = cm.mapFieldAccessToCorVar.get(fieldAccess);
 
             if (corVar != null) {
               newInputPos = childMapCorVarToOutputPos.get(corVar);
@@ -1543,12 +1548,11 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
     // override RexShuttle
     public RexNode visitFieldAccess(RexFieldAccess fieldAccess) {
-      if (mapFieldAccessToCorVar.containsKey(fieldAccess)) {
+      if (cm.mapFieldAccessToCorVar.containsKey(fieldAccess)) {
         // if it is a corVar, change it to be input ref.
-        CorrelatorRel.Correlation corVar =
-            mapFieldAccessToCorVar.get(fieldAccess);
+        Correlation corVar = cm.mapFieldAccessToCorVar.get(fieldAccess);
 
-        // corVar offset shuold point to the leftInput of currentRel,
+        // corVar offset should point to the leftInput of currentRel,
         // which is the CorrelatorRel.
         RexNode newRexNode =
             new RexInputRef(corVar.getOffset(), fieldAccess.getType());
@@ -1718,14 +1722,14 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
       // singleAggRel produces a nullable type, so create the new
       // projection that casts proj expr to a nullable type.
+      final RelOptCluster cluster = projRel.getCluster();
       RelNode newProjRel =
-          CalcRel.createProject(
-              aggRel,
-              Collections.singletonList(
+          CalcRel.createProject(aggRel,
+              ImmutableList.of(
                   rexBuilder.makeCast(
-                      projRel.getCluster().getTypeFactory()
-                          .createTypeWithNullability(
-                              projExprs.get(0).getType(), true),
+                      cluster.getTypeFactory().createTypeWithNullability(
+                          projExprs.get(0).getType(),
+                          true),
                       projExprs.get(0))),
               null);
       call.transformTo(newProjRel);
@@ -1753,7 +1757,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
       RelNode rightInputRel = call.rel(4);
       RelOptCluster cluster = corRel.getCluster();
 
-      currentRel = corRel;
+      setCurrent(call.getPlanner().getRoot(), corRel);
 
       // Check for this pattern.
       // The pattern matching could be simplified if rules can be applied
@@ -1789,7 +1793,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
       int nullIndicatorPos;
 
       if ((rightInputRel instanceof FilterRel)
-          && mapRefRelToCorVar.containsKey(rightInputRel)) {
+          && cm.mapRefRelToCorVar.containsKey(rightInputRel)) {
         // rightInputRel has this shape:
         //
         //       FilterRel (references corvar)
@@ -1817,8 +1821,8 @@ public class RelDecorrelator implements ReflectiveVisitor {
         // filterInputRel) and correlatedJoinKeys. correlatedJoinKeys
         // can be expressions, while rightJoinKeys need to be input
         // refs. These comparisons are AND'ed together.
-        List<RexNode> tmpRightJoinKeys = new ArrayList<RexNode>();
-        List<RexNode> correlatedJoinKeys = new ArrayList<RexNode>();
+        List<RexNode> tmpRightJoinKeys = Lists.newArrayList();
+        List<RexNode> correlatedJoinKeys = Lists.newArrayList();
         RelOptUtil.splitCorrelatedFilterCondition(
             filterRel,
             tmpRightJoinKeys,
@@ -1828,9 +1832,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
         // check that the columns referenced in these comparisons form
         // an unique key of the filterInputRel
         List<RexInputRef> rightJoinKeys = new ArrayList<RexInputRef>();
-        for (int i = 0; i < tmpRightJoinKeys.size(); i++) {
-          assert tmpRightJoinKeys.get(i) instanceof RexInputRef;
-          rightJoinKeys.add((RexInputRef) tmpRightJoinKeys.get(i));
+        for (RexNode key : tmpRightJoinKeys) {
+          assert key instanceof RexInputRef;
+          rightJoinKeys.add((RexInputRef) key);
         }
 
         // check that the columns referenced in rightJoinKeys form an
@@ -1857,11 +1861,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
         List<RexFieldAccess> correlatedKeyList =
             visitor.getFieldAccessList();
 
-        if (!checkCorVars(
-            corRel,
-            projRel,
-            filterRel,
-            correlatedKeyList)) {
+        if (!checkCorVars(corRel, projRel, filterRel, correlatedKeyList)) {
           return;
         }
 
@@ -1880,7 +1880,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
         nullIndicatorPos =
             leftInputRel.getRowType().getFieldCount()
                 + rightJoinKeys.get(0).getIndex();
-      } else if (mapRefRelToCorVar.containsKey(projRel)) {
+      } else if (cm.mapRefRelToCorVar.containsKey(projRel)) {
         // check filter input contains no correlation
         if (RelOptUtil.getVariablesUsed(rightInputRel).size() > 0) {
           return;
@@ -1901,12 +1901,10 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
         // make the new projRel to provide a null indicator
         rightInputRel =
-            createProjectWithAdditionalExprs(
-                rightInputRel,
-                Collections.singletonList(
-                    Pair.of(
-                        (RexNode) rexBuilder.makeLiteral(true),
-                        "nullIndicator")));
+            createProjectWithAdditionalExprs(rightInputRel,
+                ImmutableList.of(
+                    Pair.<RexNode, String>of(
+                        rexBuilder.makeLiteral(true), "nullIndicator")));
 
         // make the new aggRel
         rightInputRel =
@@ -1933,10 +1931,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
               ImmutableSet.<String>of());
 
       RelNode newProjRel =
-          projectJoinOutputWithNullability(
-              joinRel,
-              projRel,
-              nullIndicatorPos);
+          projectJoinOutputWithNullability(joinRel, projRel, nullIndicatorPos);
 
       call.transformTo(newProjRel);
 
@@ -1969,7 +1964,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
       RelNode rightInputRel = call.rel(5);
       RelOptCluster cluster = corRel.getCluster();
 
-      currentRel = corRel;
+      setCurrent(call.getPlanner().getRoot(), corRel);
 
       // check for this pattern
       // The pattern matching could be simplified if rules can be applied
@@ -2003,7 +1998,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
       List<RexNode> aggInputProjExprs = aggInputProjRel.getProjects();
 
       List<AggregateCall> aggCalls = aggRel.getAggCallList();
-      Set<Integer> isCountStar = new HashSet<Integer>();
+      Set<Integer> isCountStar = Sets.newHashSet();
 
       // mark if agg produces count(*) which needs to reference the
       // nullIndicator after the transformation.
@@ -2017,7 +2012,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
       }
 
       if ((rightInputRel instanceof FilterRel)
-          && mapRefRelToCorVar.containsKey(rightInputRel)) {
+          && cm.mapRefRelToCorVar.containsKey(rightInputRel)) {
         // rightInputRel has this shape:
         //
         //       FilterRel (references corvar)
@@ -2041,8 +2036,8 @@ public class RelDecorrelator implements ReflectiveVisitor {
         // filterInputRel) and correlatedJoinKeys. correlatedJoinKeys
         // can only be RexFieldAccess, while rightJoinKeys can be
         // expressions. These comparisons are AND'ed together.
-        List<RexNode> rightJoinKeys = new ArrayList<RexNode>();
-        List<RexNode> tmpCorrelatedJoinKeys = new ArrayList<RexNode>();
+        List<RexNode> rightJoinKeys = Lists.newArrayList();
+        List<RexNode> tmpCorrelatedJoinKeys = Lists.newArrayList();
         RelOptUtil.splitCorrelatedFilterCondition(
             filterRel,
             rightJoinKeys,
@@ -2052,17 +2047,13 @@ public class RelDecorrelator implements ReflectiveVisitor {
         // make sure the correlated reference forms a unique key check
         // that the columns referenced in these comparisons form an
         // unique key of the leftInputRel
-        List<RexFieldAccess> correlatedJoinKeys =
-            new ArrayList<RexFieldAccess>();
-        List<RexInputRef> correlatedInputRefJoinKeys =
-            new ArrayList<RexInputRef>();
+        List<RexFieldAccess> correlatedJoinKeys = Lists.newArrayList();
+        List<RexInputRef> correlatedInputRefJoinKeys = Lists.newArrayList();
         for (RexNode joinKey : tmpCorrelatedJoinKeys) {
           assert joinKey instanceof RexFieldAccess;
           correlatedJoinKeys.add((RexFieldAccess) joinKey);
           RexNode correlatedInputRef =
-              removeCorrelationExpr(
-                  joinKey,
-                  false);
+              removeCorrelationExpr(joinKey, false);
           assert correlatedInputRef instanceof RexInputRef;
           correlatedInputRefJoinKeys.add(
               (RexInputRef) correlatedInputRef);
@@ -2087,8 +2078,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
         }
 
         // check cor var references are valid
-        if (!checkCorVars(
-            corRel,
+        if (!checkCorVars(corRel,
             aggInputProjRel,
             filterRel,
             correlatedJoinKeys)) {
@@ -2140,7 +2130,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
         // first change the filter condition into a join condition
         joinCond =
             removeCorrelationExpr(filterRel.getCondition(), false);
-      } else if (mapRefRelToCorVar.containsKey(aggInputProjRel)) {
+      } else if (cm.mapRefRelToCorVar.containsKey(aggInputProjRel)) {
         // check rightInputRel contains no correlation
         if (RelOptUtil.getVariablesUsed(rightInputRel).size() > 0) {
           return;
@@ -2213,12 +2203,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
           leftInputFieldCount + aggInputProjExprs.size() + 1;
 
       rightInputRel =
-          createProjectWithAdditionalExprs(
-              rightInputRel,
-              Collections.singletonList(
-                  Pair.of(
-                      (RexNode) rexBuilder.makeLiteral(true),
-                      "nullIndicator")));
+          createProjectWithAdditionalExprs(rightInputRel,
+              ImmutableList.of(Pair.<RexNode, String>of(rexBuilder.makeLiteral(
+                      true), "nullIndicator")));
 
       JoinRel joinRel =
           new JoinRel(
@@ -2242,8 +2229,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
                   true));
 
       // first project all group-by keys plus the transformed agg input
-      List<RexNode> joinOutputProjExprs =
-          new ArrayList<RexNode>();
+      List<RexNode> joinOutputProjExprs = Lists.newArrayList();
 
       // LOJ Join preserves LHS types
       for (int i = 0; i < leftInputFieldCount; i++) {
@@ -2254,8 +2240,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
       for (RexNode aggInputProjExpr : aggInputProjExprs) {
         joinOutputProjExprs.add(
-            removeCorrelationExpr(
-                aggInputProjExpr,
+            removeCorrelationExpr(aggInputProjExpr,
                 joinType.generatesNullsOnRight(),
                 nullIndicator));
       }
@@ -2275,7 +2260,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
       final int groupCount = leftInputFieldCount;
 
-      List<AggregateCall> newAggCalls = new ArrayList<AggregateCall>();
+      List<AggregateCall> newAggCalls = Lists.newArrayList();
       k = -1;
       for (AggregateCall aggCall : aggCalls) {
         ++k;
@@ -2287,7 +2272,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
           // the null indicator is located at the end
           newAggArgs = Collections.singletonList(nullIndicatorPos);
         } else {
-          newAggArgs = new ArrayList<Integer>();
+          newAggArgs = Lists.newArrayList();
 
           for (Integer aggArg : aggArgs) {
             newAggArgs.add(aggArg + groupCount);
@@ -2307,7 +2292,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
               groupSet,
               newAggCalls);
 
-      List<RexNode> newAggOutputProjExprList = new ArrayList<RexNode>();
+      List<RexNode> newAggOutputProjExprList = Lists.newArrayList();
       for (int i : BitSets.toIter(groupSet)) {
         newAggOutputProjExprList.add(
             rexBuilder.makeInputRef(newAggRel, i));
@@ -2370,8 +2355,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
         aggRel = call.rel(2);
 
         // Create identity projection
-        List<Pair<RexNode, String>> projects =
-            new ArrayList<Pair<RexNode, String>>();
+        List<Pair<RexNode, String>> projects = Lists.newArrayList();
         final List<RelDataTypeField> fields =
             aggRel.getRowType().getFieldList();
         for (int i = 0; i < fields.size(); i++) {
@@ -2400,7 +2384,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
         return;
       }
 
-      currentRel = corRel;
+      setCurrent(call.getPlanner().getRoot(), corRel);
 
       // check for this pattern
       // The pattern matching could be simplified if rules can be applied
@@ -2430,7 +2414,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
       }
 
       List<AggregateCall> aggCalls = aggRel.getAggCallList();
-      Set<Integer> isCount = new HashSet<Integer>();
+      Set<Integer> isCount = Sets.newHashSet();
 
       // remember the count() positions
       int i = -1;
@@ -2466,12 +2450,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
       // need to update the mapCorVarToCorRel Update the output position
       // for the cor vars: only pass on the cor vars that are not used in
       // the join key.
-      Set<CorrelatorRel.Correlation> corVars =
-          new HashSet<CorrelatorRel.Correlation>();
-      corVars.addAll(mapCorVarToCorRel.keySet());
-      for (CorrelatorRel.Correlation corVar : corVars) {
-        if (mapCorVarToCorRel.get(corVar) == corRel) {
-          mapCorVarToCorRel.put(corVar, newCorRel);
+      for (Correlation c : Lists.newArrayList(cm.mapCorVarToCorRel.keySet())) {
+        if (cm.mapCorVarToCorRel.get(c) == corRel) {
+          cm.mapCorVarToCorRel.put(c, newCorRel);
         }
       }
 
@@ -2481,6 +2462,176 @@ public class RelDecorrelator implements ReflectiveVisitor {
       call.transformTo(newOutputRel);
     }
   }
+
+  /** A map of the locations of {@link org.eigenbase.rel.CorrelatorRel} in
+   * a tree of {@link RelNode}s.
+   *
+   * <p>It is used to drive the decorrelation process.
+   * Treat it as immutable; rebuild if you modify the tree.
+   *
+   * <p>There are three maps:<ol>
+   *
+   * <li>mapRefRelToCorVars map a rel node to the correlated variables it
+   * references;
+   *
+   * <li>mapCorVarToCorRel maps a correlated variable to the correlatorRel
+   * providing it;
+   *
+   * <li>mapFieldAccessToCorVar maps a rex field access to
+   * the cor var it represents. Because typeFlattener does not clone or
+   * modify a correlated field access this map does not need to be
+   * updated.
+   *
+   * </ol> */
+  private static class CorelMap {
+    private final Multimap<RelNode, Correlation> mapRefRelToCorVar;
+    private final SortedMap<Correlation, CorrelatorRel> mapCorVarToCorRel;
+    private final Map<RexFieldAccess, Correlation> mapFieldAccessToCorVar;
+
+    // TODO: create immutable copies of all maps
+    private CorelMap(Multimap<RelNode, Correlation> mapRefRelToCorVar,
+        SortedMap<Correlation, CorrelatorRel> mapCorVarToCorRel,
+        Map<RexFieldAccess, Correlation> mapFieldAccessToCorVar) {
+      this.mapRefRelToCorVar = mapRefRelToCorVar;
+      this.mapCorVarToCorRel = mapCorVarToCorRel;
+      this.mapFieldAccessToCorVar = mapFieldAccessToCorVar;
+    }
+
+    @Override
+    public String toString() {
+      return "mapRefRelToCorVar=" + mapRefRelToCorVar
+          + "\nmapCorVarToCorRel=" + mapCorVarToCorRel
+          + "\nmapFieldAccessToCorVar=" + mapFieldAccessToCorVar
+          + "\n";
+    }
+
+    @Override public boolean equals(Object obj) {
+      return obj == this
+          || obj instanceof CorelMap
+          && mapRefRelToCorVar.equals(((CorelMap) obj).mapRefRelToCorVar)
+          && mapCorVarToCorRel.equals(((CorelMap) obj).mapCorVarToCorRel)
+          && mapFieldAccessToCorVar.equals(
+              ((CorelMap) obj).mapFieldAccessToCorVar);
+    }
+
+    @Override public int hashCode() {
+      return Util.hashV(mapRefRelToCorVar, mapCorVarToCorRel,
+          mapFieldAccessToCorVar);
+    }
+
+    /** Creates a CorelMap with given contents. */
+    public static CorelMap of(
+        SortedSetMultimap<RelNode, Correlation> mapRefRelToCorVar,
+        SortedMap<Correlation, CorrelatorRel> mapCorVarToCorRel,
+        Map<RexFieldAccess, Correlation> mapFieldAccessToCorVar) {
+      return new CorelMap(mapRefRelToCorVar, mapCorVarToCorRel,
+          mapFieldAccessToCorVar);
+    }
+
+    /** Creates a CorelMap by iterating over a {@link RelNode} tree. */
+    public static CorelMap build(RelNode rel) {
+      final SortedMap<Correlation, CorrelatorRel> mapCorVarToCorRel =
+          new TreeMap<Correlation, CorrelatorRel>();
+
+      final SortedSetMultimap<RelNode, Correlation> mapRefRelToCorVar =
+          Multimaps.newSortedSetMultimap(
+              Maps.<RelNode, Collection<Correlation>>newHashMap(),
+              new Supplier<TreeSet<Correlation>>() {
+                public TreeSet<Correlation> get() {
+                  Bug.upgrade("use MultimapBuilder when we're on Guava-16");
+                  return Sets.newTreeSet();
+                }
+              });
+
+      final Map<RexFieldAccess, Correlation> mapFieldAccessToCorVar =
+          new HashMap<RexFieldAccess, Correlation>();
+
+      final Map<String, Correlation> mapNameToCorVar = Maps.newHashMap();
+
+      final Holder<Integer> offset = Holder.of(0);
+      final RelShuttleImpl shuttle = new RelShuttleImpl() {
+        @Override
+        public RelNode visit(JoinRel join) {
+          return visitJoin(join);
+        }
+
+        @Override
+        protected RelNode visitChild(RelNode parent, int i, RelNode child) {
+          return super.visitChild(parent, i, stripHep(child));
+        }
+
+        @Override
+        public RelNode visit(CorrelatorRel correlator) {
+          for (Correlation c : correlator.getCorrelations()) {
+            mapNameToCorVar.put("$cor" + c.getId(), c);
+            mapCorVarToCorRel.put(c, correlator);
+          }
+          return visitJoin(correlator);
+        }
+
+        private JoinRelBase visitJoin(JoinRelBase join) {
+          join.getCondition().accept(rexVisitor(join));
+          final int x = offset.get();
+          visitChild(join, 0, join.getLeft());
+          offset.set(x + join.getLeft().getRowType().getFieldCount());
+          visitChild(join, 0, join.getRight());
+          offset.set(x);
+          return join;
+        }
+
+        @Override
+        public RelNode visit(final FilterRel filter) {
+          filter.getCondition().accept(rexVisitor(filter));
+          return super.visit(filter);
+        }
+
+        @Override
+        public RelNode visit(ProjectRel project) {
+          for (RexNode node : project.getProjects()) {
+            node.accept(rexVisitor(project));
+          }
+          return super.visit(project);
+        }
+
+        private RexVisitorImpl<Void> rexVisitor(final RelNode rel) {
+          return new RexVisitorImpl<Void>(true) {
+            @Override
+            public Void visitFieldAccess(RexFieldAccess fieldAccess) {
+              final RexNode ref = fieldAccess.getReferenceExpr();
+              if (ref instanceof RexCorrelVariable) {
+                final RexCorrelVariable var = (RexCorrelVariable) ref;
+                final Correlation correlation =
+                    mapNameToCorVar.get(var.getName());
+                mapFieldAccessToCorVar.put(fieldAccess, correlation);
+                mapRefRelToCorVar.put(rel, correlation);
+              }
+              return super.visitFieldAccess(fieldAccess);
+            }
+          };
+        }
+      };
+      stripHep(rel).accept(shuttle);
+      return new CorelMap(mapRefRelToCorVar, mapCorVarToCorRel,
+          mapFieldAccessToCorVar);
+    }
+
+    private static RelNode stripHep(RelNode rel) {
+      if (rel instanceof HepRelVertex) {
+        HepRelVertex hepRelVertex = (HepRelVertex) rel;
+        rel = hepRelVertex.getCurrentRel();
+      }
+      return rel;
+    }
+
+    /**
+     * Returns whether there are any correlating variables in this statement.
+     *
+     * @return whether there are any correlating variables
+     */
+    public boolean hasCorrelation() {
+      return !mapCorVarToCorRel.isEmpty();
+    }
+  }
 }
 
 // End RelDecorrelator.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/main/java/org/eigenbase/sql2rel/RelStructuredTypeFlattener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql2rel/RelStructuredTypeFlattener.java b/core/src/main/java/org/eigenbase/sql2rel/RelStructuredTypeFlattener.java
index cb6867e..45526c0 100644
--- a/core/src/main/java/org/eigenbase/sql2rel/RelStructuredTypeFlattener.java
+++ b/core/src/main/java/org/eigenbase/sql2rel/RelStructuredTypeFlattener.java
@@ -30,6 +30,8 @@ import org.eigenbase.util.mapping.Mappings;
 
 import net.hydromatic.linq4j.function.Function1;
 
+import com.google.common.collect.*;
+
 // TODO jvs 10-Feb-2005:  factor out generic rewrite helper, with the
 // ability to map between old and new rels and field ordinals.  Also,
 // for now need to prohibit queries which return UDT instances.
@@ -74,7 +76,7 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
   private final RexBuilder rexBuilder;
   private final RewriteRelVisitor visitor;
 
-  private Map<RelNode, RelNode> oldToNewRelMap;
+  private final Map<RelNode, RelNode> oldToNewRelMap = Maps.newHashMap();
   private RelNode currentRel;
   private int iRestructureInput;
   private RelDataType flattenedRootType;
@@ -94,23 +96,19 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
   //~ Methods ----------------------------------------------------------------
 
   public void updateRelInMap(
-      Map<RelNode, SortedSet<CorrelatorRel.Correlation>> mapRefRelToCorVar) {
-    Set<RelNode> oldRefRelSet = new HashSet<RelNode>();
-    oldRefRelSet.addAll(mapRefRelToCorVar.keySet());
-    for (RelNode rel : oldRefRelSet) {
+      SortedSetMultimap<RelNode, Correlation> mapRefRelToCorVar) {
+    for (RelNode rel : Lists.newArrayList(mapRefRelToCorVar.keySet())) {
       if (oldToNewRelMap.containsKey(rel)) {
-        SortedSet<CorrelatorRel.Correlation> corVarSet =
-            new TreeSet<CorrelatorRel.Correlation>();
-        corVarSet.addAll(mapRefRelToCorVar.get(rel));
-        mapRefRelToCorVar.remove(rel);
-        mapRefRelToCorVar.put(oldToNewRelMap.get(rel), corVarSet);
+        SortedSet<Correlation> corVarSet =
+            mapRefRelToCorVar.removeAll(rel);
+        mapRefRelToCorVar.putAll(oldToNewRelMap.get(rel), corVarSet);
       }
     }
   }
 
   public void updateRelInMap(
-      SortedMap<CorrelatorRel.Correlation, CorrelatorRel> mapCorVarToCorRel) {
-    for (CorrelatorRel.Correlation corVar : mapCorVarToCorRel.keySet()) {
+      SortedMap<Correlation, CorrelatorRel> mapCorVarToCorRel) {
+    for (Correlation corVar : mapCorVarToCorRel.keySet()) {
       CorrelatorRel oldRel = mapCorVarToCorRel.get(corVar);
       if (oldToNewRelMap.containsKey(oldRel)) {
         RelNode newRel = oldToNewRelMap.get(oldRel);
@@ -122,7 +120,6 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
 
   public RelNode rewrite(RelNode root, boolean restructure) {
     // Perform flattening.
-    oldToNewRelMap = new HashMap<RelNode, RelNode>();
     visitor.visit(root, 0, null);
     RelNode flattened = getNewForOldRel(root);
     flattenedRootType = flattened.getRowType();
@@ -374,9 +371,9 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
   }
 
   public void rewriteRel(CorrelatorRel rel) {
-    final List<CorrelatorRel.Correlation> newCorrelations =
-        new ArrayList<CorrelatorRel.Correlation>();
-    for (CorrelatorRel.Correlation c : rel.getCorrelations()) {
+    final List<Correlation> newCorrelations =
+        new ArrayList<Correlation>();
+    for (Correlation c : rel.getCorrelations()) {
       RelDataType corrFieldType =
           rel.getLeft().getRowType().getFieldList().get(c.getOffset())
               .getType();
@@ -384,7 +381,7 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
         throw Util.needToImplement("correlation on structured type");
       }
       newCorrelations.add(
-          new CorrelatorRel.Correlation(
+          new Correlation(
               c.getId(),
               getNewForOldInput(c.getOffset())));
     }

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
index a03eb1b..60cc363 100644
--- a/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
@@ -96,24 +96,6 @@ public class SqlToRelConverter {
   private static final String CORREL_PREFIX = "$cor";
 
   /**
-   * Fields used in decorrelation.
-   */
-  private final Map<String, RelNode> mapCorrelToRefRel =
-      new HashMap<String, RelNode>();
-
-  private final SortedMap<CorrelatorRel.Correlation, CorrelatorRel>
-  mapCorVarToCorRel =
-      new TreeMap<CorrelatorRel.Correlation, CorrelatorRel>();
-
-  private final Map<RelNode, SortedSet<CorrelatorRel.Correlation>>
-  mapRefRelToCorVar =
-      new HashMap<RelNode, SortedSet<CorrelatorRel.Correlation>>();
-
-  private final Map<RexFieldAccess, CorrelatorRel.Correlation>
-  mapFieldAccessToCorVar =
-      new HashMap<RexFieldAccess, CorrelatorRel.Correlation>();
-
-  /**
    * Stack of names of datasets requested by the <code>
    * TABLE(SAMPLE(&lt;datasetName&gt;, &lt;query&gt;))</code> construct.
    */
@@ -332,20 +314,7 @@ public class SqlToRelConverter {
       boolean restructure) {
     RelStructuredTypeFlattener typeFlattener =
         new RelStructuredTypeFlattener(rexBuilder, createToRelContext());
-    RelNode newRootRel = typeFlattener.rewrite(rootRel, restructure);
-
-    // There are three maps constructed during convertQuery which need to to
-    // be maintained for use in decorrelation. 1. mapRefRelToCorVar: - map a
-    // rel node to the coorrelated variables it references 2.
-    // mapCorVarToCorRel: - map a correlated variable to the correlatorRel
-    // providing it 3. mapFieldAccessToCorVar: - map a rex field access to
-    // the cor var it represents. because typeFlattener does not clone or
-    // modify a correlated field access this map does not need to be
-    // updated.
-    typeFlattener.updateRelInMap(mapRefRelToCorVar);
-    typeFlattener.updateRelInMap(mapCorVarToCorRel);
-
-    return newRootRel;
+    return typeFlattener.rewrite(rootRel, restructure);
   }
 
   /**
@@ -357,10 +326,11 @@ public class SqlToRelConverter {
    * @return New root relational expression after decorrelation
    */
   public RelNode decorrelate(SqlNode query, RelNode rootRel) {
-    RelNode result = rootRel;
-    if (enableDecorrelation()
-        && hasCorrelation()) {
-      result = decorrelateQuery(result);
+    if (!enableDecorrelation()) {
+      return rootRel;
+    }
+    final RelNode result = decorrelateQuery(rootRel);
+    if (result != rootRel) {
       checkConvertedType(query, result);
     }
     return result;
@@ -828,22 +798,9 @@ public class SqlToRelConverter {
 
     // only allocate filter if the condition is not TRUE
     if (!convertedWhere.isAlwaysTrue()) {
-      RelNode inputRel = bb.root;
-      Set<String> correlatedVariablesBefore =
-          RelOptUtil.getVariablesUsed(inputRel);
-
       bb.setRoot(
           CalcRel.createFilter(bb.root, convertedWhere),
           false);
-      Set<String> correlatedVariables =
-          RelOptUtil.getVariablesUsed(bb.root);
-
-      correlatedVariables.removeAll(correlatedVariablesBefore);
-
-      // Associate the correlated variables with the new filter rel.
-      for (String correl : correlatedVariables) {
-        mapCorrelToRefRel.put(correl, bb.root);
-      }
     }
   }
 
@@ -1884,8 +1841,7 @@ public class SqlToRelConverter {
     }
 
     if (correlatedVariables.size() > 0) {
-      List<CorrelatorRel.Correlation> correlations =
-          new ArrayList<CorrelatorRel.Correlation>();
+      final List<Correlation> correlations = Lists.newArrayList();
 
       for (String correlName : correlatedVariables) {
         DeferredLookup lookup = mapCorrelToDeferred.get(correlName);
@@ -1952,42 +1908,23 @@ public class SqlToRelConverter {
             }
           }
 
-          CorrelatorRel.Correlation newCorVar =
-              new CorrelatorRel.Correlation(
+          Correlation newCorVar =
+              new Correlation(
                   getCorrelOrdinal(correlName),
                   pos);
 
           correlations.add(newCorVar);
-
-          mapFieldAccessToCorVar.put(fieldAccess, newCorVar);
-
-          RelNode refRel = mapCorrelToRefRel.get(correlName);
-
-          SortedSet<CorrelatorRel.Correlation> corVarList;
-
-          if (!mapRefRelToCorVar.containsKey(refRel)) {
-            corVarList = new TreeSet<CorrelatorRel.Correlation>();
-          } else {
-            corVarList = mapRefRelToCorVar.get(refRel);
-          }
-          corVarList.add(newCorVar);
-          mapRefRelToCorVar.put(refRel, corVarList);
         }
       }
 
       if (!correlations.isEmpty()) {
-        CorrelatorRel rel =
-            new CorrelatorRel(
-                rightRel.getCluster(),
-                leftRel,
-                rightRel,
-                joinCond,
-                correlations,
-                joinType);
-        for (CorrelatorRel.Correlation correlation : correlations) {
-          mapCorVarToCorRel.put(correlation, rel);
-        }
-        return rel;
+        return new CorrelatorRel(
+            rightRel.getCluster(),
+            leftRel,
+            rightRel,
+            joinCond,
+            correlations,
+            joinType);
       }
     }
 
@@ -2487,9 +2424,7 @@ public class SqlToRelConverter {
         preNames = Collections.singletonList(null);
       }
 
-      RelNode inputRel = bb.root;
-      Set<String> correlatedVariablesBefore =
-          RelOptUtil.getVariablesUsed(inputRel);
+      final RelNode inputRel = bb.root;
 
       // Project the expressions required by agg and having.
       bb.setRoot(
@@ -2501,15 +2436,6 @@ public class SqlToRelConverter {
           false);
       bb.mapRootRelToFieldProjection.put(bb.root, groupExprProjection);
 
-      Set<String> correlatedVariables =
-          RelOptUtil.getVariablesUsed(bb.root);
-      correlatedVariables.removeAll(correlatedVariablesBefore);
-
-      // Associate the correlated variables with the new project rel.
-      for (String correl : correlatedVariables) {
-        mapCorrelToRefRel.put(correl, bb.root);
-      }
-
       // REVIEW jvs 31-Oct-2007:  doesn't the declaration of
       // monotonicity here assume sort-based aggregation at
       // the physical level?
@@ -2752,41 +2678,8 @@ public class SqlToRelConverter {
     return decorrelationEnabled;
   }
 
-  /**
-   * Returns whether there are any correlating variables in this statement.
-   *
-   * @return whether there are any correlating variables
-   */
-  public boolean hasCorrelation() {
-    return !mapCorVarToCorRel.isEmpty();
-  }
-
   protected RelNode decorrelateQuery(RelNode rootRel) {
-    RelDecorrelator decorrelator =
-        new RelDecorrelator(
-            rexBuilder,
-            mapRefRelToCorVar,
-            mapCorVarToCorRel,
-            mapFieldAccessToCorVar,
-            cluster.getPlanner().getContext());
-    boolean dumpPlan = SQL2REL_LOGGER.isLoggable(Level.FINE);
-
-    RelNode newRootRel = decorrelator.removeCorrelationViaRule(rootRel);
-
-    if (dumpPlan) {
-      SQL2REL_LOGGER.fine(
-          RelOptUtil.dumpPlan(
-              "Plan after removing CorrelatorRel",
-              newRootRel,
-              false,
-              SqlExplainLevel.EXPPLAN_ATTRIBUTES));
-    }
-
-    if (!mapCorVarToCorRel.isEmpty()) {
-      newRootRel = decorrelator.decorrelate(newRootRel);
-    }
-
-    return newRootRel;
+    return RelDecorrelator.decorrelateQuery(rootRel);
   }
 
   /**
@@ -3380,14 +3273,6 @@ public class SqlToRelConverter {
                 selectList,
                 fieldNameList);
 
-        Set<String> correlatedVariables =
-            RelOptUtil.getVariablesUsed(projRel);
-
-        // Associate the correlated variables with the new project rel.
-        for (String correl : correlatedVariables) {
-          mapCorrelToRefRel.put(correl, projRel);
-        }
-
         joinList.set(i, projRel);
       }
     }
@@ -3478,9 +3363,6 @@ public class SqlToRelConverter {
     fieldNames = SqlValidatorUtil.uniquify(fieldNames);
 
     RelNode inputRel = bb.root;
-    Set<String> correlatedVariablesBefore =
-        RelOptUtil.getVariablesUsed(inputRel);
-
     bb.setRoot(
         CalcRel.createProject(bb.root, exprs, fieldNames),
         false);
@@ -3491,14 +3373,6 @@ public class SqlToRelConverter {
       bb.columnMonotonicities.add(
           selectItem.getMonotonicity(bb.scope));
     }
-
-    Set<String> correlatedVariables = RelOptUtil.getVariablesUsed(bb.root);
-    correlatedVariables.removeAll(correlatedVariablesBefore);
-
-    // Associate the correlated variables with the new project rel.
-    for (String correl : correlatedVariables) {
-      mapCorrelToRefRel.put(correl, bb.root);
-    }
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/test/java/net/hydromatic/optiq/test/OptiqAssert.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/test/OptiqAssert.java b/core/src/test/java/net/hydromatic/optiq/test/OptiqAssert.java
index b236144..6841c81 100644
--- a/core/src/test/java/net/hydromatic/optiq/test/OptiqAssert.java
+++ b/core/src/test/java/net/hydromatic/optiq/test/OptiqAssert.java
@@ -119,14 +119,9 @@ public class OptiqAssert {
           return NopAssertQuery.of(sql);
         }
 
-        @Override
-        public void connectThrows(String message) {
-          // nothing
-        }
-
-        @Override
-        public void connectThrows(Function1<Throwable, Void> exceptionChecker) {
-          // nothing
+        @Override public AssertThat connectThrows(
+            Function1<Throwable, Void> exceptionChecker) {
+          return this;
         }
 
         @Override
@@ -761,13 +756,13 @@ public class OptiqAssert {
 
     /** Asserts that there is an exception with the given message while
      * creating a connection. */
-    public void connectThrows(String message) {
-      connectThrows(checkException(message));
+    public AssertThat connectThrows(String message) {
+      return connectThrows(checkException(message));
     }
 
     /** Asserts that there is an exception that matches the given predicate
      * while creating a connection. */
-    public void connectThrows(
+    public AssertThat connectThrows(
         Function1<Throwable, Void> exceptionChecker) {
       Throwable throwable;
       try {
@@ -782,6 +777,7 @@ public class OptiqAssert {
         throwable = e;
       }
       exceptionChecker.apply(throwable);
+      return this;
     }
 
     /** Creates a {@link OptiqConnection} and executes a callback. */

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/3055cc32/core/src/test/resources/mysql-foodmart-model.json
----------------------------------------------------------------------
diff --git a/core/src/test/resources/mysql-foodmart-model.json b/core/src/test/resources/mysql-foodmart-model.json
new file mode 100644
index 0000000..7c420ff
--- /dev/null
+++ b/core/src/test/resources/mysql-foodmart-model.json
@@ -0,0 +1,15 @@
+{
+  version: '1.0',
+  defaultSchema: 'foodmart',
+  schemas: [
+    {
+      type: 'jdbc',
+      name: 'foodmart',
+      jdbcUser: 'foodmart',
+      jdbcPassword: 'foodmart',
+      jdbcUrl: 'jdbc:mysql://localhost',
+      jdbcCatalog: 'foodmart',
+      jdbcSchema: null
+    }
+  ]
+}