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

hyuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git

commit 80e6b023d4b92b2b25908c52e10f50792787cef8
Author: Haisheng Yuan <h.y...@alibaba-inc.com>
AuthorDate: Fri Feb 28 16:01:52 2020 -0800

    [CALCITE-3753] Remove rule queue importance
    
    Since we are going to execute all the rules anyway, sorting the rule matches
    will do more harm than good.
    
    Also removed ambitious and impatient mode. User could override checkCancel 
and
    throw VolcanoTimeoutException when appropriate (i.e. exceed 60 seconds, 
exceed
    1000 ticks etc.) so that the VolcanoPlanner will stop exhaustive searching 
and
    return the best plan so far.
    
    Close #1840
---
 .../org/apache/calcite/adapter/jdbc/JdbcRules.java |   5 +
 .../apache/calcite/plan/AbstractRelOptPlanner.java |   2 +-
 .../org/apache/calcite/plan/volcano/RelSet.java    |   1 -
 .../org/apache/calcite/plan/volcano/RelSubset.java |  15 +-
 .../org/apache/calcite/plan/volcano/RuleQueue.java | 427 +--------------------
 .../calcite/plan/volcano/VolcanoPlanner.java       | 215 +----------
 .../calcite/plan/volcano/VolcanoRuleMatch.java     | 101 -----
 .../plan/volcano/VolcanoTimeoutException.java      |  27 ++
 .../calcite/rel/rules/AggregateRemoveRule.java     |   1 +
 .../rel/rules/FilterProjectTransposeRule.java      |   8 +-
 .../calcite/plan/volcano/VolcanoPlannerTest.java   |   3 -
 .../org/apache/calcite/test/JdbcAdapterTest.java   | 126 +++---
 .../java/org/apache/calcite/test/JdbcTest.java     |  10 +-
 .../java/org/apache/calcite/test/LatticeTest.java  |   8 +-
 .../apache/calcite/test/MaterializationTest.java   |  17 +-
 .../apache/calcite/test/ScannableTableTest.java    |   4 +-
 .../test/enumerable/EnumerableCorrelateTest.java   |   8 +-
 .../test/enumerable/EnumerableJoinTest.java        |  22 +-
 core/src/test/resources/sql/misc.iq                |   4 +-
 core/src/test/resources/sql/sub-query.iq           |   6 +-
 core/src/test/resources/sql/winagg.iq              |   6 +-
 .../calcite/adapter/geode/rel/GeodeSort.java       |   2 +-
 .../calcite/adapter/mongodb/MongoAdapterTest.java  |   4 +-
 .../org/apache/calcite/test/SparkAdapterTest.java  |   8 +-
 24 files changed, 180 insertions(+), 850 deletions(-)

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 0e46f95..4b9b1b4 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
@@ -801,6 +801,11 @@ public class JdbcRules {
           offset, fetch);
     }
 
+    @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+        RelMetadataQuery mq) {
+      return super.computeSelfCost(planner, mq).multiplyBy(0.9);
+    }
+
     public JdbcImplementor.Result implement(JdbcImplementor implementor) {
       return implementor.implement(this);
     }
diff --git 
a/core/src/main/java/org/apache/calcite/plan/AbstractRelOptPlanner.java 
b/core/src/main/java/org/apache/calcite/plan/AbstractRelOptPlanner.java
index 25c7c51..4873db8 100644
--- a/core/src/main/java/org/apache/calcite/plan/AbstractRelOptPlanner.java
+++ b/core/src/main/java/org/apache/calcite/plan/AbstractRelOptPlanner.java
@@ -63,7 +63,7 @@ public abstract class AbstractRelOptPlanner implements 
RelOptPlanner {
 
   private Pattern ruleDescExclusionFilter;
 
-  private final AtomicBoolean cancelFlag;
+  protected final AtomicBoolean cancelFlag;
 
   private final Set<Class<? extends RelNode>> classes = new HashSet<>();
 
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 1651219..3c7bcbe 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
@@ -337,7 +337,6 @@ class RelSet {
 
     // merge subsets
     for (RelSubset otherSubset : otherSet.subsets) {
-      planner.ruleQueue.subsetImportances.remove(otherSubset);
       RelSubset subset =
           getOrCreateSubset(
               otherSubset.getCluster(),
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 22fcf14..80bbab8 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
@@ -366,8 +366,7 @@ public class RelSubset extends AbstractRelNode {
         // since best was changed, cached metadata for this subset should be 
removed
         mq.clearCache(this);
 
-        // Recompute subset's importance and propagate cost change to parents
-        planner.ruleQueue.recompute(this);
+        // Propagate cost change to parents
         for (RelNode parent : getParents()) {
           // removes parent cached metadata since its input was changed
           mq.clearCache(parent);
@@ -386,18 +385,6 @@ public class RelSubset extends AbstractRelNode {
     }
   }
 
-  public void propagateBoostRemoval(VolcanoPlanner planner) {
-    planner.ruleQueue.recompute(this);
-
-    if (boosted) {
-      boosted = false;
-
-      for (RelSubset parentSubset : getParentSubsets(planner)) {
-        parentSubset.propagateBoostRemoval(planner);
-      }
-    }
-  }
-
   @Override public void collectVariablesUsed(Set<CorrelationId> variableSet) {
     variableSet.addAll(set.variablesUsed);
   }
diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/RuleQueue.java 
b/core/src/main/java/org/apache/calcite/plan/volcano/RuleQueue.java
index 7e851f8..5ac8bf2 100644
--- a/core/src/main/java/org/apache/calcite/plan/volcano/RuleQueue.java
+++ b/core/src/main/java/org/apache/calcite/plan/volcano/RuleQueue.java
@@ -16,34 +16,24 @@
  */
 package org.apache.calcite.plan.volcano;
 
-import org.apache.calcite.plan.RelOptCost;
 import org.apache.calcite.plan.RelOptRuleOperand;
 import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.RelNodes;
-import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.util.Util;
 import org.apache.calcite.util.trace.CalciteTrace;
 
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Multimap;
-import com.google.common.collect.Ordering;
 
 import org.slf4j.Logger;
 
-import java.io.PrintWriter;
-import java.io.StringWriter;
 import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Comparator;
 import java.util.Deque;
 import java.util.EnumMap;
-import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
+import java.util.LinkedList;
 import java.util.Map;
+import java.util.Queue;
 import java.util.Set;
 
 /**
@@ -57,26 +47,9 @@ class RuleQueue {
 
   private static final Set<String> ALL_RULES = ImmutableSet.of("<ALL RULES>");
 
-  /**
-   * Largest value which is less than one.
-   */
-  private static final double ONE_MINUS_EPSILON = computeOneMinusEpsilon();
-
   //~ Instance fields --------------------------------------------------------
 
   /**
-   * The importance of each subset.
-   */
-  final Map<RelSubset, Double> subsetImportances = new HashMap<>();
-
-  /**
-   * The set of RelSubsets whose importance is currently in an artificially
-   * raised state. Typically this only includes RelSubsets which have only
-   * logical RelNodes.
-   */
-  final Set<RelSubset> boostedSubsets = new HashSet<>();
-
-  /**
    * Map of {@link VolcanoPlannerPhase} to a list of rule-matches. Initially,
    * there is an empty {@link PhaseMatchList} for each planner phase. As the
    * planner invokes {@link #addMatch(VolcanoRuleMatch)} the rule-match is
@@ -87,21 +60,9 @@ class RuleQueue {
   final Map<VolcanoPlannerPhase, PhaseMatchList> matchListMap =
       new EnumMap<>(VolcanoPlannerPhase.class);
 
-  /**
-   * Sorts rule-matches into decreasing order of importance.
-   */
-  private static final Comparator<VolcanoRuleMatch> MATCH_COMPARATOR =
-      new RuleMatchImportanceComparator();
-
   private final VolcanoPlanner planner;
 
   /**
-   * Compares relexps according to their cached 'importance'.
-   */
-  private final Ordering<RelSubset> relImportanceOrdering =
-      Ordering.from(new RelImportanceComparator());
-
-  /**
    * Maps a {@link VolcanoPlannerPhase} to a set of rule descriptions. Named 
rules
    * may be invoked in their corresponding phase.
    *
@@ -143,8 +104,6 @@ class RuleQueue {
    * Clear internal data structure for this rule queue.
    */
   public void clear() {
-    this.subsetImportances.clear();
-    this.boostedSubsets.clear();
     for (PhaseMatchList matchList : matchListMap.values()) {
       matchList.clear();
     }
@@ -159,167 +118,6 @@ class RuleQueue {
   }
 
   /**
-   * Computes the importance of a set (which is that of its most important
-   * subset).
-   */
-  public double getImportance(RelSet set) {
-    double importance = 0;
-    for (RelSubset subset : set.subsets) {
-      importance =
-          Math.max(
-              importance,
-              getImportance(subset));
-    }
-    return importance;
-  }
-
-  /**
-   * Recomputes the importance of the given RelSubset.
-   *
-   * @param subset RelSubset whose importance is to be recomputed
-   * @param force  if true, forces an importance update even if the subset has
-   *               not been registered
-   */
-  public void recompute(RelSubset subset, boolean force) {
-    Double previousImportance = subsetImportances.get(subset);
-    if (previousImportance == null) {
-      if (!force) {
-        // Subset has not been registered yet. Don't worry about it.
-        return;
-      }
-
-      previousImportance = Double.NEGATIVE_INFINITY;
-    }
-
-    double importance = computeImportance(subset);
-    if (previousImportance == importance) {
-      return;
-    }
-
-    updateImportance(subset, importance);
-  }
-
-  /**
-   * Equivalent to
-   * {@link #recompute(RelSubset, boolean) recompute(subset, false)}.
-   */
-  public void recompute(RelSubset subset) {
-    recompute(subset, false);
-  }
-
-  /**
-   * Artificially boosts the importance of the given {@link RelSubset}s by a
-   * given factor.
-   *
-   * <p>Iterates over the currently boosted RelSubsets and removes their
-   * importance boost, forcing a recalculation of the RelSubsets' importances
-   * (see {@link #recompute(RelSubset)}).
-   *
-   * <p>Once RelSubsets have been restored to their normal importance, the
-   * given RelSubsets have their importances boosted. A RelSubset's boosted
-   * importance is always less than 1.0 (and never equal to 1.0).
-   *
-   * @param subsets RelSubsets to boost importance (priority)
-   * @param factor  the amount to boost their importances (e.g., 1.25 increases
-   *                importance by 25%)
-   */
-  public void boostImportance(Collection<RelSubset> subsets, double factor) {
-    LOGGER.trace("boostImportance({}, {})", factor, subsets);
-    final List<RelSubset> boostRemovals = new ArrayList<>();
-    final Iterator<RelSubset> iter = boostedSubsets.iterator();
-    while (iter.hasNext()) {
-      RelSubset subset = iter.next();
-
-      if (!subsets.contains(subset)) {
-        iter.remove();
-        boostRemovals.add(subset);
-      }
-    }
-
-    boostRemovals.sort(new Comparator<RelSubset>() {
-      public int compare(RelSubset o1, RelSubset o2) {
-        int o1children = countChildren(o1);
-        int o2children = countChildren(o2);
-        int c = Integer.compare(o1children, o2children);
-        if (c == 0) {
-          // for determinism
-          c = Integer.compare(o1.getId(), o2.getId());
-        }
-        return c;
-      }
-
-      private int countChildren(RelSubset subset) {
-        int count = 0;
-        for (RelNode rel : subset.getRels()) {
-          count += rel.getInputs().size();
-        }
-        return count;
-      }
-    });
-
-    for (RelSubset subset : boostRemovals) {
-      subset.propagateBoostRemoval(planner);
-    }
-
-    for (RelSubset subset : subsets) {
-      double importance = subsetImportances.get(subset);
-
-      updateImportance(
-          subset,
-          Math.min(ONE_MINUS_EPSILON, importance * factor));
-
-      subset.boosted = true;
-      boostedSubsets.add(subset);
-    }
-  }
-
-  void updateImportance(RelSubset subset, Double importance) {
-    subsetImportances.put(subset, importance);
-
-    for (PhaseMatchList matchList : matchListMap.values()) {
-      Multimap<RelSubset, VolcanoRuleMatch> relMatchMap =
-          matchList.matchMap;
-      if (relMatchMap.containsKey(subset)) {
-        for (VolcanoRuleMatch match : relMatchMap.get(subset)) {
-          match.clearCachedImportance();
-        }
-      }
-    }
-  }
-
-  /**
-   * Returns the importance of an equivalence class of relational expressions.
-   * Subset importances are held in a lookup table, and importance changes
-   * gradually propagate through that table.
-   *
-   * <p>If a subset in the same set but with a different calling convention is
-   * deemed to be important, then this subset has at least half of its
-   * importance. (This rule is designed to encourage conversions to take
-   * place.)</p>
-   */
-  double getImportance(RelSubset rel) {
-    assert rel != null;
-
-    double importance = 0;
-    final RelSet set = planner.getSet(rel);
-    assert set != null;
-    for (RelSubset subset2 : set.subsets) {
-      final Double d = subsetImportances.get(subset2);
-      if (d == null) {
-        continue;
-      }
-      double subsetImportance = d;
-      if (subset2 != rel) {
-        subsetImportance /= 2;
-      }
-      if (subsetImportance > importance) {
-        importance = subsetImportance;
-      }
-    }
-    return importance;
-  }
-
-  /**
    * Adds a rule match. The rule-matches are automatically added to all
    * existing {@link PhaseMatchList per-phase rule-match lists} which allow
    * the rule referenced by the match.
@@ -342,7 +140,7 @@ class RuleQueue {
 
       LOGGER.trace("{} Rule-match queued: {}", matchList.phase.toString(), 
matchName);
 
-      matchList.list.add(match);
+      matchList.queue.offer(match);
 
       matchList.matchMap.put(
           planner.getSubset(match.rels[0]), match);
@@ -350,76 +148,7 @@ class RuleQueue {
   }
 
   /**
-   * Computes the <dfn>importance</dfn> of a node. Importance is defined as
-   * follows:
-   *
-   * <ul>
-   * <li>the root {@link RelSubset} has an importance of 1</li>
-   * <li>the importance of any other subset is the max of its importance to
-   * its parents</li>
-   * <li>The importance of children is pro-rated according to the cost of the
-   * children. Consider a node which has a cost of 3, and children with costs
-   * of 2 and 5. The total cost is 10. If the node has an importance of .5,
-   * then the children will have importance of .1 and .25. The retains .15
-   * importance points, to reflect the fact that work needs to be done on the
-   * node's algorithm.</li>
-   * </ul>
-   *
-   * <p>The formula for the importance <i>I</i> of node n is:
-   *
-   * <blockquote>I<sub>n</sub> = Max<sub>parents p of n</sub>{I<sub>p</sub> .
-   * W <sub>n, p</sub>}</blockquote>
-   *
-   * <p>where W<sub>n, p</sub>, the weight of n within its parent p, is
-   *
-   * <blockquote>W<sub>n, p</sub> = Cost<sub>n</sub> / (SelfCost<sub>p</sub> +
-   * Cost<sub>n0</sub> + ... + Cost<sub>nk</sub>)
-   * </blockquote>
-   */
-  double computeImportance(RelSubset subset) {
-    double importance;
-    if (subset == planner.root) {
-      // The root always has importance = 1
-      importance = 1.0;
-    } else {
-      final RelMetadataQuery mq = subset.getCluster().getMetadataQuery();
-
-      // The importance of a subset is the max of its importance to its
-      // parents
-      importance = 0.0;
-      for (RelSubset parent : subset.getParentSubsets(planner)) {
-        final double childImportance =
-            computeImportanceOfChild(mq, subset, parent);
-        importance = Math.max(importance, childImportance);
-      }
-    }
-    LOGGER.trace("Importance of [{}] is {}", subset, importance);
-    return importance;
-  }
-
-  private void dump() {
-    if (LOGGER.isTraceEnabled()) {
-      StringWriter sw = new StringWriter();
-      PrintWriter pw = new PrintWriter(sw);
-      dump(pw);
-      pw.flush();
-      LOGGER.trace(sw.toString());
-      planner.getRoot().getCluster().invalidateMetadataQuery();
-    }
-  }
-
-  private void dump(PrintWriter pw) {
-    planner.dump(pw);
-    pw.print("Importances: {");
-    for (RelSubset subset
-        : relImportanceOrdering.sortedCopy(subsetImportances.keySet())) {
-      pw.print(" " + subset.toString() + "=" + subsetImportances.get(subset));
-    }
-    pw.println("}");
-  }
-
-  /**
-   * Removes the rule match with the highest importance, and returns it.
+   * Removes the rule match from the head of match list, and returns it.
    *
    * <p>Returns {@code null} if there are no more matches.</p>
    *
@@ -432,60 +161,19 @@ class RuleQueue {
    *                              {@link #phaseCompleted(VolcanoPlannerPhase)}.
    */
   VolcanoRuleMatch popMatch(VolcanoPlannerPhase phase) {
-    dump();
-
     PhaseMatchList phaseMatchList = matchListMap.get(phase);
     if (phaseMatchList == null) {
       throw new AssertionError("Used match list for phase " + phase
           + " after phase complete");
     }
 
-    final List<VolcanoRuleMatch> matchList = phaseMatchList.list;
     VolcanoRuleMatch match;
     for (;;) {
-      if (matchList.isEmpty()) {
+      if (phaseMatchList.queue.isEmpty()) {
         return null;
       }
-      int bestPos;
-      if (LOGGER.isTraceEnabled()) {
-        matchList.sort(MATCH_COMPARATOR);
-        match = matchList.get(0);
-        bestPos = 0;
-
-        StringBuilder b = new StringBuilder();
-        b.append("Sorted rule queue:");
-        for (VolcanoRuleMatch match2 : matchList) {
-          final double importance = match2.computeImportance();
-          b.append("\n");
-          b.append(match2);
-          b.append(" importance ");
-          b.append(importance);
-        }
 
-        LOGGER.trace(b.toString());
-      } else {
-        // If we're not tracing, it's not worth the effort of sorting the
-        // list to find the minimum.
-        match = null;
-        bestPos = -1;
-        int i = -1;
-        for (VolcanoRuleMatch match2 : matchList) {
-          ++i;
-          if (match == null
-              || MATCH_COMPARATOR.compare(match2, match) < 0) {
-            bestPos = i;
-            match = match2;
-          }
-        }
-      }
-      // Removal from the middle is not efficient, but the removal from the 
tail is.
-      // We remove the very last element, then put it to the bestPos index 
which
-      // effectively removes an element from the list.
-      final VolcanoRuleMatch lastElement = matchList.remove(matchList.size() - 
1);
-      if (bestPos < matchList.size()) {
-        // Replace the middle element with the last one
-        matchList.set(bestPos, lastElement);
-      }
+      match = phaseMatchList.queue.poll();
 
       if (skipMatch(match)) {
         LOGGER.debug("Skip match: {}", match);
@@ -566,95 +254,9 @@ class RuleQueue {
     }
   }
 
-  /**
-   * Returns the importance of a child to a parent. This is defined by the
-   * importance of the parent, pro-rated by the cost of the child. For
-   * example, if the parent has importance = 0.8 and cost 100, then a child
-   * with cost 50 will have importance 0.4, and a child with cost 25 will have
-   * importance 0.2.
-   */
-  private double computeImportanceOfChild(RelMetadataQuery mq, RelSubset child,
-      RelSubset parent) {
-    final double parentImportance = getImportance(parent);
-    final double childCost = toDouble(planner.getCost(child, mq));
-    final double parentCost = toDouble(planner.getCost(parent, mq));
-    double alpha = childCost / parentCost;
-    if (alpha >= 1.0) {
-      // child is always less important than parent
-      alpha = 0.99;
-    }
-    final double importance = parentImportance * alpha;
-    LOGGER.trace("Importance of [{}] to its parent [{}] is {} (parent 
importance={}, child cost={},"
-        + " parent cost={})", child, parent, importance, parentImportance, 
childCost, parentCost);
-    return importance;
-  }
-
-  /**
-   * Converts a cost to a scalar quantity.
-   */
-  private double toDouble(RelOptCost cost) {
-    if (cost.isInfinite()) {
-      return 1e+30;
-    } else {
-      return cost.getCpu() + cost.getRows() + cost.getIo();
-    }
-  }
-
-  private static double computeOneMinusEpsilon() {
-    for (double d = 0d;;) {
-      double d0 = d;
-      d = (d + 1d) / 2d;
-      if (d == 1.0) {
-        return d0;
-      }
-    }
-  }
-
   //~ Inner Classes ----------------------------------------------------------
 
   /**
-   * Compares {@link RelNode} objects according to their cached 'importance'.
-   */
-  private class RelImportanceComparator implements Comparator<RelSubset> {
-    public int compare(
-        RelSubset rel1,
-        RelSubset rel2) {
-      double imp1 = getImportance(rel1);
-      double imp2 = getImportance(rel2);
-      int c = Double.compare(imp2, imp1);
-      if (c == 0) {
-        c = rel1.getId() - rel2.getId();
-      }
-      return c;
-    }
-  }
-
-  /**
-   * Compares {@link VolcanoRuleMatch} objects according to their importance.
-   * Matches which are more important collate earlier. Ties are adjudicated by
-   * comparing the {@link RelNode#getId id}s of the relational expressions
-   * matched.
-   */
-  private static class RuleMatchImportanceComparator
-      implements Comparator<VolcanoRuleMatch> {
-    public int compare(VolcanoRuleMatch match1,
-        VolcanoRuleMatch match2) {
-      double imp1 = match1.getImportance();
-      double imp2 = match2.getImportance();
-      int c = Double.compare(imp1, imp2);
-      if (c != 0) {
-        return -c;
-      }
-      c = match1.rule.getClass().getName()
-          .compareTo(match2.rule.getClass().getName());
-      if (c != 0) {
-        return -c;
-      }
-      return -RelNodes.compareRels(match1.rels, match2.rels);
-    }
-  }
-
-  /**
    * PhaseMatchList represents a set of {@link VolcanoRuleMatch rule-matches}
    * for a particular
    * {@link VolcanoPlannerPhase phase of the planner's execution}.
@@ -666,24 +268,20 @@ class RuleQueue {
     final VolcanoPlannerPhase phase;
 
     /**
-     * Current list of VolcanoRuleMatches for this phase. New rule-matches
-     * are appended to the end of this list.
+     * Current queue of VolcanoRuleMatches for this phase. New rule-matches
+     * are appended to the end of this queue.
      * The rules are not sorted in any way.
      */
-    final List<VolcanoRuleMatch> list = new ArrayList<>();
+    final Queue<VolcanoRuleMatch> queue = new LinkedList<>();
 
     /**
-     * A set of rule-match names contained in {@link #list}. Allows fast
+     * A set of rule-match names contained in {@link #queue}. Allows fast
      * detection of duplicate rule-matches.
      */
     final Set<String> names = new HashSet<>();
 
     /**
-     * Multi-map of RelSubset to VolcanoRuleMatches. Used to
-     * {@link VolcanoRuleMatch#clearCachedImportance() clear} the rule-match's
-     * cached importance when the importance of a related RelSubset is modified
-     * (e.g., due to invocation of
-     * {@link RuleQueue#boostImportance(Collection, double)}).
+     * Multi-map of RelSubset to VolcanoRuleMatches.
      */
     final Multimap<RelSubset, VolcanoRuleMatch> matchMap =
         HashMultimap.create();
@@ -693,8 +291,7 @@ class RuleQueue {
     }
 
     void clear() {
-      list.clear();
-      ((ArrayList<VolcanoRuleMatch>) list).trimToSize();
+      queue.clear();
       names.clear();
       matchMap.clear();
     }
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 7b2a6ac..fcdd501 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
@@ -67,8 +67,6 @@ import java.io.StringWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.Deque;
 import java.util.HashMap;
@@ -87,37 +85,12 @@ import java.util.regex.Pattern;
  * according to a dynamic programming algorithm.
  */
 public class VolcanoPlanner extends AbstractRelOptPlanner {
-  protected static final double COST_IMPROVEMENT = .5;
 
   //~ Instance fields --------------------------------------------------------
 
   protected RelSubset root;
 
   /**
-   * If true, the planner keeps applying rules as long as they continue to
-   * reduce the cost. If false, the planner terminates as soon as it has found
-   * any implementation, no matter how expensive.
-   */
-  protected boolean ambitious = true;
-
-  /**
-   * If true, and if {@link #ambitious} is true, the planner waits a finite
-   * number of iterations for the cost to improve.
-   *
-   * <p>The number of iterations K is equal to the number of iterations
-   * required to get the first finite plan. After the first finite plan, it
-   * continues to fire rules to try to improve it. The planner sets a target
-   * cost of the current best cost multiplied by {@link #COST_IMPROVEMENT}. If
-   * it does not meet that cost target within K steps, it quits, and uses the
-   * current best plan. If it meets the cost, it sets a new, lower target, and
-   * has another K iterations to meet it. And so forth.
-   *
-   * <p>If false, the planner continues to fire rules until the rule queue is
-   * empty.
-   */
-  protected boolean impatient = false;
-
-  /**
    * Operands that apply to a given class of {@link RelNode}.
    *
    * <p>Any operand can be an 'entry point' to a rule call, when a RelNode is
@@ -196,12 +169,6 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
   private int nextSetId = 0;
 
   /**
-   * Incremented every time a relational expression is registered or two sets
-   * are merged. Tells us whether anything is going on.
-   */
-  private int registerCount;
-
-  /**
    * Listener for this planner, or null if none set.
    */
   RelOptListener listener;
@@ -301,8 +268,6 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
       this.originalRoot = rel;
     }
 
-    // Making a node the root changes its importance.
-    this.ruleQueue.recompute(this.root);
     ensureRootConverters();
   }
 
@@ -549,24 +514,8 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
    * the exact rules that may be fired varies. The mapping of phases to rule
    * sets is maintained in the {@link #ruleQueue}.
    *
-   * <p>In each phase, the planner sets the initial importance of the existing
-   * RelSubSets ({@link #setInitialImportance()}). The planner then iterates
-   * over the rule matches presented by the rule queue until:
-   *
-   * <ol>
-   * <li>The rule queue becomes empty.</li>
-   * <li>For ambitious planners: No improvements to the plan have been made
-   * recently (specifically within a number of iterations that is 10% of the
-   * number of iterations necessary to first reach an implementable plan or 25
-   * iterations whichever is larger).</li>
-   * <li>For non-ambitious planners: When an implementable plan is found.</li>
-   * </ol>
-   *
-   * <p>Furthermore, after every 10 iterations without an implementable plan,
-   * RelSubSets that contain only logical RelNodes are given an importance
-   * boost via {@link #injectImportanceBoost()}. Once an implementable plan is
-   * found, the artificially raised importance values are cleared (see
-   * {@link #clearImportanceBoost()}).
+   * <p>In each phase, the planner then iterates over the rule matches 
presented
+   * by the rule queue until the rule queue becomes empty.
    *
    * @return the most efficient RelNode tree found for implementing the given
    * query
@@ -574,56 +523,12 @@ public class VolcanoPlanner extends AbstractRelOptPlanner 
{
   public RelNode findBestExp() {
     ensureRootConverters();
     registerMaterializations();
-    int cumulativeTicks = 0;
-    for (VolcanoPlannerPhase phase : VolcanoPlannerPhase.values()) {
-      setInitialImportance();
-
-      RelOptCost targetCost = costFactory.makeHugeCost();
-      int tick = 0;
-      int firstFiniteTick = -1;
-      int splitCount = 0;
-      int giveUpTick = Integer.MAX_VALUE;
 
+    PLANNING:
+    for (VolcanoPlannerPhase phase : VolcanoPlannerPhase.values()) {
       while (true) {
-        ++tick;
-        ++cumulativeTicks;
-        if (root.bestCost.isLe(targetCost)) {
-          if (firstFiniteTick < 0) {
-            firstFiniteTick = cumulativeTicks;
-
-            clearImportanceBoost();
-          }
-          if (ambitious) {
-            // Choose a slightly more ambitious target cost, and
-            // try again. If it took us 1000 iterations to find our
-            // first finite plan, give ourselves another 100
-            // iterations to reduce the cost by 10%.
-            targetCost = root.bestCost.multiplyBy(0.9);
-            ++splitCount;
-            if (impatient) {
-              if (firstFiniteTick < 10) {
-                // It's possible pre-processing can create
-                // an implementable plan -- give us some time
-                // to actually optimize it.
-                giveUpTick = cumulativeTicks + 25;
-              } else {
-                giveUpTick =
-                    cumulativeTicks
-                        + Math.max(firstFiniteTick / 10, 25);
-              }
-            }
-          } else {
-            break;
-          }
-        } else if (cumulativeTicks > giveUpTick) {
-          // We haven't made progress recently. Take the current best.
-          break;
-        } else if (root.bestCost.isInfinite() && ((tick % 10) == 0)) {
-          injectImportanceBoost();
-        }
-
-        LOGGER.debug("PLANNER = {}; TICK = {}/{}; PHASE = {}; COST = {}",
-            this, cumulativeTicks, tick, phase.toString(), root.bestCost);
+        LOGGER.debug("PLANNER = {}; PHASE = {}; COST = {}",
+            this, phase.toString(), root.bestCost);
 
         VolcanoRuleMatch match = ruleQueue.popMatch(phase);
         if (match == null) {
@@ -631,7 +536,13 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
         }
 
         assert match.getRule().matches(match);
-        match.onMatch();
+        try {
+          match.onMatch();
+        } catch (VolcanoTimeoutException e) {
+          root = canonize(root);
+          ruleQueue.phaseCompleted(phase);
+          break PLANNING;
+        }
 
         // The root may have been merged with another
         // subset. Find the new root subset.
@@ -659,6 +570,12 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
     return cheapest;
   }
 
+  @Override public void checkCancel() {
+    if (cancelFlag.get()) {
+      throw new VolcanoTimeoutException();
+    }
+  }
+
   /** Informs {@link JaninoRelMetadataProvider} about the different kinds of
    * {@link RelNode} that we will be dealing with. It will reduce the number
    * of times that we need to re-generate the provider. */
@@ -761,75 +678,6 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
     }
   }
 
-  private void setInitialImportance() {
-    RelVisitor visitor =
-        new RelVisitor() {
-          int depth = 0;
-          final Set<RelSubset> visitedSubsets = new HashSet<>();
-
-          public void visit(
-              RelNode p,
-              int ordinal,
-              RelNode parent) {
-            if (p instanceof RelSubset) {
-              RelSubset subset = (RelSubset) p;
-
-              if (visitedSubsets.contains(subset)) {
-                return;
-              }
-
-              if (subset != root) {
-                Double importance = Math.pow(0.9, (double) depth);
-
-                ruleQueue.updateImportance(subset, importance);
-              }
-
-              visitedSubsets.add(subset);
-
-              depth++;
-              for (RelNode rel : subset.getRels()) {
-                visit(rel, -1, subset);
-              }
-              depth--;
-            } else {
-              super.visit(p, ordinal, parent);
-            }
-          }
-        };
-
-    visitor.go(root);
-  }
-
-  /**
-   * Finds RelSubsets in the plan that contain only rels of
-   * {@link Convention#NONE} and boosts their importance by 25%.
-   */
-  private void injectImportanceBoost() {
-    final Set<RelSubset> requireBoost = new HashSet<>();
-
-  SUBSET_LOOP:
-    for (RelSubset subset : ruleQueue.subsetImportances.keySet()) {
-      for (RelNode rel : subset.getRels()) {
-        if (rel.getConvention() != Convention.NONE) {
-          continue SUBSET_LOOP;
-        }
-      }
-
-      requireBoost.add(subset);
-    }
-
-    ruleQueue.boostImportance(requireBoost, 1.25);
-  }
-
-  /**
-   * Clear all importance boosts.
-   */
-  private void clearImportanceBoost() {
-    Collection<RelSubset> empty = Collections.emptySet();
-
-    ruleQueue.boostImportance(empty, 1.0);
-  }
-
   public RelSubset register(
       RelNode rel,
       RelNode equivRel) {
@@ -1129,8 +977,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
         pw.println(
             "\t" + subset + ", best="
             + ((subset.best == null) ? "null"
-                : ("rel#" + subset.best.getId())) + ", importance="
-                + ruleQueue.getImportance(subset));
+                : ("rel#" + subset.best.getId())));
         assert subset.set == set;
         for (int k = 0; k < j; k++) {
           assert !set.subsets.get(k).getTraitSet().equals(
@@ -1370,7 +1217,6 @@ public class VolcanoPlanner extends AbstractRelOptPlanner 
{
         mapDigestToRel.put(key, equivRel);
 
         RelSubset equivRelSubset = getSubset(equivRel);
-        ruleQueue.recompute(equivRelSubset, true);
 
         // Remove back-links from children.
         for (RelNode input : rel.getInputs()) {
@@ -1428,8 +1274,6 @@ public class VolcanoPlanner extends AbstractRelOptPlanner 
{
       assert equivRel.getClass() == rel.getClass();
       assert equivRel.getTraitSet().equals(rel.getTraitSet());
 
-      RelSubset equivRelSubset = getSubset(equivRel);
-      ruleQueue.recompute(equivRelSubset, true);
       return;
     }
 
@@ -1658,7 +1502,6 @@ public class VolcanoPlanner extends AbstractRelOptPlanner 
{
             "Register #{} {} (and merge sets, because it is a conversion)",
             rel.getId(), rel.getDigest());
         merge(set, childSet, true);
-        registerCount++;
 
         // During the mergers, the child set may have changed, and since
         // we're not registered yet, we won't have been informed. So
@@ -1705,7 +1548,6 @@ public class VolcanoPlanner extends AbstractRelOptPlanner 
{
     // Allow each rel to register its own rules.
     registerClass(rel);
 
-    registerCount++;
     final int subsetBeforeCount = set.subsets.size();
     RelSubset subset = addRelToSet(rel, set);
 
@@ -1720,27 +1562,11 @@ public class VolcanoPlanner extends 
AbstractRelOptPlanner {
       return subset;
     }
 
-    // Create back-links from its children, which makes children more
-    // important.
-    if (rel == this.root) {
-      ruleQueue.subsetImportances.put(
-          subset,
-          1.0); // todo: remove
-    }
     for (RelNode input : rel.getInputs()) {
       RelSubset childSubset = (RelSubset) input;
       childSubset.set.parents.add(rel);
-
-      // Child subset is more important now a new parent uses it.
-      ruleQueue.recompute(childSubset);
-    }
-    if (rel == this.root) {
-      ruleQueue.subsetImportances.remove(subset);
     }
 
-    // Make sure this rel's subset importance is updated
-    ruleQueue.recompute(subset, true);
-
     // Queue up all rules triggered by this relexp's creation.
     fireRules(rel, true);
 
@@ -1781,7 +1607,6 @@ public class VolcanoPlanner extends AbstractRelOptPlanner 
{
       LOGGER.trace("Register #{} {}, and merge sets", subset.getId(), subset);
       boolean enableSwap = !set.getChildSets(this).contains(subset.set);
       merge(subset.set, set, enableSwap);
-      registerCount++;
     }
     return subset;
   }
diff --git 
a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleMatch.java 
b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleMatch.java
index 9c3e9d3..beda1c5 100644
--- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleMatch.java
+++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleMatch.java
@@ -17,8 +17,6 @@
 package org.apache.calcite.plan.volcano;
 
 import org.apache.calcite.plan.RelOptRuleOperand;
-import org.apache.calcite.plan.RelTrait;
-import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.util.Litmus;
 
@@ -32,10 +30,7 @@ import java.util.Map;
 class VolcanoRuleMatch extends VolcanoRuleCall {
   //~ Instance fields --------------------------------------------------------
 
-  private final RelSet targetSet;
-  private RelSubset targetSubset;
   private String digest;
-  private double cachedImportance = Double.NaN;
 
   //~ Constructors -----------------------------------------------------------
 
@@ -52,10 +47,6 @@ class VolcanoRuleMatch extends VolcanoRuleCall {
     super(volcanoPlanner, operand0, rels.clone(), nodeInputs);
     assert allNotNull(rels, Litmus.THROW);
 
-    // Try to deduce which subset the result will belong to. Assume --
-    // for now -- that the set is the same as the root relexp.
-    targetSet = volcanoPlanner.getSet(rels[0]);
-    assert targetSet != null : rels[0].toString() + " isn't in a set";
     digest = computeDigest();
   }
 
@@ -66,72 +57,6 @@ class VolcanoRuleMatch extends VolcanoRuleCall {
   }
 
   /**
-   * Clears the cached importance value of this rule match. The importance
-   * will be re-calculated next time {@link #getImportance()} is called.
-   */
-  void clearCachedImportance() {
-    cachedImportance = Double.NaN;
-  }
-
-  /**
-   * Returns the importance of this rule.
-   *
-   * <p>Calls {@link #computeImportance()} the first time, thereafter uses a
-   * cached value until {@link #clearCachedImportance()} is called.
-   *
-   * @return importance of this rule; a value between 0 and 1
-   */
-  double getImportance() {
-    if (Double.isNaN(cachedImportance)) {
-      cachedImportance = computeImportance();
-    }
-
-    return cachedImportance;
-  }
-
-  /**
-   * Computes the importance of this rule match.
-   *
-   * @return importance of this rule match
-   */
-  double computeImportance() {
-    assert rels[0] != null;
-    RelSubset subset = volcanoPlanner.getSubset(rels[0]);
-    double importance = 0;
-    if (subset != null) {
-      importance = volcanoPlanner.ruleQueue.getImportance(subset);
-    }
-    final RelSubset targetSubset = guessSubset();
-    if ((targetSubset != null) && (targetSubset != subset)) {
-      // If this rule will generate a member of an equivalence class
-      // which is more important, use that importance.
-      final double targetImportance =
-          volcanoPlanner.ruleQueue.getImportance(targetSubset);
-      if (targetImportance > importance) {
-        importance = targetImportance;
-
-        // If the equivalence class is cheaper than the target, bump up
-        // the importance of the rule. A converter is an easy way to
-        // make the plan cheaper, so we'd hate to miss this opportunity.
-        //
-        // REVIEW: jhyde, 2007/12/21: This rule seems to make sense, but
-        // is disabled until it has been proven.
-        //
-        // CHECKSTYLE: IGNORE 3
-        if ((subset != null)
-            && subset.bestCost.isLt(targetSubset.bestCost)
-            && false) {
-          importance *=
-              targetSubset.bestCost.divideBy(subset.bestCost);
-          importance = Math.min(importance, 0.99);
-        }
-      }
-    }
-
-    return importance;
-  }
-
-  /**
    * Computes a string describing this rule match. Two rule matches are
    * equivalent if and only if their digests are the same.
    *
@@ -158,32 +83,6 @@ class VolcanoRuleMatch extends VolcanoRuleCall {
     digest = computeDigest();
   }
 
-  /**
-   * Returns a guess as to which subset (that is equivalence class of
-   * relational expressions combined with a set of physical traits) the result
-   * of this rule will belong to.
-   *
-   * @return expected subset, or null if we cannot guess
-   */
-  private RelSubset guessSubset() {
-    if (targetSubset != null) {
-      return targetSubset;
-    }
-    final RelTrait targetTrait = getRule().getOutTrait();
-    if ((targetSet != null) && (targetTrait != null)) {
-      final RelTraitSet targetTraitSet =
-          rels[0].getTraitSet().replace(targetTrait);
-
-      // Find the subset in the target set which matches the expected
-      // set of traits. It may not exist yet.
-      targetSubset = targetSet.getSubset(targetTraitSet);
-      return targetSubset;
-    }
-
-    // The target subset doesn't exist yet.
-    return null;
-  }
-
   /** Returns whether all elements of a given array are not-null;
    * fails if any are null. */
   private static <E> boolean allNotNull(E[] es, Litmus litmus) {
diff --git 
a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoTimeoutException.java
 
b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoTimeoutException.java
new file mode 100644
index 0000000..44e1bdf
--- /dev/null
+++ 
b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoTimeoutException.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.plan.volcano;
+
+/**
+ * Indicates that planning timed out. This is not an error; you can
+ * retry the operation.
+ */
+public class VolcanoTimeoutException extends RuntimeException {
+  public VolcanoTimeoutException() {
+    super("Volcano timeout", null);
+  }
+}
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateRemoveRule.java 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateRemoveRule.java
index b8bc35d..22af6da 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateRemoveRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateRemoveRule.java
@@ -123,6 +123,7 @@ public class AggregateRemoveRule extends RelOptRule {
       // aggregate functions, add a project for the same effect.
       relBuilder.project(relBuilder.fields(aggregate.getGroupSet()));
     }
+    call.getPlanner().setImportance(aggregate, 0d);
     call.transformTo(relBuilder.build());
   }
 }
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
index dbe2bf5..6876b66 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
@@ -27,6 +27,8 @@ import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.core.RelFactories;
+import org.apache.calcite.rel.logical.LogicalFilter;
+import org.apache.calcite.rel.logical.LogicalProject;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexOver;
 import org.apache.calcite.rex.RexUtil;
@@ -45,17 +47,13 @@ public class FilterProjectTransposeRule extends RelOptRule {
   /** The default instance of
    * {@link org.apache.calcite.rel.rules.FilterProjectTransposeRule}.
    *
-   * <p>It matches any kind of {@link org.apache.calcite.rel.core.Join} or
-   * {@link org.apache.calcite.rel.core.Filter}, and generates the same kind of
-   * Join and Filter.
-   *
    * <p>It does not allow a Filter to be pushed past the Project if
    * {@link RexUtil#containsCorrelation there is a correlation condition})
    * anywhere in the Filter, since in some cases it can prevent a
    * {@link org.apache.calcite.rel.core.Correlate} from being de-correlated.
    */
   public static final FilterProjectTransposeRule INSTANCE =
-      new FilterProjectTransposeRule(Filter.class, Project.class, true, true,
+      new FilterProjectTransposeRule(LogicalFilter.class, 
LogicalProject.class, true, true,
           RelFactories.LOGICAL_BUILDER);
 
   private final boolean copyFilter;
diff --git 
a/core/src/test/java/org/apache/calcite/plan/volcano/VolcanoPlannerTest.java 
b/core/src/test/java/org/apache/calcite/plan/volcano/VolcanoPlannerTest.java
index a72ad79..df57623 100644
--- a/core/src/test/java/org/apache/calcite/plan/volcano/VolcanoPlannerTest.java
+++ b/core/src/test/java/org/apache/calcite/plan/volcano/VolcanoPlannerTest.java
@@ -288,7 +288,6 @@ public class VolcanoPlannerTest {
 
   private void removeTrivialProject(boolean useRule) {
     VolcanoPlanner planner = new VolcanoPlanner();
-    planner.ambitious = true;
 
     planner.addRelTraitDef(ConventionTraitDef.INSTANCE);
 
@@ -355,7 +354,6 @@ public class VolcanoPlannerTest {
   @Disabled // broken, because ReformedSingleRule matches child traits strictly
   @Test public void testRemoveSingleReformed() {
     VolcanoPlanner planner = new VolcanoPlanner();
-    planner.ambitious = true;
     planner.addRelTraitDef(ConventionTraitDef.INSTANCE);
 
     planner.addRule(new PhysLeafRule());
@@ -390,7 +388,6 @@ public class VolcanoPlannerTest {
    */
   @Test public void testRemoveSingleGood() {
     VolcanoPlanner planner = new VolcanoPlanner();
-    planner.ambitious = true;
     planner.addRelTraitDef(ConventionTraitDef.INSTANCE);
 
     planner.addRule(new PhysLeafRule());
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java 
b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
index b98e1e3..4f421fd 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
@@ -160,20 +160,20 @@ public class JdbcAdapterTest {
             + "from scott.emp e inner join scott.dept d\n"
             + "on e.deptno = d.deptno")
         .explainContains("PLAN=JdbcToEnumerableConverter\n"
-            + "  JdbcProject(EMPNO=[$2], ENAME=[$3], DEPTNO=[$4], 
DNAME=[$1])\n"
-            + "    JdbcJoin(condition=[=($4, $0)], joinType=[inner])\n"
-            + "      JdbcProject(DEPTNO=[$0], DNAME=[$1])\n"
-            + "        JdbcTableScan(table=[[SCOTT, DEPT]])\n"
+            + "  JdbcProject(EMPNO=[$0], ENAME=[$1], DEPTNO=[$2], 
DNAME=[$4])\n"
+            + "    JdbcJoin(condition=[=($2, $3)], joinType=[inner])\n"
             + "      JdbcProject(EMPNO=[$0], ENAME=[$1], DEPTNO=[$7])\n"
-            + "        JdbcTableScan(table=[[SCOTT, EMP]])")
+            + "        JdbcTableScan(table=[[SCOTT, EMP]])\n"
+            + "      JdbcProject(DEPTNO=[$0], DNAME=[$1])\n"
+            + "        JdbcTableScan(table=[[SCOTT, DEPT]])")
         .runs()
         .enable(CalciteAssert.DB == CalciteAssert.DatabaseInstance.HSQLDB)
-        .planHasSql("SELECT \"t0\".\"EMPNO\", \"t0\".\"ENAME\", "
-            + "\"t0\".\"DEPTNO\", \"t\".\"DNAME\"\n"
-            + "FROM (SELECT \"DEPTNO\", \"DNAME\"\n"
-            + "FROM \"SCOTT\".\"DEPT\") AS \"t\"\n"
-            + "INNER JOIN (SELECT \"EMPNO\", \"ENAME\", \"DEPTNO\"\n"
-            + "FROM \"SCOTT\".\"EMP\") AS \"t0\" "
+        .planHasSql("SELECT \"t\".\"EMPNO\", \"t\".\"ENAME\", "
+            + "\"t\".\"DEPTNO\", \"t0\".\"DNAME\"\n"
+            + "FROM (SELECT \"EMPNO\", \"ENAME\", \"DEPTNO\"\n"
+            + "FROM \"SCOTT\".\"EMP\") AS \"t\"\n"
+            + "INNER JOIN (SELECT \"DEPTNO\", \"DNAME\"\n"
+            + "FROM \"SCOTT\".\"DEPT\") AS \"t0\" "
             + "ON \"t\".\"DEPTNO\" = \"t0\".\"DEPTNO\"");
   }
 
@@ -202,15 +202,14 @@ public class JdbcAdapterTest {
         + "group by deptno, job\n"
         + "order by 1, 2";
     final String explain = "PLAN=JdbcToEnumerableConverter\n"
-        + "  JdbcProject(DEPTNO=[$1], JOB=[$0], EXPR$2=[$2])\n"
-        + "    JdbcSort(sort0=[$1], sort1=[$0], dir0=[ASC], dir1=[ASC])\n"
+        + "  JdbcSort(sort0=[$0], sort1=[$1], dir0=[ASC], dir1=[ASC])\n"
+        + "    JdbcProject(DEPTNO=[$1], JOB=[$0], EXPR$2=[$2])\n"
         + "      JdbcAggregate(group=[{2, 7}], EXPR$2=[SUM($5)])\n"
         + "        JdbcTableScan(table=[[SCOTT, EMP]])";
-    final String sqlHsqldb = "SELECT \"DEPTNO\", \"JOB\", \"EXPR$2\"\n"
-        + "FROM (SELECT \"JOB\", \"DEPTNO\", SUM(\"SAL\") AS \"EXPR$2\"\n"
+    final String sqlHsqldb = "SELECT \"DEPTNO\", \"JOB\", SUM(\"SAL\")\n"
         + "FROM \"SCOTT\".\"EMP\"\n"
         + "GROUP BY \"JOB\", \"DEPTNO\"\n"
-        + "ORDER BY \"DEPTNO\" NULLS LAST, \"JOB\" NULLS LAST) AS \"t0\"";
+        + "ORDER BY \"DEPTNO\" NULLS LAST, \"JOB\" NULLS LAST";
     CalciteAssert.model(JdbcTest.SCOTT_MODEL)
         .query(sql)
         .explainContains(explain)
@@ -228,19 +227,19 @@ public class JdbcAdapterTest {
             + "from scott.emp e inner join scott.salgrade s\n"
             + "on e.sal > s.losal and e.sal < s.hisal")
         .explainContains("PLAN=JdbcToEnumerableConverter\n"
-            + "  JdbcProject(EMPNO=[$3], ENAME=[$4], GRADE=[$0])\n"
-            + "    JdbcJoin(condition=[AND(>($5, $1), <($5, $2))], 
joinType=[inner])\n"
-            + "      JdbcTableScan(table=[[SCOTT, SALGRADE]])\n"
+            + "  JdbcProject(EMPNO=[$0], ENAME=[$1], GRADE=[$3])\n"
+            + "    JdbcJoin(condition=[AND(>($2, $4), <($2, $5))], 
joinType=[inner])\n"
             + "      JdbcProject(EMPNO=[$0], ENAME=[$1], SAL=[$5])\n"
-            + "        JdbcTableScan(table=[[SCOTT, EMP]])")
+            + "        JdbcTableScan(table=[[SCOTT, EMP]])\n"
+            + "      JdbcTableScan(table=[[SCOTT, SALGRADE]])")
         .runs()
         .enable(CalciteAssert.DB == CalciteAssert.DatabaseInstance.HSQLDB)
-        .planHasSql("SELECT \"t\".\"EMPNO\", \"t\".\"ENAME\", "
-            + "\"SALGRADE\".\"GRADE\"\nFROM \"SCOTT\".\"SALGRADE\"\n"
-            + "INNER JOIN (SELECT \"EMPNO\", \"ENAME\", \"SAL\"\n"
-            + "FROM \"SCOTT\".\"EMP\") AS \"t\" "
-            + "ON \"SALGRADE\".\"LOSAL\" < \"t\".\"SAL\" "
-            + "AND \"SALGRADE\".\"HISAL\" > \"t\".\"SAL\"");
+        .planHasSql("SELECT \"t\".\"EMPNO\", \"t\".\"ENAME\", 
\"SALGRADE\".\"GRADE\"\n"
+            + "FROM (SELECT \"EMPNO\", \"ENAME\", \"SAL\"\n"
+            + "FROM \"SCOTT\".\"EMP\") AS \"t\"\n"
+            + "INNER JOIN \"SCOTT\".\"SALGRADE\" "
+            + "ON \"t\".\"SAL\" > \"SALGRADE\".\"LOSAL\" "
+            + "AND \"t\".\"SAL\" < \"SALGRADE\".\"HISAL\"");
   }
 
   @Test public void testNonEquiJoinReverseConditionPlan() {
@@ -249,18 +248,18 @@ public class JdbcAdapterTest {
             + "from scott.emp e inner join scott.salgrade s\n"
             + "on s.losal <= e.sal and s.hisal >= e.sal")
         .explainContains("PLAN=JdbcToEnumerableConverter\n"
-            + "  JdbcProject(EMPNO=[$3], ENAME=[$4], GRADE=[$0])\n"
-            + "    JdbcJoin(condition=[AND(<=($1, $5), >=($2, $5))], 
joinType=[inner])\n"
-            + "      JdbcTableScan(table=[[SCOTT, SALGRADE]])\n"
+            + "  JdbcProject(EMPNO=[$0], ENAME=[$1], GRADE=[$3])\n"
+            + "    JdbcJoin(condition=[AND(<=($4, $2), >=($5, $2))], 
joinType=[inner])\n"
             + "      JdbcProject(EMPNO=[$0], ENAME=[$1], SAL=[$5])\n"
-            + "        JdbcTableScan(table=[[SCOTT, EMP]])")
+            + "        JdbcTableScan(table=[[SCOTT, EMP]])\n"
+            + "      JdbcTableScan(table=[[SCOTT, SALGRADE]])")
         .runs()
         .enable(CalciteAssert.DB == CalciteAssert.DatabaseInstance.HSQLDB)
-        .planHasSql("SELECT \"t\".\"EMPNO\", \"t\".\"ENAME\", "
-            + "\"SALGRADE\".\"GRADE\"\nFROM \"SCOTT\".\"SALGRADE\"\n"
-            + "INNER JOIN (SELECT \"EMPNO\", \"ENAME\", \"SAL\"\n"
-            + "FROM \"SCOTT\".\"EMP\") AS \"t\" "
-            + "ON \"SALGRADE\".\"LOSAL\" <= \"t\".\"SAL\" AND 
\"SALGRADE\".\"HISAL\" >= \"t\".\"SAL\"");
+        .planHasSql("SELECT \"t\".\"EMPNO\", \"t\".\"ENAME\", 
\"SALGRADE\".\"GRADE\"\n"
+            + "FROM (SELECT \"EMPNO\", \"ENAME\", \"SAL\"\n"
+            + "FROM \"SCOTT\".\"EMP\") AS \"t\"\n"
+            + "INNER JOIN \"SCOTT\".\"SALGRADE\" ON \"t\".\"SAL\" >= 
\"SALGRADE\".\"LOSAL\" "
+            + "AND \"t\".\"SAL\" <= \"SALGRADE\".\"HISAL\"");
   }
 
   @Test public void testMixedJoinPlan() {
@@ -269,21 +268,21 @@ public class JdbcAdapterTest {
             + "from scott.emp e inner join scott.emp m on\n"
             + "e.mgr = m.empno and e.sal > m.sal")
         .explainContains("PLAN=JdbcToEnumerableConverter\n"
-            + "  JdbcProject(EMPNO=[$2], ENAME=[$3], EMPNO0=[$2], 
ENAME0=[$3])\n"
-            + "    JdbcJoin(condition=[AND(=($4, $0), >($5, $1))], 
joinType=[inner])\n"
-            + "      JdbcProject(EMPNO=[$0], SAL=[$5])\n"
-            + "        JdbcTableScan(table=[[SCOTT, EMP]])\n"
+            + "  JdbcProject(EMPNO=[$0], ENAME=[$1], EMPNO0=[$0], 
ENAME0=[$1])\n"
+            + "    JdbcJoin(condition=[AND(=($2, $4), >($3, $5))], 
joinType=[inner])\n"
             + "      JdbcProject(EMPNO=[$0], ENAME=[$1], MGR=[$3], SAL=[$5])\n"
+            + "        JdbcTableScan(table=[[SCOTT, EMP]])\n"
+            + "      JdbcProject(EMPNO=[$0], SAL=[$5])\n"
             + "        JdbcTableScan(table=[[SCOTT, EMP]])")
         .runs()
         .enable(CalciteAssert.DB == CalciteAssert.DatabaseInstance.HSQLDB)
-        .planHasSql("SELECT \"t0\".\"EMPNO\", \"t0\".\"ENAME\", "
-            + "\"t0\".\"EMPNO\" AS \"EMPNO0\", \"t0\".\"ENAME\" AS 
\"ENAME0\"\n"
-            + "FROM (SELECT \"EMPNO\", \"SAL\"\n"
+        .planHasSql("SELECT \"t\".\"EMPNO\", \"t\".\"ENAME\", "
+            + "\"t\".\"EMPNO\" AS \"EMPNO0\", \"t\".\"ENAME\" AS \"ENAME0\"\n"
+            + "FROM (SELECT \"EMPNO\", \"ENAME\", \"MGR\", \"SAL\"\n"
             + "FROM \"SCOTT\".\"EMP\") AS \"t\"\n"
-            + "INNER JOIN (SELECT \"EMPNO\", \"ENAME\", \"MGR\", \"SAL\"\n"
+            + "INNER JOIN (SELECT \"EMPNO\", \"SAL\"\n"
             + "FROM \"SCOTT\".\"EMP\") AS \"t0\" "
-            + "ON \"t\".\"EMPNO\" = \"t0\".\"MGR\" AND \"t\".\"SAL\" < 
\"t0\".\"SAL\"");
+            + "ON \"t\".\"MGR\" = \"t0\".\"EMPNO\" AND \"t\".\"SAL\" > 
\"t0\".\"SAL\"");
   }
 
   @Test public void testMixedJoinWithOrPlan() {
@@ -292,22 +291,22 @@ public class JdbcAdapterTest {
             + "from scott.emp e inner join scott.emp m on\n"
             + "e.mgr = m.empno and (e.sal > m.sal or m.hiredate > e.hiredate)")
         .explainContains("PLAN=JdbcToEnumerableConverter\n"
-            + "  JdbcProject(EMPNO=[$3], ENAME=[$4], EMPNO0=[$3], 
ENAME0=[$4])\n"
-            + "    JdbcJoin(condition=[AND(=($5, $0), OR(>($7, $2), >($1, 
$6)))], joinType=[inner])\n"
-            + "      JdbcProject(EMPNO=[$0], HIREDATE=[$4], SAL=[$5])\n"
-            + "        JdbcTableScan(table=[[SCOTT, EMP]])\n"
+            + "  JdbcProject(EMPNO=[$0], ENAME=[$1], EMPNO0=[$0], 
ENAME0=[$1])\n"
+            + "    JdbcJoin(condition=[AND(=($2, $5), OR(>($4, $7), >($6, 
$3)))], joinType=[inner])\n"
             + "      JdbcProject(EMPNO=[$0], ENAME=[$1], MGR=[$3], 
HIREDATE=[$4], SAL=[$5])\n"
+            + "        JdbcTableScan(table=[[SCOTT, EMP]])\n"
+            + "      JdbcProject(EMPNO=[$0], HIREDATE=[$4], SAL=[$5])\n"
             + "        JdbcTableScan(table=[[SCOTT, EMP]])")
         .runs()
         .enable(CalciteAssert.DB == CalciteAssert.DatabaseInstance.HSQLDB)
-        .planHasSql("SELECT \"t0\".\"EMPNO\", \"t0\".\"ENAME\", "
-            + "\"t0\".\"EMPNO\" AS \"EMPNO0\", \"t0\".\"ENAME\" AS 
\"ENAME0\"\n"
-            + "FROM (SELECT \"EMPNO\", \"HIREDATE\", \"SAL\"\n"
+        .planHasSql("SELECT \"t\".\"EMPNO\", \"t\".\"ENAME\", "
+            + "\"t\".\"EMPNO\" AS \"EMPNO0\", \"t\".\"ENAME\" AS \"ENAME0\"\n"
+            + "FROM (SELECT \"EMPNO\", \"ENAME\", \"MGR\", \"HIREDATE\", 
\"SAL\"\n"
             + "FROM \"SCOTT\".\"EMP\") AS \"t\"\n"
-            + "INNER JOIN (SELECT \"EMPNO\", \"ENAME\", \"MGR\", \"HIREDATE\", 
\"SAL\"\n"
+            + "INNER JOIN (SELECT \"EMPNO\", \"HIREDATE\", \"SAL\"\n"
             + "FROM \"SCOTT\".\"EMP\") AS \"t0\" "
-            + "ON \"t\".\"EMPNO\" = \"t0\".\"MGR\" "
-            + "AND (\"t\".\"SAL\" < \"t0\".\"SAL\" OR \"t\".\"HIREDATE\" > 
\"t0\".\"HIREDATE\")");
+            + "ON \"t\".\"MGR\" = \"t0\".\"EMPNO\" "
+            + "AND (\"t\".\"SAL\" > \"t0\".\"SAL\" OR \"t\".\"HIREDATE\" < 
\"t0\".\"HIREDATE\")");
   }
 
   @Test public void testJoin3TablesPlan() {
@@ -318,24 +317,25 @@ public class JdbcAdapterTest {
             + "inner join scott.salgrade s\n"
             + "on e.sal > s.losal and e.sal < s.hisal")
         .explainContains("PLAN=JdbcToEnumerableConverter\n"
-            + "  JdbcProject(EMPNO=[$3], ENAME=[$4], DNAME=[$8], GRADE=[$0])\n"
-            + "    JdbcJoin(condition=[AND(>($5, $1), <($5, $2))], 
joinType=[inner])\n"
-            + "      JdbcTableScan(table=[[SCOTT, SALGRADE]])\n"
+            + "  JdbcProject(EMPNO=[$0], ENAME=[$1], DNAME=[$5], GRADE=[$6])\n"
+            + "    JdbcJoin(condition=[AND(>($2, $7), <($2, $8))], 
joinType=[inner])\n"
             + "      JdbcJoin(condition=[=($3, $4)], joinType=[inner])\n"
             + "        JdbcProject(EMPNO=[$0], ENAME=[$1], SAL=[$5], 
DEPTNO=[$7])\n"
             + "          JdbcTableScan(table=[[SCOTT, EMP]])\n"
             + "        JdbcProject(DEPTNO=[$0], DNAME=[$1])\n"
-            + "          JdbcTableScan(table=[[SCOTT, DEPT]])")
+            + "          JdbcTableScan(table=[[SCOTT, DEPT]])\n"
+            + "      JdbcTableScan(table=[[SCOTT, SALGRADE]])\n")
         .runs()
         .enable(CalciteAssert.DB == CalciteAssert.DatabaseInstance.HSQLDB)
         .planHasSql("SELECT \"t\".\"EMPNO\", \"t\".\"ENAME\", "
             + "\"t0\".\"DNAME\", \"SALGRADE\".\"GRADE\"\n"
-            + "FROM \"SCOTT\".\"SALGRADE\"\n"
-            + "INNER JOIN ((SELECT \"EMPNO\", \"ENAME\", \"SAL\", \"DEPTNO\"\n"
-            + "FROM \"SCOTT\".\"EMP\") AS \"t\" "
+            + "FROM (SELECT \"EMPNO\", \"ENAME\", \"SAL\", \"DEPTNO\"\n"
+            + "FROM \"SCOTT\".\"EMP\") AS \"t\"\n"
             + "INNER JOIN (SELECT \"DEPTNO\", \"DNAME\"\n"
-            + "FROM \"SCOTT\".\"DEPT\") AS \"t0\" ON \"t\".\"DEPTNO\" = 
\"t0\".\"DEPTNO\")"
-            + " ON \"SALGRADE\".\"LOSAL\" < \"t\".\"SAL\" AND 
\"SALGRADE\".\"HISAL\" > \"t\".\"SAL\"");
+            + "FROM \"SCOTT\".\"DEPT\") AS \"t0\" ON \"t\".\"DEPTNO\" = 
\"t0\".\"DEPTNO\"\n"
+            + "INNER JOIN \"SCOTT\".\"SALGRADE\" "
+            + "ON \"t\".\"SAL\" > \"SALGRADE\".\"LOSAL\" "
+            + "AND \"t\".\"SAL\" < \"SALGRADE\".\"HISAL\"");
   }
 
   @Test public void testCrossJoinWithJoinKeyPlan() {
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java 
b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 43eefdc..5156c9e 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -2720,12 +2720,12 @@ public class JdbcTest {
             + "from \"hr\".\"emps\"\n"
             + " join \"hr\".\"depts\" using (\"deptno\")")
         .explainContains(""
-            + "EnumerableCalc(expr#0..3=[{inputs}], empid=[$t2], deptno=[$t0], 
name=[$t1])\n"
-            + "  EnumerableHashJoin(condition=[=($0, $3)], joinType=[inner])\n"
-            + "    EnumerableCalc(expr#0..3=[{inputs}], proj#0..1=[{exprs}])\n"
-            + "      EnumerableTableScan(table=[[hr, depts]])\n"
+            + "EnumerableCalc(expr#0..3=[{inputs}], empid=[$t0], deptno=[$t2], 
name=[$t3])\n"
+            + "  EnumerableHashJoin(condition=[=($1, $2)], joinType=[inner])\n"
             + "    EnumerableCalc(expr#0..4=[{inputs}], proj#0..1=[{exprs}])\n"
-            + "      EnumerableTableScan(table=[[hr, emps]])")
+            + "      EnumerableTableScan(table=[[hr, emps]])\n"
+            + "    EnumerableCalc(expr#0..3=[{inputs}], proj#0..1=[{exprs}])\n"
+            + "      EnumerableTableScan(table=[[hr, depts]])")
         .returns("empid=100; deptno=10; name=Sales\n"
             + "empid=150; deptno=10; name=Sales\n"
             + "empid=110; deptno=10; name=Sales\n");
diff --git a/core/src/test/java/org/apache/calcite/test/LatticeTest.java 
b/core/src/test/java/org/apache/calcite/test/LatticeTest.java
index 052986d..2d814ac 100644
--- a/core/src/test/java/org/apache/calcite/test/LatticeTest.java
+++ b/core/src/test/java/org/apache/calcite/test/LatticeTest.java
@@ -928,11 +928,11 @@ public class LatticeTest {
         + "join \"time_by_day\" using (\"time_id\")\n";
     final String explain = "PLAN=JdbcToEnumerableConverter\n"
         + "  JdbcAggregate(group=[{}], EXPR$0=[COUNT()])\n"
-        + "    JdbcJoin(condition=[=($1, $0)], joinType=[inner])\n"
-        + "      JdbcProject(time_id=[$0])\n"
-        + "        JdbcTableScan(table=[[foodmart, time_by_day]])\n"
+        + "    JdbcJoin(condition=[=($0, $1)], joinType=[inner])\n"
         + "      JdbcProject(time_id=[$1])\n"
-        + "        JdbcTableScan(table=[[foodmart, sales_fact_1997]])\n";
+        + "        JdbcTableScan(table=[[foodmart, sales_fact_1997]])\n"
+        + "      JdbcProject(time_id=[$0])\n"
+        + "        JdbcTableScan(table=[[foodmart, time_by_day]])\n";
     CalciteAssert.model(model)
         .withDefaultSchema("foodmart")
         .query(sql)
diff --git 
a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java 
b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
index e28959c..3cc1312 100644
--- a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
+++ b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
@@ -91,10 +91,6 @@ public class MaterializationTest {
       CalciteAssert.checkResultContains(
           "EnumerableTableScan(table=[[hr, m0]])");
 
-  private static final Consumer<ResultSet> CONTAINS_LOCATIONS =
-      CalciteAssert.checkResultContains(
-          "EnumerableTableScan(table=[[hr, locations]])");
-
   private static final Ordering<Iterable<String>> 
CASE_INSENSITIVE_LIST_COMPARATOR =
       Ordering.from(String.CASE_INSENSITIVE_ORDER).lexicographical();
 
@@ -1587,7 +1583,7 @@ public class MaterializationTest {
         + "where \"deptno\" > 10\n"
         + "group by \"deptno\"";
     final String expected = "EnumerableAggregate(group=[{1}])\n"
-        + "  EnumerableCalc(expr#0..1=[{inputs}], expr#2=[10], expr#3=[<($t2, 
$t1)], "
+        + "  EnumerableCalc(expr#0..1=[{inputs}], expr#2=[10], expr#3=[>($t1, 
$t2)], "
         + "proj#0..1=[{exprs}], $condition=[$t3])\n"
         + "    EnumerableTableScan(table=[[hr, m0]])";
     sql(materialize, query).withResultContains(expected).ok();
@@ -1659,7 +1655,7 @@ public class MaterializationTest {
             + "from \"emps\" where \"deptno\" > 10 group by \"deptno\"")
         .withResultContains(
             "EnumerableAggregate(group=[{1}], S=[$SUM0($3)])\n"
-                + "  EnumerableCalc(expr#0..3=[{inputs}], expr#4=[10], 
expr#5=[<($t4, $t1)], "
+                + "  EnumerableCalc(expr#0..3=[{inputs}], expr#4=[10], 
expr#5=[>($t1, $t4)], "
                 + "proj#0..3=[{exprs}], $condition=[$t5])\n"
                 + "    EnumerableTableScan(table=[[hr, m0]])")
         .ok();
@@ -1928,7 +1924,7 @@ public class MaterializationTest {
         + "join \"emps\" using (\"deptno\") where \"emps\".\"empid\" > 15\n"
         + "group by \"depts\".\"deptno\"";
     final String expected = "EnumerableAggregate(group=[{0}])\n"
-        + "  EnumerableCalc(expr#0..1=[{inputs}], expr#2=[15], expr#3=[<($t2, 
$t1)], "
+        + "  EnumerableCalc(expr#0..1=[{inputs}], expr#2=[15], expr#3=[>($t1, 
$t2)], "
         + "proj#0..1=[{exprs}], $condition=[$t3])\n"
         + "    EnumerableTableScan(table=[[hr, m0]])";
     sql(materialize, query).withResultContains(expected).ok();
@@ -2278,7 +2274,7 @@ public class MaterializationTest {
         "select \"empid\" \"deptno\" from \"emps\"\n"
             + "join \"depts\" using (\"deptno\") where \"empid\" = 1")
         .withResultContains(
-            "EnumerableCalc(expr#0=[{inputs}], expr#1=[CAST($t0):INTEGER NOT 
NULL], expr#2=[1], "
+            "EnumerableCalc(expr#0=[{inputs}], expr#1=[1], 
expr#2=[CAST($t0):INTEGER NOT NULL], "
                 + "expr#3=[=($t1, $t2)], deptno=[$t0], $condition=[$t3])\n"
                 + "  EnumerableTableScan(table=[[hr, m0]])")
         .ok();
@@ -2291,7 +2287,7 @@ public class MaterializationTest {
             + "join \"depts\" using (\"deptno\") where \"empid\" > 1")
         .withResultContains(
             "EnumerableCalc(expr#0=[{inputs}], expr#1=[CAST($t0):JavaType(int) 
NOT NULL], "
-                + "expr#2=[1], expr#3=[>($t1, $t2)], EXPR$0=[$t1], 
$condition=[$t3])\n"
+                + "expr#2=[1], expr#3=[<($t2, $t1)], EXPR$0=[$t1], 
$condition=[$t3])\n"
                 + "  EnumerableTableScan(table=[[hr, m0]])")
         .ok();
   }
@@ -2303,7 +2299,7 @@ public class MaterializationTest {
             + "join \"depts\" using (\"deptno\") where \"empid\" = 1")
         .withResultContains(
             "EnumerableCalc(expr#0=[{inputs}], expr#1=[CAST($t0):JavaType(int) 
NOT NULL], "
-                + "expr#2=[CAST($t1):INTEGER NOT NULL], expr#3=[1], 
expr#4=[=($t2, $t3)], "
+                + "expr#2=[1], expr#3=[CAST($t1):INTEGER NOT NULL], 
expr#4=[=($t2, $t3)], "
                 + "EXPR$0=[$t1], $condition=[$t4])\n"
                 + "  EnumerableTableScan(table=[[hr, m0]])")
         .ok();
@@ -2586,7 +2582,6 @@ public class MaterializationTest {
               })
           .query(q)
           .enableMaterializations(true)
-          .explainMatches("", CONTAINS_LOCATIONS)
           .sameResultWithMaterializationsDisabled();
     }
   }
diff --git a/core/src/test/java/org/apache/calcite/test/ScannableTableTest.java 
b/core/src/test/java/org/apache/calcite/test/ScannableTableTest.java
index 2739cb9..528b803 100644
--- a/core/src/test/java/org/apache/calcite/test/ScannableTableTest.java
+++ b/core/src/test/java/org/apache/calcite/test/ScannableTableTest.java
@@ -268,10 +268,10 @@ public class ScannableTableTest {
         + "group by \"k\"";
     final Table table = new BeatlesProjectableFilterableTable(buf, false);
     final String explain = "PLAN="
-        + "EnumerableAggregate(group=[{1}], C=[COUNT()])\n"
+        + "EnumerableAggregate(group=[{0}], C=[COUNT()])\n"
         + "  EnumerableAggregate(group=[{0, 1}])\n"
         + "    EnumerableInterpreter\n"
-        + "      BindableTableScan(table=[[s, beatles]], filters=[[=($2, 
1940)]], projects=[[0, 2]])";
+        + "      BindableTableScan(table=[[s, beatles]], filters=[[=($2, 
1940)]], projects=[[2, 0]])";
     CalciteAssert.that()
         .with(newSchema("s", Pair.of("beatles", table)))
         .query(sql)
diff --git 
a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCorrelateTest.java
 
b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCorrelateTest.java
index baff85e..9b7ccb9 100644
--- 
a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCorrelateTest.java
+++ 
b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCorrelateTest.java
@@ -96,10 +96,10 @@ public class EnumerableCorrelateTest {
         })
         .explainContains(""
             + "EnumerableCalc(expr#0..3=[{inputs}], empid=[$t1], name=[$t3])\n"
-            + "  EnumerableCorrelate(correlation=[$cor2], joinType=[inner], 
requiredColumns=[{0}])\n"
+            + "  EnumerableCorrelate(correlation=[$cor1], joinType=[inner], 
requiredColumns=[{0}])\n"
             + "    EnumerableAggregate(group=[{0}])\n"
             + "      EnumerableTableScan(table=[[s, depts]])\n"
-            + "    EnumerableCalc(expr#0..4=[{inputs}], expr#5=[$cor2], 
expr#6=[$t5.deptno], expr#7=[=($t1, $t6)], proj#0..2=[{exprs}], 
$condition=[$t7])\n"
+            + "    EnumerableCalc(expr#0..4=[{inputs}], expr#5=[$cor1], 
expr#6=[$t5.deptno], expr#7=[=($t1, $t6)], proj#0..2=[{exprs}], 
$condition=[$t7])\n"
             + "      EnumerableTableScan(table=[[s, emps]])")
         .returnsUnordered(
             "empid=100; name=Bill",
@@ -125,10 +125,10 @@ public class EnumerableCorrelateTest {
         })
         .explainContains(""
             + "EnumerableCalc(expr#0..3=[{inputs}], empid=[$t1], name=[$t3])\n"
-            + "  EnumerableCorrelate(correlation=[$cor4], joinType=[inner], 
requiredColumns=[{0}])\n"
+            + "  EnumerableCorrelate(correlation=[$cor5], joinType=[inner], 
requiredColumns=[{0}])\n"
             + "    EnumerableAggregate(group=[{0}])\n"
             + "      EnumerableTableScan(table=[[s, depts]])\n"
-            + "    EnumerableCalc(expr#0..4=[{inputs}], expr#5=[$cor4], 
expr#6=[$t5.deptno], expr#7=[=($t1, $t6)], expr#8=[100], expr#9=[>($t0, $t8)], 
expr#10=[AND($t7, $t9)], proj#0..2=[{exprs}], $condition=[$t10])\n"
+            + "    EnumerableCalc(expr#0..4=[{inputs}], expr#5=[100], 
expr#6=[>($t0, $t5)], expr#7=[$cor5], expr#8=[$t7.deptno], expr#9=[=($t1, 
$t8)], expr#10=[AND($t6, $t9)], proj#0..2=[{exprs}], $condition=[$t10])\n"
             + "      EnumerableTableScan(table=[[s, emps]])")
         .returnsUnordered(
             "empid=110; name=Theodore",
diff --git 
a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableJoinTest.java 
b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableJoinTest.java
index aa500ab..4911f6a 100644
--- 
a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableJoinTest.java
+++ 
b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableJoinTest.java
@@ -207,14 +207,14 @@ public class EnumerableJoinTest {
                 builder.alias(builder.field(1, "depts", "deptno"), "d_deptno"))
             .build())
         .explainHookMatches("" // It is important that we have MergeJoin in 
the plan
-            + "EnumerableCalc(expr#0..4=[{inputs}], expr#5=[10], 
expr#6=[*($t5, $t0)], expr#7=[>($t2, $t6)], empid=[$t2], name=[$t4], 
dept_name=[$t1], e_deptno=[$t3], d_deptno=[$t0], $condition=[$t7])\n"
-            + "  EnumerableMergeJoin(condition=[=($0, $3)], 
joinType=[inner])\n"
-            + "    EnumerableSort(sort0=[$0], dir0=[ASC])\n"
-            + "      EnumerableCalc(expr#0..3=[{inputs}], 
proj#0..1=[{exprs}])\n"
-            + "        EnumerableTableScan(table=[[s, depts]])\n"
+            + "EnumerableCalc(expr#0..4=[{inputs}], expr#5=[10], 
expr#6=[*($t5, $t3)], expr#7=[>($t0, $t6)], empid=[$t0], name=[$t2], 
dept_name=[$t4], e_deptno=[$t1], d_deptno=[$t3], $condition=[$t7])\n"
+            + "  EnumerableMergeJoin(condition=[=($1, $3)], 
joinType=[inner])\n"
             + "    EnumerableSort(sort0=[$1], dir0=[ASC])\n"
             + "      EnumerableCalc(expr#0..4=[{inputs}], 
proj#0..2=[{exprs}])\n"
-            + "        EnumerableTableScan(table=[[s, emps]])\n")
+            + "        EnumerableTableScan(table=[[s, emps]])\n"
+            + "    EnumerableSort(sort0=[$0], dir0=[ASC])\n"
+            + "      EnumerableCalc(expr#0..3=[{inputs}], 
proj#0..1=[{exprs}])\n"
+            + "        EnumerableTableScan(table=[[s, depts]])\n")
         .returnsUnordered(""
             + "empid=110; name=Theodore; dept_name=Sales; e_deptno=10; 
d_deptno=10\n"
             + "empid=150; name=Sebastian; dept_name=Sales; e_deptno=10; 
d_deptno=10");
@@ -281,17 +281,17 @@ public class EnumerableJoinTest {
             + "    EnumerableCalc(expr#0..4=[{inputs}], expr#5=[2], 
expr#6=[=($t0, $t5)], empid=[$t0], name=[$t2], $condition=[$t6])\n"
             + "      EnumerableTableScan(table=[[s, emps]])\n"
             + "  EnumerableTableSpool(readType=[LAZY], writeType=[LAZY], 
table=[[#DELTA#]])\n"
-            + "    EnumerableCalc(expr#0..8=[{inputs}], empid=[$t0], 
name=[$t2])\n"
-            + "      EnumerableMergeJoin(condition=[=($0, $8)], 
joinType=[inner])\n"
-            + "        EnumerableSort(sort0=[$0], dir0=[ASC])\n"
-            + "          EnumerableTableScan(table=[[s, emps]])\n"
+            + "    EnumerableCalc(expr#0..8=[{inputs}], empid=[$t4], 
name=[$t6])\n"
+            + "      EnumerableMergeJoin(condition=[=($3, $4)], 
joinType=[inner])\n"
             + "        EnumerableSort(sort0=[$3], dir0=[ASC])\n"
             + "          EnumerableMergeJoin(condition=[=($0, $2)], 
joinType=[inner])\n"
             + "            EnumerableSort(sort0=[$0], dir0=[ASC])\n"
             + "              EnumerableInterpreter\n"
             + "                BindableTableScan(table=[[#DELTA#]])\n"
             + "            EnumerableSort(sort0=[$0], dir0=[ASC])\n"
-            + "              EnumerableTableScan(table=[[s, hierarchies]])\n")
+            + "              EnumerableTableScan(table=[[s, hierarchies]])\n"
+            + "        EnumerableSort(sort0=[$0], dir0=[ASC])\n"
+            + "          EnumerableTableScan(table=[[s, emps]])\n")
         .returnsUnordered(""
             + "empid=2; name=Emp2\n"
             + "empid=3; name=Emp3\n"
diff --git a/core/src/test/resources/sql/misc.iq 
b/core/src/test/resources/sql/misc.iq
index 4e63283..a142fbf 100644
--- a/core/src/test/resources/sql/misc.iq
+++ b/core/src/test/resources/sql/misc.iq
@@ -557,10 +557,10 @@ select count(*) as c from "hr"."emps", "hr"."depts";
 !ok
 EnumerableAggregate(group=[{}], C=[COUNT()])
   EnumerableNestedLoopJoin(condition=[true], joinType=[inner])
-    EnumerableCalc(expr#0..3=[{inputs}], expr#4=[0], DUMMY=[$t4])
-      EnumerableTableScan(table=[[hr, depts]])
     EnumerableCalc(expr#0..4=[{inputs}], expr#5=[0], DUMMY=[$t5])
       EnumerableTableScan(table=[[hr, emps]])
+    EnumerableCalc(expr#0..3=[{inputs}], expr#4=[0], DUMMY=[$t4])
+      EnumerableTableScan(table=[[hr, depts]])
 !plan
 
 # [CALCITE-345] AssertionError in RexToLixTranslator comparing to date literal
diff --git a/core/src/test/resources/sql/sub-query.iq 
b/core/src/test/resources/sql/sub-query.iq
index 8d6eae2..808add1 100644
--- a/core/src/test/resources/sql/sub-query.iq
+++ b/core/src/test/resources/sql/sub-query.iq
@@ -463,11 +463,11 @@ EnumerableCalc(expr#0..2=[{inputs}], proj#0..1=[{exprs}])
 # Uncorrelated
 with t (a, b) as (select * from (values (60, 'b')))
 select * from t where a in (select deptno from "scott".dept);
-EnumerableCalc(expr#0..2=[{inputs}], A=[$t1], B=[$t2])
-  EnumerableMergeJoin(condition=[=($0, $1)], joinType=[inner])
+EnumerableCalc(expr#0..2=[{inputs}], proj#0..1=[{exprs}])
+  EnumerableMergeJoin(condition=[=($0, $2)], joinType=[inner])
+    EnumerableValues(tuples=[[{ 60, 'b' }]])
     EnumerableCalc(expr#0..2=[{inputs}], DEPTNO=[$t0])
       EnumerableTableScan(table=[[scott, DEPT]])
-    EnumerableValues(tuples=[[{ 60, 'b' }]])
 !plan
 +---+---+
 | A | B |
diff --git a/core/src/test/resources/sql/winagg.iq 
b/core/src/test/resources/sql/winagg.iq
index b64e1fa..05270a7 100644
--- a/core/src/test/resources/sql/winagg.iq
+++ b/core/src/test/resources/sql/winagg.iq
@@ -431,14 +431,14 @@ join (
   from "hr"."emps"
   window w as (partition by "deptno" order by "commission")) b
 on a."deptno" = b."deptno"
-limit 5;
+order by "deptno", ar, br limit 5;
 
 +--------+-----+-----+
 | deptno | AR  | BR  |
 +--------+-----+-----+
 |     10 | 110 | 100 |
-|     10 | 110 | 110 |
-|     20 | 200 | 200 |
+|     10 | 110 | 100 |
+|     10 | 110 | 100 |
 |     10 | 110 | 110 |
 |     10 | 110 | 110 |
 +--------+-----+-----+
diff --git 
a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSort.java 
b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSort.java
index f122f27..95e604d 100644
--- a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSort.java
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSort.java
@@ -58,7 +58,7 @@ public class GeodeSort extends Sort implements GeodeRel {
     if (fetch != null) {
       return cost.multiplyBy(0.05);
     } else {
-      return cost;
+      return cost.multiplyBy(0.9);
     }
   }
 
diff --git 
a/mongodb/src/test/java/org/apache/calcite/adapter/mongodb/MongoAdapterTest.java
 
b/mongodb/src/test/java/org/apache/calcite/adapter/mongodb/MongoAdapterTest.java
index bc91d33..daff9ed 100644
--- 
a/mongodb/src/test/java/org/apache/calcite/adapter/mongodb/MongoAdapterTest.java
+++ 
b/mongodb/src/test/java/org/apache/calcite/adapter/mongodb/MongoAdapterTest.java
@@ -470,8 +470,8 @@ public class MongoAdapterTest implements SchemaFactory {
                 "{$project: {STATE: '$state', POP: '$pop'}}",
                 "{$group: {_id: '$STATE', _1: {$sum: '$POP'}, _2: {$sum: 
{$cond: [ {$eq: ['POP', null]}, 0, 1]}}}}",
                 "{$project: {STATE: '$_id', _1: '$_1', _2: '$_2'}}",
-                "{$sort: {STATE: 1}}",
-                "{$project: {STATE: 1, A: {$divide: [{$cond:[{$eq: ['$_2', 
{$literal: 0}]},null,'$_1']}, '$_2']}, S: {$cond:[{$eq: ['$_2', {$literal: 
0}]},null,'$_1']}, C: '$_2'}}"));
+                "{$project: {STATE: 1, A: {$divide: [{$cond:[{$eq: ['$_2', 
{$literal: 0}]},null,'$_1']}, '$_2']}, S: {$cond:[{$eq: ['$_2', {$literal: 
0}]},null,'$_1']}, C: '$_2'}}",
+                "{$sort: {STATE: 1}}"));
   }
 
   @Test public void testGroupByHaving() {
diff --git a/spark/src/test/java/org/apache/calcite/test/SparkAdapterTest.java 
b/spark/src/test/java/org/apache/calcite/test/SparkAdapterTest.java
index 89a0eb6..f2400c5 100644
--- a/spark/src/test/java/org/apache/calcite/test/SparkAdapterTest.java
+++ b/spark/src/test/java/org/apache/calcite/test/SparkAdapterTest.java
@@ -166,8 +166,8 @@ public class SparkAdapterTest {
         + "order by x asc";
 
     final String plan = "PLAN="
-        + "EnumerableCalc(expr#0..4=[{inputs}], expr#5=[CAST($t1):CHAR(1) NOT 
NULL], expr#6=[CAST($t2):CHAR(1) NOT NULL], expr#7=[CAST($t3):BIGINT NOT NULL], 
X=[$t0], MIN_Y=[$t5], MAX_Y=[$t6], CNT_Y=[$t7], CNT_DIST_Y=[$t4])\n"
-        + "  EnumerableSort(sort0=[$0], dir0=[ASC])\n"
+        + "EnumerableSort(sort0=[$0], dir0=[ASC])\n"
+        + "  EnumerableCalc(expr#0..4=[{inputs}], expr#5=[CAST($t1):CHAR(1) 
NOT NULL], expr#6=[CAST($t2):CHAR(1) NOT NULL], expr#7=[CAST($t3):BIGINT NOT 
NULL], X=[$t0], MIN_Y=[$t5], MAX_Y=[$t6], CNT_Y=[$t7], CNT_DIST_Y=[$t4])\n"
         + "    EnumerableAggregate(group=[{0}], MIN_Y=[MIN($2) FILTER $6], 
MAX_Y=[MIN($3) FILTER $6], CNT_Y=[MIN($4) FILTER $6], CNT_DIST_Y=[COUNT($1) 
FILTER $5])\n"
         + "      EnumerableCalc(expr#0..5=[{inputs}], expr#6=[0], 
expr#7=[=($t5, $t6)], expr#8=[1], expr#9=[=($t5, $t8)], proj#0..4=[{exprs}], 
$g_0=[$t7], $g_1=[$t9])\n"
         + "        EnumerableAggregate(group=[{0, 1}], groups=[[{0, 1}, {0}]], 
MIN_Y=[MIN($1)], MAX_Y=[MAX($1)], CNT_Y=[COUNT()], $g=[GROUPING($0, $1)])\n"
@@ -188,8 +188,8 @@ public class SparkAdapterTest {
         + "order by x desc";
 
     final String plan = "PLAN="
-        + "EnumerableCalc(expr#0..4=[{inputs}], expr#5=[CAST($t1):CHAR(1) NOT 
NULL], expr#6=[CAST($t2):CHAR(1) NOT NULL], expr#7=[CAST($t3):BIGINT NOT NULL], 
X=[$t0], MIN_Y=[$t5], MAX_Y=[$t6], CNT_Y=[$t7], CNT_DIST_Y=[$t4])\n"
-        + "  EnumerableSort(sort0=[$0], dir0=[DESC])\n"
+        + "EnumerableSort(sort0=[$0], dir0=[DESC])\n"
+        + "  EnumerableCalc(expr#0..4=[{inputs}], expr#5=[CAST($t1):CHAR(1) 
NOT NULL], expr#6=[CAST($t2):CHAR(1) NOT NULL], expr#7=[CAST($t3):BIGINT NOT 
NULL], X=[$t0], MIN_Y=[$t5], MAX_Y=[$t6], CNT_Y=[$t7], CNT_DIST_Y=[$t4])\n"
         + "    EnumerableAggregate(group=[{0}], MIN_Y=[MIN($2) FILTER $6], 
MAX_Y=[MIN($3) FILTER $6], CNT_Y=[MIN($4) FILTER $6], CNT_DIST_Y=[COUNT($1) 
FILTER $5])\n"
         + "      EnumerableCalc(expr#0..5=[{inputs}], expr#6=[0], 
expr#7=[=($t5, $t6)], expr#8=[1], expr#9=[=($t5, $t8)], proj#0..4=[{exprs}], 
$g_0=[$t7], $g_1=[$t9])\n"
         + "        EnumerableAggregate(group=[{0, 1}], groups=[[{0, 1}, {0}]], 
MIN_Y=[MIN($1)], MAX_Y=[MAX($1)], CNT_Y=[COUNT()], $g=[GROUPING($0, $1)])\n"

Reply via email to