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 2016/01/11 05:47:25 UTC

[24/27] calcite git commit: [CALCITE-816] Represent sub-query as a RexNode

[CALCITE-816] Represent sub-query as a RexNode

Reduce 3-value logic to 1- or 2-value logic.

Optimize certain IN and EXISTS to an inner join.

Represent correlation variables using CorrelationId wherever possible.

In Join, replace field "ImmutableSet<String> variablesStopped" with
"ImmutableSet<CorrelationId> variablesSet". RelNode.getVariablesSet
is now preferred to RelNode.getVariablesStopped.

Make Join.joinType final.

Verify in builder that there are no correlation variables where there
shouldn't be.

Refactor decorrelator.

Logged [CALCITE-1045] for remaining work.


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

Branch: refs/heads/master
Commit: 505a9064b96a6c8399735fc2fa4d0ac9d5f3ed87
Parents: cd92b77
Author: Julian Hyde <jh...@apache.org>
Authored: Sat Jul 25 14:44:20 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Sun Jan 10 00:51:25 2016 -0800

----------------------------------------------------------------------
 .../adapter/enumerable/EnumerableJoin.java      |   42 +-
 .../adapter/enumerable/EnumerableJoinRule.java  |    9 +-
 .../adapter/enumerable/EnumerableMergeJoin.java |   22 +-
 .../enumerable/EnumerableMergeJoinRule.java     |    4 +-
 .../adapter/enumerable/EnumerableThetaJoin.java |   16 +-
 .../calcite/adapter/enumerable/RexImpTable.java |   14 +-
 .../apache/calcite/adapter/jdbc/JdbcRules.java  |   20 +-
 .../apache/calcite/interpreter/Bindables.java   |   17 +-
 .../org/apache/calcite/materialize/Lattice.java |    3 +-
 .../org/apache/calcite/plan/RelOptCluster.java  |    5 +-
 .../org/apache/calcite/plan/RelOptQuery.java    |    3 +-
 .../org/apache/calcite/plan/RelOptUtil.java     |  127 +-
 .../calcite/plan/SubstitutionVisitor.java       |   19 +-
 .../org/apache/calcite/plan/volcano/RelSet.java |   24 +-
 .../apache/calcite/plan/volcano/RelSubset.java  |   29 +-
 .../calcite/plan/volcano/VolcanoPlanner.java    |    2 +-
 .../org/apache/calcite/prepare/Prepare.java     |   10 +-
 .../org/apache/calcite/rel/AbstractRelNode.java |   16 +-
 .../java/org/apache/calcite/rel/RelNode.java    |   31 +-
 .../org/apache/calcite/rel/core/Correlate.java  |   15 +-
 .../apache/calcite/rel/core/CorrelationId.java  |   54 +-
 .../org/apache/calcite/rel/core/EquiJoin.java   |   15 +-
 .../java/org/apache/calcite/rel/core/Join.java  |   53 +-
 .../apache/calcite/rel/core/RelFactories.java   |   63 +-
 .../org/apache/calcite/rel/core/SemiJoin.java   |    4 +-
 .../apache/calcite/rel/externalize/RelJson.java |    2 +-
 .../apache/calcite/rel/logical/LogicalCalc.java |    5 +-
 .../calcite/rel/logical/LogicalCorrelate.java   |    3 +
 .../calcite/rel/logical/LogicalFilter.java      |   46 +-
 .../apache/calcite/rel/logical/LogicalJoin.java |   73 +-
 .../calcite/rel/metadata/RelMdUniqueKeys.java   |   11 +-
 .../org/apache/calcite/rel/rules/EquiJoin.java  |    3 +-
 .../calcite/rel/rules/JoinToCorrelateRule.java  |    5 +-
 .../rel/rules/JoinUnionTransposeRule.java       |    2 +-
 .../calcite/rel/rules/SubQueryRemoveRule.java   |  365 ++++
 .../apache/calcite/rel/stream/StreamRules.java  |   10 +-
 .../org/apache/calcite/rex/LogicVisitor.java    |  158 ++
 .../org/apache/calcite/rex/RexBiVisitor.java    |   52 +
 .../java/org/apache/calcite/rex/RexBuilder.java |    7 +-
 .../java/org/apache/calcite/rex/RexCall.java    |    8 +-
 .../apache/calcite/rex/RexCorrelVariable.java   |   12 +-
 .../org/apache/calcite/rex/RexDynamicParam.java |    4 +
 .../org/apache/calcite/rex/RexFieldAccess.java  |    4 +
 .../org/apache/calcite/rex/RexInputRef.java     |    4 +
 .../java/org/apache/calcite/rex/RexLiteral.java |   10 +-
 .../org/apache/calcite/rex/RexLocalRef.java     |    4 +
 .../java/org/apache/calcite/rex/RexNode.java    |    6 +
 .../java/org/apache/calcite/rex/RexOver.java    |    4 +
 .../java/org/apache/calcite/rex/RexProgram.java |    4 +
 .../org/apache/calcite/rex/RexRangeRef.java     |    4 +
 .../java/org/apache/calcite/rex/RexShuttle.java |   12 +-
 .../org/apache/calcite/rex/RexSubQuery.java     |  115 ++
 .../java/org/apache/calcite/rex/RexUtil.java    |  103 +-
 .../java/org/apache/calcite/rex/RexVisitor.java |    2 +
 .../org/apache/calcite/rex/RexVisitorImpl.java  |   12 +
 .../org/apache/calcite/schema/SchemaPlus.java   |    2 +-
 .../java/org/apache/calcite/sql/SqlKind.java    |    8 +-
 .../calcite/sql/validate/SqlValidatorImpl.java  |    1 +
 .../sql2rel/DeduplicateCorrelateVariables.java  |   65 +-
 .../apache/calcite/sql2rel/RelDecorrelator.java | 1646 ++++++++----------
 .../apache/calcite/sql2rel/RelFieldTrimmer.java |   12 +-
 .../sql2rel/RelStructuredTypeFlattener.java     |    7 +-
 .../calcite/sql2rel/SqlToRelConverter.java      |  390 +++--
 .../java/org/apache/calcite/tools/Programs.java |   54 +-
 .../org/apache/calcite/tools/RelBuilder.java    |   47 +-
 .../main/java/org/apache/calcite/util/Bug.java  |    5 +
 .../org/apache/calcite/test/CalciteAssert.java  |   10 +-
 .../apache/calcite/test/JdbcAdapterTest.java    |  122 +-
 .../java/org/apache/calcite/test/JdbcTest.java  |  214 ++-
 .../org/apache/calcite/test/LatticeTest.java    |   47 +-
 .../calcite/test/ReflectiveSchemaTest.java      |   21 +-
 .../org/apache/calcite/test/RelBuilderTest.java |   22 +
 .../apache/calcite/test/RelMetadataTest.java    |    3 +-
 .../apache/calcite/test/RelOptRulesTest.java    |  159 +-
 .../org/apache/calcite/test/RelOptTestBase.java |   30 +
 .../apache/calcite/test/RexTransformerTest.java |  210 +--
 .../calcite/test/SqlToRelConverterTest.java     |  421 ++++-
 .../apache/calcite/test/SqlToRelTestBase.java   |   36 +-
 .../apache/calcite/test/SqlValidatorTest.java   |   37 +
 .../enumerable/EnumerableCorrelateTest.java     |   29 +-
 .../org/apache/calcite/test/RelOptRulesTest.xml |  706 ++++++++
 .../calcite/test/SqlToRelConverterTest.xml      |  582 ++++++-
 core/src/test/resources/sql/join.iq             |   18 +-
 core/src/test/resources/sql/misc.iq             |  121 +-
 core/src/test/resources/sql/subquery.iq         |   70 +-
 site/_docs/reference.md                         |   17 +-
 86 files changed, 5027 insertions(+), 1752 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableJoin.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableJoin.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableJoin.java
index 14fcf3b..0b86771 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableJoin.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableJoin.java
@@ -26,6 +26,7 @@ import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.InvalidRelException;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelNodes;
+import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.core.EquiJoin;
 import org.apache.calcite.rel.core.JoinInfo;
 import org.apache.calcite.rel.core.JoinRelType;
@@ -53,8 +54,8 @@ public class EnumerableJoin extends EquiJoin implements EnumerableRel {
       RexNode condition,
       ImmutableIntList leftKeys,
       ImmutableIntList rightKeys,
-      JoinRelType joinType,
-      Set<String> variablesStopped)
+      Set<CorrelationId> variablesSet,
+      JoinRelType joinType)
       throws InvalidRelException {
     super(
         cluster,
@@ -64,8 +65,17 @@ public class EnumerableJoin extends EquiJoin implements EnumerableRel {
         condition,
         leftKeys,
         rightKeys,
-        joinType,
-        variablesStopped);
+        variablesSet,
+        joinType);
+  }
+
+  @Deprecated // to be removed before 2.0
+  protected EnumerableJoin(RelOptCluster cluster, RelTraitSet traits,
+      RelNode left, RelNode right, RexNode condition, ImmutableIntList leftKeys,
+      ImmutableIntList rightKeys, JoinRelType joinType,
+      Set<String> variablesStopped) throws InvalidRelException {
+    this(cluster, traits, left, right, condition, leftKeys, rightKeys,
+        CorrelationId.setOf(variablesStopped), joinType);
   }
 
   /** Creates an EnumerableJoin. */
@@ -75,14 +85,28 @@ public class EnumerableJoin extends EquiJoin implements EnumerableRel {
       RexNode condition,
       ImmutableIntList leftKeys,
       ImmutableIntList rightKeys,
-      JoinRelType joinType,
-      Set<String> variablesStopped)
+      Set<CorrelationId> variablesSet,
+      JoinRelType joinType)
       throws InvalidRelException {
     final RelOptCluster cluster = left.getCluster();
     final RelTraitSet traitSet =
         cluster.traitSetOf(EnumerableConvention.INSTANCE);
     return new EnumerableJoin(cluster, traitSet, left, right, condition,
-        leftKeys, rightKeys, joinType, variablesStopped);
+        leftKeys, rightKeys, variablesSet, joinType);
+  }
+
+  @Deprecated // to be removed before 2.0
+  public static EnumerableJoin create(
+      RelNode left,
+      RelNode right,
+      RexNode condition,
+      ImmutableIntList leftKeys,
+      ImmutableIntList rightKeys,
+      JoinRelType joinType,
+      Set<String> variablesStopped)
+      throws InvalidRelException {
+    return create(left, right, condition, leftKeys, rightKeys,
+        CorrelationId.setOf(variablesStopped), joinType);
   }
 
   @Override public EnumerableJoin copy(RelTraitSet traitSet, RexNode condition,
@@ -92,8 +116,8 @@ public class EnumerableJoin extends EquiJoin implements EnumerableRel {
     assert joinInfo.isEqui();
     try {
       return new EnumerableJoin(getCluster(), traitSet, left, right,
-          condition, joinInfo.leftKeys, joinInfo.rightKeys, joinType,
-          variablesStopped);
+          condition, joinInfo.leftKeys, joinInfo.rightKeys, variablesSet,
+          joinType);
     } catch (InvalidRelException e) {
       // Semantic error not possible. Must be a bug. Convert to
       // internal error.

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableJoinRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableJoinRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableJoinRule.java
index 6ffc912..88655ba 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableJoinRule.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableJoinRule.java
@@ -43,7 +43,7 @@ class EnumerableJoinRule extends ConverterRule {
 
   @Override public RelNode convert(RelNode rel) {
     LogicalJoin join = (LogicalJoin) rel;
-    List<RelNode> newInputs = new ArrayList<RelNode>();
+    List<RelNode> newInputs = new ArrayList<>();
     for (RelNode input : join.getInputs()) {
       if (!(input.getConvention() instanceof EnumerableConvention)) {
         input =
@@ -65,8 +65,7 @@ class EnumerableJoinRule extends ConverterRule {
       // if it is an inner join.
       try {
         return new EnumerableThetaJoin(cluster, traitSet, left, right,
-            join.getCondition(), join.getJoinType(),
-            join.getVariablesStopped());
+            join.getCondition(), join.getVariablesSet(), join.getJoinType());
       } catch (InvalidRelException e) {
         EnumerableRules.LOGGER.fine(e.toString());
         return null;
@@ -82,8 +81,8 @@ class EnumerableJoinRule extends ConverterRule {
           info.getEquiCondition(left, right, cluster.getRexBuilder()),
           info.leftKeys,
           info.rightKeys,
-          join.getJoinType(),
-          join.getVariablesStopped());
+          join.getVariablesSet(),
+          join.getJoinType());
     } catch (InvalidRelException e) {
       EnumerableRules.LOGGER.fine(e.toString());
       return null;

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java
index be0d821..18419e3 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java
@@ -28,6 +28,7 @@ import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelCollationTraitDef;
 import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.core.EquiJoin;
 import org.apache.calcite.rel.core.JoinInfo;
 import org.apache.calcite.rel.core.JoinRelType;
@@ -56,16 +57,25 @@ public class EnumerableMergeJoin extends EquiJoin implements EnumerableRel {
       RexNode condition,
       ImmutableIntList leftKeys,
       ImmutableIntList rightKeys,
-      JoinRelType joinType,
-      Set<String> variablesStopped)
+      Set<CorrelationId> variablesSet,
+      JoinRelType joinType)
       throws InvalidRelException {
     super(cluster, traits, left, right, condition, leftKeys, rightKeys,
-        joinType, variablesStopped);
+        variablesSet, joinType);
     final List<RelCollation> collations =
         traits.getTraits(RelCollationTraitDef.INSTANCE);
     assert collations == null || RelCollations.contains(collations, leftKeys);
   }
 
+  @Deprecated // to be removed before 2.0
+  EnumerableMergeJoin(RelOptCluster cluster, RelTraitSet traits, RelNode left,
+      RelNode right, RexNode condition, ImmutableIntList leftKeys,
+      ImmutableIntList rightKeys, JoinRelType joinType,
+      Set<String> variablesStopped) throws InvalidRelException {
+    this(cluster, traits, left, right, condition, leftKeys, rightKeys,
+        CorrelationId.setOf(variablesStopped), joinType);
+  }
+
   public static EnumerableMergeJoin create(RelNode left, RelNode right,
       RexLiteral condition, ImmutableIntList leftKeys,
       ImmutableIntList rightKeys, JoinRelType joinType)
@@ -78,7 +88,7 @@ public class EnumerableMergeJoin extends EquiJoin implements EnumerableRel {
       traitSet = traitSet.replace(collations);
     }
     return new EnumerableMergeJoin(cluster, traitSet, left, right, condition,
-        leftKeys, rightKeys, joinType, ImmutableSet.<String>of());
+        leftKeys, rightKeys, ImmutableSet.<CorrelationId>of(), joinType);
   }
 
   @Override public EnumerableMergeJoin copy(RelTraitSet traitSet,
@@ -88,8 +98,8 @@ public class EnumerableMergeJoin extends EquiJoin implements EnumerableRel {
     assert joinInfo.isEqui();
     try {
       return new EnumerableMergeJoin(getCluster(), traitSet, left, right,
-          condition, joinInfo.leftKeys, joinInfo.rightKeys, joinType,
-          variablesStopped);
+          condition, joinInfo.leftKeys, joinInfo.rightKeys, variablesSet,
+          joinType);
     } catch (InvalidRelException e) {
       // Semantic error not possible. Must be a bug. Convert to
       // internal error.

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoinRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoinRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoinRule.java
index 51f09f4..9dd0ce1 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoinRule.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoinRule.java
@@ -99,8 +99,8 @@ class EnumerableMergeJoinRule extends ConverterRule {
           info.getEquiCondition(left, right, cluster.getRexBuilder()),
           info.leftKeys,
           info.rightKeys,
-          join.getJoinType(),
-          join.getVariablesStopped());
+          join.getVariablesSet(),
+          join.getJoinType());
     } catch (InvalidRelException e) {
       EnumerableRules.LOGGER.fine(e.toString());
       return null;

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableThetaJoin.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableThetaJoin.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableThetaJoin.java
index bf4516a..e28ddfc 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableThetaJoin.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableThetaJoin.java
@@ -27,6 +27,7 @@ import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.InvalidRelException;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.core.Join;
 import org.apache.calcite.rel.core.JoinRelType;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
@@ -43,11 +44,20 @@ import java.util.Set;
  * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}
  * that allows conditions that are not just {@code =} (equals). */
 public class EnumerableThetaJoin extends Join implements EnumerableRel {
+  /** Creates an EnumerableThetaJoin. */
+  protected EnumerableThetaJoin(RelOptCluster cluster, RelTraitSet traits,
+      RelNode left, RelNode right, RexNode condition,
+      Set<CorrelationId> variablesSet, JoinRelType joinType)
+      throws InvalidRelException {
+    super(cluster, traits, left, right, condition, variablesSet, joinType);
+  }
+
+  @Deprecated // to be removed before 2.0
   protected EnumerableThetaJoin(RelOptCluster cluster, RelTraitSet traits,
       RelNode left, RelNode right, RexNode condition, JoinRelType joinType,
       Set<String> variablesStopped) throws InvalidRelException {
-    super(cluster, traits, left, right, condition, joinType,
-        variablesStopped);
+    this(cluster, traits, left, right, condition,
+        CorrelationId.setOf(variablesStopped), joinType);
   }
 
   @Override public EnumerableThetaJoin copy(RelTraitSet traitSet,
@@ -55,7 +65,7 @@ public class EnumerableThetaJoin extends Join implements EnumerableRel {
       boolean semiJoinDone) {
     try {
       return new EnumerableThetaJoin(getCluster(), traitSet, left, right,
-          condition, joinType, variablesStopped);
+          condition, variablesSet, joinType);
     } catch (InvalidRelException e) {
       // Semantic error not possible. Must be a bug. Convert to
       // internal error.

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 511584b..2633490 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -359,10 +359,8 @@ public class RexImpTable {
       public T get() {
         try {
           return constructor.newInstance();
-        } catch (InstantiationException e) {
-          throw new IllegalStateException(
-              "Unable to instantiate aggregate implementor " + constructor, e);
-        } catch (IllegalAccessException | InvocationTargetException e) {
+        } catch (InstantiationException | IllegalAccessException
+            | InvocationTargetException e) {
           throw new IllegalStateException(
               "Error while creating aggregate implementor " + constructor, e);
         }
@@ -1883,11 +1881,9 @@ public class RexImpTable {
         return translator.translate(operands.get(0),
             negate ? NullAs.IS_NOT_NULL : NullAs.IS_NULL);
       } else {
-        return maybeNegate(
-            negate == seek,
-            translator.translate(
-                operands.get(0),
-                negate == seek ? NullAs.TRUE : NullAs.FALSE));
+        return maybeNegate(negate == seek,
+            translator.translate(operands.get(0),
+                seek ? NullAs.FALSE : NullAs.TRUE));
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
index 5fbb08f..ca80d29 100644
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
+++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
@@ -35,6 +35,7 @@ import org.apache.calcite.rel.SingleRel;
 import org.apache.calcite.rel.convert.ConverterRule;
 import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.AggregateCall;
+import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.core.Intersect;
 import org.apache.calcite.rel.core.Join;
@@ -159,8 +160,8 @@ public class JdbcRules {
             newInputs.get(0),
             newInputs.get(1),
             join.getCondition(),
-            join.getJoinType(),
-            join.getVariablesStopped());
+            join.getVariablesSet(),
+            join.getJoinType());
       } catch (InvalidRelException e) {
         LOGGER.fine(e.toString());
         return null;
@@ -211,6 +212,15 @@ public class JdbcRules {
 
   /** Join operator implemented in JDBC convention. */
   public static class JdbcJoin extends Join implements JdbcRel {
+    /** Creates a JdbcJoin. */
+    protected JdbcJoin(RelOptCluster cluster, RelTraitSet traitSet,
+        RelNode left, RelNode right, RexNode condition,
+        Set<CorrelationId> variablesSet, JoinRelType joinType)
+        throws InvalidRelException {
+      super(cluster, traitSet, left, right, condition, variablesSet, joinType);
+    }
+
+    @Deprecated // to be removed before 2.0
     protected JdbcJoin(
         RelOptCluster cluster,
         RelTraitSet traitSet,
@@ -220,8 +230,8 @@ public class JdbcRules {
         JoinRelType joinType,
         Set<String> variablesStopped)
         throws InvalidRelException {
-      super(cluster, traitSet, left, right, condition,
-          joinType, variablesStopped);
+      this(cluster, traitSet, left, right, condition,
+          CorrelationId.setOf(variablesStopped), joinType);
     }
 
     @Override public JdbcJoin copy(RelTraitSet traitSet, RexNode condition,
@@ -229,7 +239,7 @@ public class JdbcRules {
         boolean semiJoinDone) {
       try {
         return new JdbcJoin(getCluster(), traitSet, left, right,
-            condition, joinType, variablesStopped);
+            condition, variablesSet, joinType);
       } catch (InvalidRelException e) {
         // Semantic error not possible. Must be a bug. Convert to
         // internal error.

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/interpreter/Bindables.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/interpreter/Bindables.java b/core/src/main/java/org/apache/calcite/interpreter/Bindables.java
index a4474b4..9d4b250 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/Bindables.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/Bindables.java
@@ -37,6 +37,7 @@ import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.convert.ConverterRule;
 import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.AggregateCall;
+import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.core.Join;
 import org.apache.calcite.rel.core.JoinRelType;
@@ -417,25 +418,33 @@ public class Bindables {
           convert(join.getRight(),
               join.getRight().getTraitSet()
                   .replace(BindableConvention.INSTANCE)),
-          join.getCondition(), join.getJoinType(), join.getVariablesStopped());
+          join.getCondition(), join.getVariablesSet(), join.getJoinType());
     }
   }
 
   /** Implementation of {@link org.apache.calcite.rel.core.Join} in
    * bindable calling convention. */
   public static class BindableJoin extends Join implements BindableRel {
+    /** Creates a BindableJoin. */
+    protected BindableJoin(RelOptCluster cluster, RelTraitSet traitSet,
+        RelNode left, RelNode right, RexNode condition,
+        Set<CorrelationId> variablesSet, JoinRelType joinType) {
+      super(cluster, traitSet, left, right, condition, variablesSet, joinType);
+    }
+
+    @Deprecated // to be removed before 2.0
     protected BindableJoin(RelOptCluster cluster, RelTraitSet traitSet,
         RelNode left, RelNode right, RexNode condition, JoinRelType joinType,
         Set<String> variablesStopped) {
-      super(cluster, traitSet, left, right, condition, joinType,
-          variablesStopped);
+      this(cluster, traitSet, left, right, condition,
+          CorrelationId.setOf(variablesStopped), joinType);
     }
 
     public BindableJoin copy(RelTraitSet traitSet, RexNode conditionExpr,
         RelNode left, RelNode right, JoinRelType joinType,
         boolean semiJoinDone) {
       return new BindableJoin(getCluster(), traitSet, left, right,
-          conditionExpr, joinType, variablesStopped);
+          conditionExpr, variablesSet, joinType);
     }
 
     public Class<Object[]> getElementType() {

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/materialize/Lattice.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/materialize/Lattice.java b/core/src/main/java/org/apache/calcite/materialize/Lattice.java
index 62ab07f..244a245 100644
--- a/core/src/main/java/org/apache/calcite/materialize/Lattice.java
+++ b/core/src/main/java/org/apache/calcite/materialize/Lattice.java
@@ -44,7 +44,6 @@ import org.apache.calcite.sql.SqlSelect;
 import org.apache.calcite.sql.SqlUtil;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.validate.SqlValidatorUtil;
-import org.apache.calcite.util.BitSets;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.graph.DefaultDirectedGraph;
 import org.apache.calcite.util.graph.DefaultEdge;
@@ -260,7 +259,7 @@ public class Lattice {
     final StringBuilder groupBuf = new StringBuilder("\nGROUP BY ");
     int k = 0;
     final Set<String> columnNames = Sets.newHashSet();
-    for (int i : BitSets.toIter(groupSet)) {
+    for (int i : groupSet) {
       if (k++ > 0) {
         buf.append(", ");
         groupBuf.append(", ");

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java b/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
index c4592db..241f4e0 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.plan;
 
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.metadata.DefaultRelMetadataProvider;
 import org.apache.calcite.rel.metadata.MetadataFactory;
 import org.apache.calcite.rel.metadata.MetadataFactoryImpl;
@@ -143,8 +144,8 @@ public class RelOptCluster {
    * Constructs a new id for a correlating variable. It is unique within the
    * whole query.
    */
-  public int createCorrel() {
-    return nextCorrel.getAndIncrement();
+  public CorrelationId createCorrel() {
+    return new CorrelationId(nextCorrel.getAndIncrement());
   }
 
   /** Returns the default trait set for this cluster. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/plan/RelOptQuery.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptQuery.java b/core/src/main/java/org/apache/calcite/plan/RelOptQuery.java
index 2c2703d..34f5c5f 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptQuery.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptQuery.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.plan;
 
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rex.RexBuilder;
 
@@ -35,7 +36,7 @@ public class RelOptQuery {
   /**
    * Prefix to the name of correlating variables.
    */
-  public static final String CORREL_PREFIX = "$cor";
+  public static final String CORREL_PREFIX = CorrelationId.CORREL_PREFIX;
 
   //~ Instance fields --------------------------------------------------------
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/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 b932cef..eeaea21 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -26,6 +26,7 @@ import org.apache.calcite.rel.RelVisitor;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.rel.core.Calc;
+import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.core.Join;
 import org.apache.calcite.rel.core.JoinRelType;
@@ -60,6 +61,7 @@ import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexOver;
 import org.apache.calcite.rex.RexProgram;
 import org.apache.calcite.rex.RexShuttle;
+import org.apache.calcite.rex.RexSubQuery;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.rex.RexVisitorImpl;
 import org.apache.calcite.sql.SqlExplainLevel;
@@ -83,7 +85,9 @@ import org.apache.calcite.util.mapping.Mappings;
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -155,7 +159,7 @@ public abstract class RelOptUtil {
    * Returns a list of variables set by a relational expression or its
    * descendants.
    */
-  public static Set<String> getVariablesSet(RelNode rel) {
+  public static Set<CorrelationId> getVariablesSet(RelNode rel) {
     VariableSetVisitor visitor = new VariableSetVisitor();
     go(visitor, rel);
     return visitor.variables;
@@ -165,19 +169,18 @@ public abstract class RelOptUtil {
    * Returns a set of distinct variables set by <code>rel0</code> and used by
    * <code>rel1</code>.
    */
-  public static List<String> getVariablesSetAndUsed(
-      RelNode rel0,
+  public static List<CorrelationId> getVariablesSetAndUsed(RelNode rel0,
       RelNode rel1) {
-    Set<String> set = getVariablesSet(rel0);
+    Set<CorrelationId> set = getVariablesSet(rel0);
     if (set.size() == 0) {
       return ImmutableList.of();
     }
-    Set<String> used = getVariablesUsed(rel1);
+    Set<CorrelationId> used = getVariablesUsed(rel1);
     if (used.size() == 0) {
       return ImmutableList.of();
     }
-    final List<String> result = new ArrayList<>();
-    for (String s : set) {
+    final List<CorrelationId> result = new ArrayList<>();
+    for (CorrelationId s : set) {
       if (used.contains(s) && !result.contains(s)) {
         result.add(s);
       }
@@ -187,24 +190,45 @@ public abstract class RelOptUtil {
 
   /**
    * Returns a set of variables used by a relational expression or its
-   * descendants. The set may contain duplicates. The item type is the same as
-   * {@link org.apache.calcite.rex.RexVariable#getName}
-   */
-  public static Set<String> getVariablesUsed(RelNode rel) {
-    final VariableUsedVisitor vuv = new VariableUsedVisitor();
-    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;
-      }
-    };
+   * descendants.
+   *
+   * <p>The set may contain "duplicates" (variables with different ids that,
+   * when resolved, will reference the same source relational expression).
+   *
+   * <p>The item type is the same as
+   * {@link org.apache.calcite.rex.RexCorrelVariable#id}.
+   */
+  public static Set<CorrelationId> getVariablesUsed(RelNode rel) {
+    CorrelationCollector visitor = new CorrelationCollector();
     rel.accept(visitor);
-    return vuv.variables;
+    return visitor.vuv.variables;
+  }
+
+  /** Finds which columns of a correlation variable are used within a
+   * relational expression. */
+  public static ImmutableBitSet correlationColumns(CorrelationId id,
+      RelNode rel) {
+    final CorrelationCollector collector = new CorrelationCollector();
+    rel.accept(collector);
+    final ImmutableBitSet.Builder builder = ImmutableBitSet.builder();
+    for (int field : collector.vuv.variableFields.get(id)) {
+      if (field >= 0) {
+        builder.set(field);
+      }
+    }
+    return builder.build();
+  }
+
+  /** Returns true, and calls {@link Litmus#succeed()} if a given relational
+   * expression does not contain a given correlation. */
+  public static boolean notContainsCorrelation(RelNode r,
+      CorrelationId correlationId, Litmus litmus) {
+    final Set<CorrelationId> set = getVariablesUsed(r);
+    if (!set.contains(correlationId)) {
+      return litmus.succeed();
+    } else {
+      return litmus.fail("contains " + correlationId);
+    }
   }
 
   /**
@@ -2946,6 +2970,7 @@ public abstract class RelOptUtil {
     public Logic negate() {
       switch (this) {
       case UNKNOWN_AS_FALSE:
+      case TRUE:
         return UNKNOWN_AS_TRUE;
       case UNKNOWN_AS_TRUE:
         return UNKNOWN_AS_FALSE;
@@ -2982,7 +3007,8 @@ public abstract class RelOptUtil {
     // Pushing sub-queries is OK in principle (if they don't reference both
     // sides of the join via correlating variables) but we'd rather not do it
     // yet.
-    if (!containsGet(joinCond)) {
+    if (!containsGet(joinCond)
+        && RexUtil.SubQueryFinder.find(joinCond) == null) {
       joinCond = pushDownEqualJoinConditions(
           joinCond, leftCount, rightCount, extraLeftExprs, extraRightExprs);
     }
@@ -3177,7 +3203,7 @@ public abstract class RelOptUtil {
 
   /** Visitor that finds all variables used but not stopped in an expression. */
   private static class VariableSetVisitor extends RelVisitor {
-    final Set<String> variables = new HashSet<>();
+    final Set<CorrelationId> variables = new HashSet<>();
 
     // implement RelVisitor
     public void visit(
@@ -3189,18 +3215,42 @@ public abstract class RelOptUtil {
 
       // Important! Remove stopped variables AFTER we visit children
       // (which what super.visit() does)
-      variables.removeAll(p.getVariablesStopped());
+      variables.removeAll(p.getVariablesSet());
     }
   }
 
   /** Visitor that finds all variables used in an expression. */
   public static class VariableUsedVisitor extends RexShuttle {
-    public final Set<String> variables = new LinkedHashSet<>();
+    public final Set<CorrelationId> variables = new LinkedHashSet<>();
+    public final Multimap<CorrelationId, Integer> variableFields =
+        LinkedHashMultimap.create();
+    private final RelShuttle relShuttle;
+
+    public VariableUsedVisitor(RelShuttle relShuttle) {
+      this.relShuttle = relShuttle;
+    }
 
-    public RexNode visitCorrelVariable(RexCorrelVariable p) {
-      variables.add(p.getName());
+    @Override public RexNode visitCorrelVariable(RexCorrelVariable p) {
+      variables.add(p.id);
+      variableFields.put(p.id, -1);
       return p;
     }
+
+    @Override public RexNode visitFieldAccess(RexFieldAccess fieldAccess) {
+      if (fieldAccess.getReferenceExpr() instanceof RexCorrelVariable) {
+        final RexCorrelVariable v =
+            (RexCorrelVariable) fieldAccess.getReferenceExpr();
+        variableFields.put(v.id, fieldAccess.getField().getIndex());
+      }
+      return super.visitFieldAccess(fieldAccess);
+    }
+
+    @Override public RexNode visitSubQuery(RexSubQuery subQuery) {
+      if (relShuttle != null) {
+        subQuery.rel.accept(relShuttle); // look inside sub-queries
+      }
+      return super.visitSubQuery(subQuery);
+    }
   }
 
   /** Shuttle that finds the set of inputs that are used. */
@@ -3462,6 +3512,23 @@ public abstract class RelOptUtil {
       return BOTH;
     }
   }
+
+  /** Shuttle that finds correlation variables inside a given relational
+   * expression, including those that are inside
+   * {@link RexSubQuery sub-queries}. */
+  private static class CorrelationCollector extends RelHomogeneousShuttle {
+    private final VariableUsedVisitor vuv = new VariableUsedVisitor(this);
+
+    @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.getVariablesSet());
+      return result;
+    }
+  }
 }
 
 // End RelOptUtil.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
index 8dd0d01..ccbe2c2 100644
--- a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
+++ b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
@@ -24,6 +24,7 @@ import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.SingleRel;
 import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.AggregateCall;
+import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.core.Join;
 import org.apache.calcite.rel.core.JoinRelType;
@@ -249,7 +250,7 @@ public class SubstitutionVisitor {
       final MutableRel left = toMutable(join.getLeft());
       final MutableRel right = toMutable(join.getRight());
       return MutableJoin.of(join.getCluster(), left, right,
-          join.getCondition(), join.getJoinType(), join.getVariablesStopped());
+          join.getCondition(), join.getJoinType(), join.getVariablesSet());
     }
     if (rel instanceof Sort) {
       final Sort sort = (Sort) rel;
@@ -646,7 +647,7 @@ public class SubstitutionVisitor {
     case JOIN:
       final MutableJoin join = (MutableJoin) node;
       return LogicalJoin.create(fromMutable(join.getLeft()), fromMutable(join.getRight()),
-          join.getCondition(), join.getJoinType(), join.getVariablesStopped());
+          join.getCondition(), join.getVariablesSet(), join.getJoinType());
     default:
       throw new AssertionError(node.deep());
     }
@@ -690,7 +691,7 @@ public class SubstitutionVisitor {
       final MutableJoin join = (MutableJoin) node;
       return MutableJoin.of(join.cluster, copyMutable(join.getLeft()),
           copyMutable(join.getRight()), join.getCondition(), join.getJoinType(),
-          join.getVariablesStopped());
+          join.getVariablesSet());
     default:
       throw new AssertionError(node.deep());
     }
@@ -1980,7 +1981,7 @@ public class SubstitutionVisitor {
     //~ Instance fields --------------------------------------------------------
 
     protected final RexNode condition;
-    protected final ImmutableSet<String> variablesStopped;
+    protected final ImmutableSet<CorrelationId> variablesSet;
 
     /**
      * Values must be of enumeration {@link JoinRelType}, except that
@@ -1994,10 +1995,10 @@ public class SubstitutionVisitor {
         MutableRel right,
         RexNode condition,
         JoinRelType joinType,
-        Set<String> variablesStopped) {
+        Set<CorrelationId> variablesSet) {
       super(MutableRelType.JOIN, left.cluster, rowType, left, right);
       this.condition = Preconditions.checkNotNull(condition);
-      this.variablesStopped = ImmutableSet.copyOf(variablesStopped);
+      this.variablesSet = ImmutableSet.copyOf(variablesSet);
       this.joinType = Preconditions.checkNotNull(joinType);
     }
 
@@ -2009,13 +2010,13 @@ public class SubstitutionVisitor {
       return joinType;
     }
 
-    public ImmutableSet getVariablesStopped() {
-      return variablesStopped;
+    public ImmutableSet<CorrelationId> getVariablesSet() {
+      return variablesSet;
     }
 
     static MutableJoin of(RelOptCluster cluster, MutableRel left,
         MutableRel right, RexNode condition, JoinRelType joinType,
-        Set<String> variablesStopped) {
+        Set<CorrelationId> variablesStopped) {
       List<RelDataTypeField> fieldList = Collections.emptyList();
       RelDataType rowType =
           Join.deriveJoinRowType(left.getRowType(), right.getRowType(),

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/plan/volcano/RelSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/RelSet.java b/core/src/main/java/org/apache/calcite/plan/volcano/RelSet.java
index 93d8509..cf78dff 100644
--- a/core/src/main/java/org/apache/calcite/plan/volcano/RelSet.java
+++ b/core/src/main/java/org/apache/calcite/plan/volcano/RelSet.java
@@ -22,6 +22,7 @@ import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.plan.RelTrait;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.util.trace.CalciteTrace;
 
 import com.google.common.collect.ImmutableList;
@@ -47,21 +48,20 @@ class RelSet {
 
   //~ Instance fields --------------------------------------------------------
 
-  final List<RelNode> rels = new ArrayList<RelNode>();
+  final List<RelNode> rels = new ArrayList<>();
   /**
    * Relational expressions that have a subset in this set as a child. This
    * is a multi-set. If multiple relational expressions in this set have the
    * same parent, there will be multiple entries.
    */
-  final List<RelNode> parents = new ArrayList<RelNode>();
-  final List<RelSubset> subsets = new ArrayList<RelSubset>();
+  final List<RelNode> parents = new ArrayList<>();
+  final List<RelSubset> subsets = new ArrayList<>();
 
   /**
    * List of {@link AbstractConverter} objects which have not yet been
    * satisfied.
    */
-  final List<AbstractConverter> abstractConverters =
-      new ArrayList<AbstractConverter>();
+  final List<AbstractConverter> abstractConverters = new ArrayList<>();
 
   /**
    * Set to the superseding set when this is found to be equivalent to another
@@ -71,15 +71,15 @@ class RelSet {
   RelNode rel;
 
   /**
-   * Names of variables which are set by relational expressions in this set
+   * Variables that are set by relational expressions in this set
    * and available for use by parent and child expressions.
    */
-  final Set<String> variablesPropagated;
+  final Set<CorrelationId> variablesPropagated;
 
   /**
-   * Names of variables which are used by relational expressions in this set.
+   * Variables that are used by relational expressions in this set.
    */
-  final Set<String> variablesUsed;
+  final Set<CorrelationId> variablesUsed;
   final int id;
 
   /**
@@ -91,8 +91,8 @@ class RelSet {
 
   RelSet(
       int id,
-      Set<String> variablesPropagated,
-      Set<String> variablesUsed) {
+      Set<CorrelationId> variablesPropagated,
+      Set<CorrelationId> variablesUsed) {
     this.id = id;
     this.variablesPropagated = variablesPropagated;
     this.variablesUsed = variablesUsed;
@@ -275,7 +275,7 @@ class RelSet {
     }
 
     // Make sure the cost changes as a result of merging are propagated.
-    Set<RelSubset> activeSet = new HashSet<RelSubset>();
+    Set<RelSubset> activeSet = new HashSet<>();
     for (RelNode parentRel : getParentRels()) {
       final RelSubset parentSubset = planner.getSubset(parentRel);
       parentSubset.propagateCostImprovements(

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/plan/volcano/RelSubset.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/RelSubset.java b/core/src/main/java/org/apache/calcite/plan/volcano/RelSubset.java
index bb42218..3fede02 100644
--- a/core/src/main/java/org/apache/calcite/plan/volcano/RelSubset.java
+++ b/core/src/main/java/org/apache/calcite/plan/volcano/RelSubset.java
@@ -28,6 +28,7 @@ import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.AbstractRelNode;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelWriter;
+import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.util.Litmus;
@@ -136,14 +137,6 @@ public class RelSubset extends AbstractRelNode {
     }
   }
 
-  public Set<String> getVariablesSet() {
-    return set.variablesPropagated;
-  }
-
-  public Set<String> getVariablesUsed() {
-    return set.variablesUsed;
-  }
-
   public RelNode getBest() {
     return best;
   }
@@ -164,7 +157,7 @@ public class RelSubset extends AbstractRelNode {
     }
   }
 
-  public void explain(RelWriter pw) {
+  @Override public void explain(RelWriter pw) {
     // Not a typical implementation of "explain". We don't gather terms &
     // values to be printed later. We actually do the work.
     String s = getDescription();
@@ -178,7 +171,7 @@ public class RelSubset extends AbstractRelNode {
     pw.done(input);
   }
 
-  protected String computeDigest() {
+  @Override protected String computeDigest() {
     StringBuilder digest = new StringBuilder("Subset#");
     digest.append(set.id);
     for (RelTrait trait : traitSet) {
@@ -276,13 +269,13 @@ public class RelSubset extends AbstractRelNode {
           "rowtype of set", getRowType(), Litmus.THROW);
     }
     set.addInternal(rel);
-    Set<String> variablesSet = RelOptUtil.getVariablesSet(rel);
-    Set<String> variablesStopped = rel.getVariablesStopped();
+    Set<CorrelationId> variablesSet = RelOptUtil.getVariablesSet(rel);
+    Set<CorrelationId> variablesStopped = rel.getVariablesSet();
     if (false) {
-      Set<String> variablesPropagated =
+      Set<CorrelationId> variablesPropagated =
           Util.minus(variablesSet, variablesStopped);
       assert set.variablesPropagated.containsAll(variablesPropagated);
-      Set<String> variablesUsed = RelOptUtil.getVariablesUsed(rel);
+      Set<CorrelationId> variablesUsed = RelOptUtil.getVariablesUsed(rel);
       assert set.variablesUsed.containsAll(variablesUsed);
     }
   }
@@ -375,12 +368,12 @@ public class RelSubset extends AbstractRelNode {
     }
   }
 
-  public void collectVariablesUsed(Set<String> variableSet) {
-    variableSet.addAll(getVariablesUsed());
+  @Override public void collectVariablesUsed(Set<CorrelationId> variableSet) {
+    variableSet.addAll(set.variablesUsed);
   }
 
-  public void collectVariablesSet(Set<String> variableSet) {
-    variableSet.addAll(getVariablesSet());
+  @Override public void collectVariablesSet(Set<CorrelationId> variableSet) {
+    variableSet.addAll(set.variablesPropagated);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
index 4186232..425ebec 100644
--- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
+++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
@@ -1759,7 +1759,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
           nextSetId++,
           Util.minus(
               RelOptUtil.getVariablesSet(rel),
-              rel.getVariablesStopped()),
+              rel.getVariablesSet()),
           RelOptUtil.getVariablesUsed(rel));
       this.allSets.add(set);
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/prepare/Prepare.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/Prepare.java b/core/src/main/java/org/apache/calcite/prepare/Prepare.java
index 3530e93..e624d1d 100644
--- a/core/src/main/java/org/apache/calcite/prepare/Prepare.java
+++ b/core/src/main/java/org/apache/calcite/prepare/Prepare.java
@@ -87,9 +87,13 @@ public abstract class Prepare {
   public static final TryThreadLocal<Boolean> THREAD_TRIM =
       TryThreadLocal.of(false);
 
-  /** Temporary, while CALCITE-816 is under development.
+  /** Temporary, until
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-1045">[CALCITE-1045]
+   * Decorrelate sub-queries in Project and Join</a> is fixed.
    *
-   * @see org.apache.calcite.util.Util#deprecated(Object, boolean) */
+   * <p>The default is false, meaning do not expand queries during sql-to-rel,
+   * but a few tests override and set it to true. After CALCITE-1045
+   * is fixed, remove those overrides and use false everywhere. */
   public static final TryThreadLocal<Boolean> THREAD_EXPAND =
       TryThreadLocal.of(false);
 
@@ -209,6 +213,7 @@ public abstract class Prepare {
 
     SqlToRelConverter sqlToRelConverter =
         getSqlToRelConverter(validator, catalogReader);
+    sqlToRelConverter.setExpand(THREAD_EXPAND.get());
 
     SqlExplain sqlExplain = null;
     if (sqlQuery.getKind() == SqlKind.EXPLAIN) {
@@ -344,6 +349,7 @@ public abstract class Prepare {
         getSqlToRelConverter(
             getSqlValidator(), catalogReader);
     converter.setTrimUnusedFields(shouldTrim(root.rel));
+    converter.setExpand(THREAD_EXPAND.get());
     final boolean ordered = !root.collation.getFieldCollations().isEmpty();
     final boolean dml = SqlKind.DML.contains(root.kind);
     return root.withRel(converter.trimUnusedFields(dml || ordered, root.rel));

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/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 1938447..75a546c 100644
--- a/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java
+++ b/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java
@@ -26,6 +26,7 @@ import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.plan.RelTrait;
 import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.externalize.RelWriterImpl;
 import org.apache.calcite.rel.metadata.Metadata;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
@@ -40,6 +41,7 @@ import org.apache.calcite.util.Util;
 import org.apache.calcite.util.trace.CalciteTrace;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -237,15 +239,19 @@ public abstract class AbstractRelNode implements RelNode {
     return 1.0;
   }
 
-  public Set<String> getVariablesStopped() {
-    return Collections.emptySet();
+  public final Set<String> getVariablesStopped() {
+    return CorrelationId.names(getVariablesSet());
   }
 
-  public void collectVariablesUsed(Set<String> variableSet) {
+  public Set<CorrelationId> getVariablesSet() {
+    return ImmutableSet.of();
+  }
+
+  public void collectVariablesUsed(Set<CorrelationId> variableSet) {
     // for default case, nothing to do
   }
 
-  public void collectVariablesSet(Set<String> variableSet) {
+  public void collectVariablesSet(Set<CorrelationId> variableSet) {
   }
 
   public void childrenAccept(RelVisitor visitor) {
@@ -305,7 +311,7 @@ public abstract class AbstractRelNode implements RelNode {
 
   public RelNode onRegister(RelOptPlanner planner) {
     List<RelNode> oldInputs = getInputs();
-    List<RelNode> inputs = new ArrayList<RelNode>(oldInputs.size());
+    List<RelNode> inputs = new ArrayList<>(oldInputs.size());
     for (final RelNode input : oldInputs) {
       RelNode e = planner.ensureRegistered(input, null);
       if (e != input) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/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 29dc023..973bc98 100644
--- a/core/src/main/java/org/apache/calcite/rel/RelNode.java
+++ b/core/src/main/java/org/apache/calcite/rel/RelNode.java
@@ -23,6 +23,7 @@ import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelOptQuery;
 import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.metadata.Metadata;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
@@ -175,18 +176,40 @@ public interface RelNode extends RelOptNode, Cloneable {
   double getRows();
 
   /**
-   * Returns the names of variables which are set in this relational
+   * Returns the names of variables that are set in this relational
    * expression but also used and therefore not available to parents of this
    * relational expression.
+   *
    * <p>Note: only {@link org.apache.calcite.rel.core.Correlate} should set
-   * variables</p>
+   * variables.
+   *
+   * <p>Note: {@link #getVariablesSet()} is equivalent but returns
+   * {@link CorrelationId} rather than their names. It is preferable except for
+   * calling old methods that require a set of strings.
    *
    * @return Names of variables which are set in this relational
    *   expression
+   *
+   * @deprecated Use {@link #getVariablesSet()}
+   * and {@link CorrelationId#names(Set)}
    */
+  @Deprecated // to be removed before 2.0
   Set<String> getVariablesStopped();
 
   /**
+   * Returns the variables that are set in this relational
+   * expression but also used and therefore not available to parents of this
+   * relational expression.
+   *
+   * <p>Note: only {@link org.apache.calcite.rel.core.Correlate} should set
+   * variables.
+   *
+   * @return Names of variables which are set in this relational
+   *   expression
+   */
+  Set<CorrelationId> getVariablesSet();
+
+  /**
    * Collects variables known to be used by this expression or its
    * descendants. By default, no such information is available and must be
    * derived by analyzing sub-expressions, but some optimizer implementations
@@ -194,7 +217,7 @@ public interface RelNode extends RelOptNode, Cloneable {
    *
    * @param variableSet receives variables used
    */
-  void collectVariablesUsed(Set<String> variableSet);
+  void collectVariablesUsed(Set<CorrelationId> variableSet);
 
   /**
    * Collects variables set by this expression.
@@ -202,7 +225,7 @@ public interface RelNode extends RelOptNode, Cloneable {
    *
    * @param variableSet receives variables known to be set by
    */
-  void collectVariablesSet(Set<String> variableSet);
+  void collectVariablesSet(Set<CorrelationId> variableSet);
 
   /**
    * Interacts with the {@link RelVisitor} in a

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/rel/core/Correlate.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Correlate.java b/core/src/main/java/org/apache/calcite/rel/core/Correlate.java
index b1c0e04..982a762 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Correlate.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Correlate.java
@@ -19,6 +19,7 @@ package org.apache.calcite.rel.core;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptCost;
 import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.BiRel;
 import org.apache.calcite.rel.RelInput;
@@ -29,6 +30,7 @@ import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.sql.SemiJoinType;
 import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.Litmus;
 
 import com.google.common.collect.ImmutableSet;
 
@@ -109,6 +111,11 @@ public abstract class Correlate extends BiRel {
 
   //~ Methods ----------------------------------------------------------------
 
+  @Override public boolean isValid(Litmus litmus) {
+    return super.isValid(litmus)
+        && RelOptUtil.notContainsCorrelation(left, correlationId, litmus);
+  }
+
   @Override public Correlate copy(RelTraitSet traitSet, List<RelNode> inputs) {
     assert inputs.size() == 2;
     return copy(traitSet,
@@ -133,8 +140,8 @@ public abstract class Correlate extends BiRel {
     case INNER:
       // LogicalJoin is used to share the code of column names deduplication
       final LogicalJoin join = LogicalJoin.create(left, right,
-          getCluster().getRexBuilder().makeLiteral(true), joinType.toJoinType(),
-          ImmutableSet.<String>of());
+          getCluster().getRexBuilder().makeLiteral(true),
+          ImmutableSet.<CorrelationId>of(), joinType.toJoinType());
       return join.deriveRowType();
     case ANTI:
     case SEMI:
@@ -174,8 +181,8 @@ public abstract class Correlate extends BiRel {
     return requiredColumns;
   }
 
-  @Override public Set<String> getVariablesStopped() {
-    return ImmutableSet.of(correlationId.getName());
+  @Override public Set<CorrelationId> getVariablesSet() {
+    return ImmutableSet.of(correlationId);
   }
 
   @Override public RelOptCost computeSelfCost(RelOptPlanner planner) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/rel/core/CorrelationId.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/CorrelationId.java b/core/src/main/java/org/apache/calcite/rel/core/CorrelationId.java
index 2b2558c..d5d74c9 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/CorrelationId.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/CorrelationId.java
@@ -16,39 +16,51 @@
  */
 package org.apache.calcite.rel.core;
 
+import com.google.common.collect.ImmutableSet;
+
+import java.util.Set;
+
 /**
  * Describes the necessary parameters for an implementation in order to
  * identify and set dynamic variables
  */
 public class CorrelationId implements Cloneable, Comparable<CorrelationId> {
-  private static final String CORREL_PREFIX = "$cor";
+  /**
+   * Prefix to the name of correlating variables.
+   */
+  public static final String CORREL_PREFIX = "$cor";
 
   private final int id;
   private final String name;
 
   /**
    * Creates a correlation identifier.
+   */
+  private CorrelationId(int id, String name) {
+    this.id = id;
+    this.name = name;
+  }
+
+  /**
+   * Creates a correlation identifier.
    * This is a type-safe wrapper over int.
    *
    * @param id     Identifier
    */
   public CorrelationId(int id) {
-    this.id = id;
-    this.name = CORREL_PREFIX + id;
+    this(id, CORREL_PREFIX + id);
   }
 
   /**
-   * Creates a correlation identifier.
-   * This is a type-safe wrapper over int.
+   * Creates a correlation identifier from a name.
    *
    * @param name     variable name
    */
   public CorrelationId(String name) {
-    assert name != null && name.startsWith(CORREL_PREFIX)
+    this(Integer.parseInt(name.substring(CORREL_PREFIX.length())), name);
+    assert name.startsWith(CORREL_PREFIX)
         : "Correlation name should start with " + CORREL_PREFIX
         + " actual name is " + name;
-    this.id = Integer.parseInt(name.substring(CORREL_PREFIX.length()));
-    this.name = name;
   }
 
   /**
@@ -61,7 +73,7 @@ public class CorrelationId implements Cloneable, Comparable<CorrelationId> {
   }
 
   /**
-   * Returns the preffered name of the variable.
+   * Returns the preferred name of the variable.
    *
    * @return name
    */
@@ -86,6 +98,30 @@ public class CorrelationId implements Cloneable, Comparable<CorrelationId> {
         || obj instanceof CorrelationId
         && this.id == ((CorrelationId) obj).id;
   }
+
+  /** Converts a set of correlation ids to a set of names. */
+  public static ImmutableSet<CorrelationId> setOf(Set<String> set) {
+    if (set.isEmpty()) {
+      return ImmutableSet.of();
+    }
+    final ImmutableSet.Builder<CorrelationId> builder = ImmutableSet.builder();
+    for (String s : set) {
+      builder.add(new CorrelationId(s));
+    }
+    return builder.build();
+  }
+
+  /** Converts a set of names to a set of correlation ids. */
+  public static Set<String> names(Set<CorrelationId> set) {
+    if (set.isEmpty()) {
+      return ImmutableSet.of();
+    }
+    final ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+    for (CorrelationId s : set) {
+      builder.add(s.name);
+    }
+    return builder.build();
+  }
 }
 
 // End CorrelationId.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/rel/core/EquiJoin.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/EquiJoin.java b/core/src/main/java/org/apache/calcite/rel/core/EquiJoin.java
index 3a06c86..a45a854 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/EquiJoin.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/EquiJoin.java
@@ -36,13 +36,22 @@ public abstract class EquiJoin extends Join {
   /** Creates an EquiJoin. */
   public EquiJoin(RelOptCluster cluster, RelTraitSet traits, RelNode left,
       RelNode right, RexNode condition, ImmutableIntList leftKeys,
-      ImmutableIntList rightKeys, JoinRelType joinType,
-      Set<String> variablesStopped) {
-    super(cluster, traits, left, right, condition, joinType, variablesStopped);
+      ImmutableIntList rightKeys, Set<CorrelationId> variablesSet,
+      JoinRelType joinType) {
+    super(cluster, traits, left, right, condition, variablesSet, joinType);
     this.leftKeys = Preconditions.checkNotNull(leftKeys);
     this.rightKeys = Preconditions.checkNotNull(rightKeys);
   }
 
+  @Deprecated // to be removed before 2.0
+  public EquiJoin(RelOptCluster cluster, RelTraitSet traits, RelNode left,
+      RelNode right, RexNode condition, ImmutableIntList leftKeys,
+      ImmutableIntList rightKeys, JoinRelType joinType,
+      Set<String> variablesStopped) {
+    this(cluster, traits, left, right, condition, leftKeys, rightKeys,
+        CorrelationId.setOf(variablesStopped), joinType);
+  }
+
   public ImmutableIntList getLeftKeys() {
     return leftKeys;
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/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 bb460ff..3ac32d2 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
@@ -35,6 +35,7 @@ import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.Litmus;
 import org.apache.calcite.util.Util;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 
@@ -56,43 +57,67 @@ public abstract class Join extends BiRel {
   //~ Instance fields --------------------------------------------------------
 
   protected final RexNode condition;
-  protected final ImmutableSet<String> variablesStopped;
+  protected final ImmutableSet<CorrelationId> variablesSet;
 
   /**
    * Values must be of enumeration {@link JoinRelType}, except that
    * {@link JoinRelType#RIGHT} is disallowed.
    */
-  protected JoinRelType joinType;
+  protected final JoinRelType joinType;
 
   //~ Constructors -----------------------------------------------------------
 
+  // Next time we need to change the constructor of Join, let's change the
+  // "Set<String> variablesStopped" parameter to
+  // "Set<CorrelationId> variablesSet". At that point we would deprecate
+  // RelNode.getVariablesStopped().
+
   /**
    * Creates a Join.
    *
+   * <p>Note: We plan to change the {@code variablesStopped} parameter to
+   * {@code Set&lt;CorrelationId&gt; variablesSet}
+   * {@link org.apache.calcite.util.Bug#upgrade(String) before version 2.0},
+   * because {@link #getVariablesSet()}
+   * is preferred over {@link #getVariablesStopped()}.
+   * This constructor is not deprecated, for now, because maintaining overloaded
+   * constructors in multiple sub-classes would be onerous.
+   *
    * @param cluster          Cluster
-   * @param traits           Traits
+   * @param traitSet         Trait set
    * @param left             Left input
    * @param right            Right input
    * @param condition        Join condition
    * @param joinType         Join type
-   * @param variablesStopped Set of names of variables which are set by the
+   * @param variablesSet     Set variables that are set by the
    *                         LHS and used by the RHS and are not available to
-   *                         nodes above this LogicalJoin in the tree
+   *                         nodes above this Join in the tree
    */
   protected Join(
       RelOptCluster cluster,
-      RelTraitSet traits,
+      RelTraitSet traitSet,
+      RelNode left,
+      RelNode right,
+      RexNode condition,
+      Set<CorrelationId> variablesSet,
+      JoinRelType joinType) {
+    super(cluster, traitSet, left, right);
+    this.condition = Preconditions.checkNotNull(condition);
+    this.variablesSet = ImmutableSet.copyOf(variablesSet);
+    this.joinType = Preconditions.checkNotNull(joinType);
+  }
+
+  @Deprecated // to be removed before 2.0
+  protected Join(
+      RelOptCluster cluster,
+      RelTraitSet traitSet,
       RelNode left,
       RelNode right,
       RexNode condition,
       JoinRelType joinType,
       Set<String> variablesStopped) {
-    super(cluster, traits, left, right);
-    this.condition = condition;
-    this.variablesStopped = ImmutableSet.copyOf(variablesStopped);
-    assert joinType != null;
-    assert condition != null;
-    this.joinType = joinType;
+    this(cluster, traitSet, left, right, condition,
+        CorrelationId.setOf(variablesStopped), joinType);
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -172,8 +197,8 @@ public abstract class Join extends BiRel {
     return Util.first(RelMdUtil.getJoinRowCount(this, condition), 1D);
   }
 
-  @Override public Set<String> getVariablesStopped() {
-    return variablesStopped;
+  @Override public Set<CorrelationId> getVariablesSet() {
+    return variablesSet;
   }
 
   @Override public RelWriter explainTerms(RelWriter pw) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java b/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java
index d58150b..6c410b9 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java
@@ -24,6 +24,7 @@ import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelNode;
 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;
@@ -37,6 +38,7 @@ import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.SemiJoinType;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.tools.RelBuilderFactory;
@@ -60,6 +62,9 @@ public class RelFactories {
 
   public static final JoinFactory DEFAULT_JOIN_FACTORY = new JoinFactoryImpl();
 
+  public static final CorrelateFactory DEFAULT_CORRELATE_FACTORY =
+      new CorrelateFactoryImpl();
+
   public static final SemiJoinFactory DEFAULT_SEMI_JOIN_FACTORY =
       new SemiJoinFactoryImpl();
 
@@ -234,14 +239,19 @@ public class RelFactories {
      * @param left             Left input
      * @param right            Right input
      * @param condition        Join condition
-     * @param joinType         Join type
-     * @param variablesStopped Set of names of variables which are set by the
+     * @param variablesSet     Set of variables that are set by the
      *                         LHS and used by the RHS and are not available to
      *                         nodes above this LogicalJoin in the tree
+     * @param joinType         Join type
      * @param semiJoinDone     Whether this join has been translated to a
      *                         semi-join
      */
     RelNode createJoin(RelNode left, RelNode right, RexNode condition,
+        Set<CorrelationId> variablesSet, JoinRelType joinType,
+        boolean semiJoinDone);
+
+    @Deprecated // to be removed before 2.0
+    RelNode createJoin(RelNode left, RelNode right, RexNode condition,
         JoinRelType joinType, Set<String> variablesStopped,
         boolean semiJoinDone);
   }
@@ -252,10 +262,51 @@ public class RelFactories {
    */
   private static class JoinFactoryImpl implements JoinFactory {
     public RelNode createJoin(RelNode left, RelNode right,
-        RexNode condition, JoinRelType joinType,
-        Set<String> variablesStopped, boolean semiJoinDone) {
-      return LogicalJoin.create(left, right, condition, joinType,
-          variablesStopped, semiJoinDone, ImmutableList.<RelDataTypeField>of());
+        RexNode condition, Set<CorrelationId> variablesSet,
+        JoinRelType joinType, boolean semiJoinDone) {
+      return LogicalJoin.create(left, right, condition, variablesSet, joinType,
+          semiJoinDone, ImmutableList.<RelDataTypeField>of());
+    }
+
+    public RelNode createJoin(RelNode left, RelNode right, RexNode condition,
+        JoinRelType joinType, Set<String> variablesStopped,
+        boolean semiJoinDone) {
+      return createJoin(left, right, condition,
+          CorrelationId.setOf(variablesStopped), joinType, semiJoinDone);
+    }
+  }
+
+  /**
+   * Can create a correlate of the appropriate type for a rule's calling
+   * convention.
+   *
+   * <p>The result is typically a {@link Correlate}.
+   */
+  public interface CorrelateFactory {
+    /**
+     * Creates a correlate.
+     *
+     * @param left             Left input
+     * @param right            Right input
+     * @param correlationId    Variable name for the row of left input
+     * @param requiredColumns  Required columns
+     * @param joinType         Join type
+     */
+    RelNode createCorrelate(RelNode left, RelNode right,
+        CorrelationId correlationId, ImmutableBitSet requiredColumns,
+        SemiJoinType joinType);
+  }
+
+  /**
+   * Implementation of {@link CorrelateFactory} that returns a vanilla
+   * {@link org.apache.calcite.rel.logical.LogicalCorrelate}.
+   */
+  private static class CorrelateFactoryImpl implements CorrelateFactory {
+    public RelNode createCorrelate(RelNode left, RelNode right,
+        CorrelationId correlationId, ImmutableBitSet requiredColumns,
+        SemiJoinType joinType) {
+      return LogicalCorrelate.create(left, right, correlationId,
+          requiredColumns, joinType);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/rel/core/SemiJoin.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/SemiJoin.java b/core/src/main/java/org/apache/calcite/rel/core/SemiJoin.java
index 4570919..6db45f1 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/SemiJoin.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/SemiJoin.java
@@ -71,8 +71,8 @@ public class SemiJoin extends EquiJoin {
         condition,
         leftKeys,
         rightKeys,
-        JoinRelType.INNER,
-        ImmutableSet.<String>of());
+        ImmutableSet.<CorrelationId>of(),
+        JoinRelType.INNER);
   }
 
   /** Creates a SemiJoin. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/rel/externalize/RelJson.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelJson.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelJson.java
index a4da2d4..92324ba 100644
--- a/core/src/main/java/org/apache/calcite/rel/externalize/RelJson.java
+++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelJson.java
@@ -397,7 +397,7 @@ public class RelJson {
       if (correl != null) {
         final Object jsonType = map.get("type");
         RelDataType type = toType(cluster.getTypeFactory(), jsonType);
-        return rexBuilder.makeCorrel(type, correl);
+        return rexBuilder.makeCorrel(type, new CorrelationId(correl));
       }
       if (map.containsKey("literal")) {
         final Object literal = map.get("literal");

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/rel/logical/LogicalCalc.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalCalc.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalCalc.java
index 3fb2123..3ca1645 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalCalc.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalCalc.java
@@ -26,6 +26,7 @@ import org.apache.calcite.rel.RelDistribution;
 import org.apache.calcite.rel.RelDistributionTraitDef;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Calc;
+import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.metadata.RelMdCollation;
 import org.apache.calcite.rel.metadata.RelMdDistribution;
 import org.apache.calcite.rel.rules.FilterToCalcRule;
@@ -113,9 +114,9 @@ public final class LogicalCalc extends Calc {
     return new LogicalCalc(getCluster(), traitSet, child, program);
   }
 
-  @Override public void collectVariablesUsed(Set<String> variableSet) {
+  @Override public void collectVariablesUsed(Set<CorrelationId> variableSet) {
     final RelOptUtil.VariableUsedVisitor vuv =
-        new RelOptUtil.VariableUsedVisitor();
+        new RelOptUtil.VariableUsedVisitor(null);
     for (RexNode expr : program.getExprList()) {
       expr.accept(vuv);
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java
index fd403e0..c0c69d4 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java
@@ -19,6 +19,7 @@ package org.apache.calcite.rel.logical;
 import org.apache.calcite.plan.Convention;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.prepare.CalcitePrepareImpl;
 import org.apache.calcite.rel.RelInput;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelShuttle;
@@ -26,6 +27,7 @@ import org.apache.calcite.rel.core.Correlate;
 import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.sql.SemiJoinType;
 import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.Litmus;
 
 /**
  * A relational operator that performs nested-loop joins.
@@ -69,6 +71,7 @@ public final class LogicalCorrelate extends Correlate {
         correlationId,
         requiredColumns,
         joinType);
+    assert !CalcitePrepareImpl.DEBUG || isValid(Litmus.THROW);
   }
 
   @Deprecated // to be removed before 2.0

http://git-wip-us.apache.org/repos/asf/calcite/blob/505a9064/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
index 79b0b1d..d25874e 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
@@ -26,20 +26,27 @@ import org.apache.calcite.rel.RelDistributionTraitDef;
 import org.apache.calcite.rel.RelInput;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelShuttle;
+import org.apache.calcite.rel.RelWriter;
+import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.metadata.RelMdCollation;
 import org.apache.calcite.rel.metadata.RelMdDistribution;
 import org.apache.calcite.rex.RexNode;
 
+import com.google.common.base.Preconditions;
 import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableSet;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * Sub-class of {@link org.apache.calcite.rel.core.Filter}
  * not targeted at any particular engine or calling convention.
  */
 public final class LogicalFilter extends Filter {
+  private final ImmutableSet<CorrelationId> variablesSet;
+
   //~ Constructors -----------------------------------------------------------
 
   /**
@@ -51,21 +58,35 @@ public final class LogicalFilter extends Filter {
    * @param child     Input relational expression
    * @param condition Boolean expression which determines whether a row is
    *                  allowed to pass
+   * @param variablesSet Correlation variables set by this relational expression
+   *                     to be used by nested expressions
    */
   public LogicalFilter(
       RelOptCluster cluster,
       RelTraitSet traitSet,
       RelNode child,
-      RexNode condition) {
+      RexNode condition,
+      ImmutableSet<CorrelationId> variablesSet) {
     super(cluster, traitSet, child, condition);
+    this.variablesSet = Preconditions.checkNotNull(variablesSet);
   }
 
   @Deprecated // to be removed before 2.0
   public LogicalFilter(
       RelOptCluster cluster,
+      RelTraitSet traitSet,
       RelNode child,
       RexNode condition) {
-    this(cluster, cluster.traitSetOf(Convention.NONE), child, condition);
+    this(cluster, traitSet, child, condition, ImmutableSet.<CorrelationId>of());
+  }
+
+  @Deprecated // to be removed before 2.0
+  public LogicalFilter(
+      RelOptCluster cluster,
+      RelNode child,
+      RexNode condition) {
+    this(cluster, cluster.traitSetOf(Convention.NONE), child, condition,
+        ImmutableSet.<CorrelationId>of());
   }
 
   /**
@@ -73,10 +94,17 @@ public final class LogicalFilter extends Filter {
    */
   public LogicalFilter(RelInput input) {
     super(input);
+    this.variablesSet = ImmutableSet.of();
   }
 
   /** Creates a LogicalFilter. */
   public static LogicalFilter create(final RelNode input, RexNode condition) {
+    return create(input, condition, ImmutableSet.<CorrelationId>of());
+  }
+
+  /** Creates a LogicalFilter. */
+  public static LogicalFilter create(final RelNode input, RexNode condition,
+      ImmutableSet<CorrelationId> variablesSet) {
     final RelOptCluster cluster = input.getCluster();
     final RelTraitSet traitSet = cluster.traitSetOf(Convention.NONE)
         .replaceIfs(RelCollationTraitDef.INSTANCE,
@@ -91,20 +119,30 @@ public final class LogicalFilter extends Filter {
                 return RelMdDistribution.filter(input);
               }
             });
-    return new LogicalFilter(cluster, traitSet, input, condition);
+    return new LogicalFilter(cluster, traitSet, input, condition, variablesSet);
   }
 
   //~ Methods ----------------------------------------------------------------
 
+  @Override public Set<CorrelationId> getVariablesSet() {
+    return variablesSet;
+  }
+
   public LogicalFilter copy(RelTraitSet traitSet, RelNode input,
       RexNode condition) {
     assert traitSet.containsIfApplicable(Convention.NONE);
-    return new LogicalFilter(getCluster(), traitSet, input, condition);
+    return new LogicalFilter(getCluster(), traitSet, input, condition,
+        variablesSet);
   }
 
   @Override public RelNode accept(RelShuttle shuttle) {
     return shuttle.visit(this);
   }
+
+  @Override public RelWriter explainTerms(RelWriter pw) {
+    return super.explainTerms(pw)
+        .itemIf("variablesSet", variablesSet, !variablesSet.isEmpty());
+  }
 }
 
 // End LogicalFilter.java