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 2017/06/09 21:03:12 UTC

[2/2] calcite git commit: [CALCITE-1812] Cache RelMetadataQuery, invalidating when a rule registers new RelNodes (Remus Rusanu)

[CALCITE-1812] Cache RelMetadataQuery, invalidating when a rule registers new RelNodes (Remus Rusanu)

RelMetadataQuery is cached in RelOptCluster; it is invalidated when a
rule calls RelOptRuleCall.transformTo, because that may affect costs
and other statistics; rules (and other code) should get it via
RelOptRuleCall.getMetadataQuery whenever possible.

Close apache/calcite#462


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

Branch: refs/heads/master
Commit: 56d5261abb893dd92b0a2cc4893292876a2b7880
Parents: 33873f6
Author: Remus Rusanu <re...@apache.org>
Authored: Wed May 31 00:54:26 2017 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Jun 9 13:55:16 2017 -0700

----------------------------------------------------------------------
 .../adapter/enumerable/EnumerableCalc.java      |  2 +-
 .../adapter/enumerable/EnumerableFilter.java    |  2 +-
 .../adapter/enumerable/EnumerableLimit.java     |  2 +-
 .../adapter/enumerable/EnumerableMergeJoin.java |  2 +-
 .../adapter/enumerable/EnumerableProject.java   |  2 +-
 .../adapter/enumerable/EnumerableValues.java    |  2 +-
 .../apache/calcite/interpreter/Bindables.java   |  2 +-
 .../apache/calcite/plan/ConventionTraitDef.java |  2 +-
 .../org/apache/calcite/plan/RelOptCluster.java  | 23 +++++++++
 .../org/apache/calcite/plan/RelOptRuleCall.java |  9 ++++
 .../org/apache/calcite/plan/RelOptUtil.java     |  4 +-
 .../org/apache/calcite/plan/hep/HepPlanner.java |  4 +-
 .../apache/calcite/plan/hep/HepRuleCall.java    |  1 +
 .../org/apache/calcite/plan/volcano/RelSet.java |  2 +-
 .../apache/calcite/plan/volcano/RelSubset.java  |  2 +-
 .../apache/calcite/plan/volcano/RuleQueue.java  | 14 +++---
 .../calcite/plan/volcano/VolcanoPlanner.java    |  8 ++--
 .../calcite/plan/volcano/VolcanoRuleCall.java   |  1 +
 .../java/org/apache/calcite/rel/core/Union.java |  3 +-
 .../calcite/rel/externalize/RelWriterImpl.java  |  3 +-
 .../apache/calcite/rel/logical/LogicalCalc.java |  2 +-
 .../calcite/rel/logical/LogicalFilter.java      |  2 +-
 .../calcite/rel/logical/LogicalProject.java     |  2 +-
 .../calcite/rel/logical/LogicalValues.java      |  2 +-
 .../rel/rules/AggregateFilterTransposeRule.java |  2 +-
 .../rel/rules/AggregateJoinTransposeRule.java   |  2 +-
 .../AggregateProjectPullUpConstantsRule.java    |  2 +-
 .../calcite/rel/rules/AggregateRemoveRule.java  |  2 +-
 .../rel/rules/AggregateStarTableRule.java       |  2 +-
 .../rel/rules/AggregateUnionTransposeRule.java  |  2 +-
 .../rules/JoinPushTransitivePredicatesRule.java |  2 +-
 .../apache/calcite/rel/rules/LoptMultiJoin.java |  2 +-
 .../calcite/rel/rules/LoptOptimizeJoinRule.java | 49 +++++++++++++-------
 .../rel/rules/LoptSemiJoinOptimizer.java        |  6 +--
 .../rel/rules/MultiJoinOptimizeBushyRule.java   |  2 +-
 .../rel/rules/ReduceExpressionsRule.java        |  6 +--
 .../rel/rules/SortJoinTransposeRule.java        |  4 +-
 .../rel/rules/SortUnionTransposeRule.java       |  2 +-
 .../calcite/rel/rules/SubQueryRemoveRule.java   |  2 +-
 .../rel/rules/UnionPullUpConstantsRule.java     |  2 +-
 .../apache/calcite/sql2rel/RelDecorrelator.java |  6 +--
 .../apache/calcite/sql2rel/RelFieldTrimmer.java |  2 +-
 .../org/apache/calcite/tools/RelBuilder.java    |  2 +-
 43 files changed, 122 insertions(+), 75 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java
index a23dcfe..25c882a 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java
@@ -90,7 +90,7 @@ public class EnumerableCalc extends Calc implements EnumerableRel {
   public static EnumerableCalc create(final RelNode input,
       final RexProgram program) {
     final RelOptCluster cluster = input.getCluster();
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = cluster.getMetadataQuery();
     final RelTraitSet traitSet = cluster.traitSet()
         .replace(EnumerableConvention.INSTANCE)
         .replaceIfs(RelCollationTraitDef.INSTANCE,

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java
index 894ff16..da1cefa 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java
@@ -54,7 +54,7 @@ public class EnumerableFilter
   public static EnumerableFilter create(final RelNode input,
       RexNode condition) {
     final RelOptCluster cluster = input.getCluster();
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = cluster.getMetadataQuery();
     final RelTraitSet traitSet =
         cluster.traitSetOf(EnumerableConvention.INSTANCE)
             .replaceIfs(

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java
index 827944f..0e00dda 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java
@@ -64,7 +64,7 @@ public class EnumerableLimit extends SingleRel implements EnumerableRel {
   public static EnumerableLimit create(final RelNode input, RexNode offset,
       RexNode fetch) {
     final RelOptCluster cluster = input.getCluster();
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = cluster.getMetadataQuery();
     final RelTraitSet traitSet =
         cluster.traitSetOf(EnumerableConvention.INSTANCE)
             .replaceIfs(

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/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 95d6239..6b72e08 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
@@ -90,7 +90,7 @@ public class EnumerableMergeJoin extends EquiJoin implements EnumerableRel {
     final RelOptCluster cluster = right.getCluster();
     RelTraitSet traitSet = cluster.traitSet();
     if (traitSet.isEnabled(RelCollationTraitDef.INSTANCE)) {
-      final RelMetadataQuery mq = RelMetadataQuery.instance();
+      final RelMetadataQuery mq = cluster.getMetadataQuery();
       final List<RelCollation> collations =
           RelMdCollation.mergeJoin(mq, left, right, leftKeys, rightKeys);
       traitSet = traitSet.replace(collations);

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java
index fa2b48b..a1c3984 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java
@@ -69,7 +69,7 @@ public class EnumerableProject extends Project implements EnumerableRel {
   public static EnumerableProject create(final RelNode input,
       final List<? extends RexNode> projects, RelDataType rowType) {
     final RelOptCluster cluster = input.getCluster();
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = cluster.getMetadataQuery();
     final RelTraitSet traitSet =
         cluster.traitSet().replace(EnumerableConvention.INSTANCE)
             .replaceIfs(RelCollationTraitDef.INSTANCE,

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java
index 6023c02..b4a95c3 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java
@@ -58,7 +58,7 @@ public class EnumerableValues extends Values implements EnumerableRel {
   public static EnumerableValues create(RelOptCluster cluster,
       final RelDataType rowType,
       final ImmutableList<ImmutableList<RexLiteral>> tuples) {
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = cluster.getMetadataQuery();
     final RelTraitSet traitSet =
         cluster.traitSetOf(EnumerableConvention.INSTANCE)
             .replaceIfs(RelCollationTraitDef.INSTANCE,

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/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 82c8ada..167181c 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/Bindables.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/Bindables.java
@@ -269,7 +269,7 @@ public class Bindables {
     public static BindableFilter create(final RelNode input,
         RexNode condition) {
       final RelOptCluster cluster = input.getCluster();
-      final RelMetadataQuery mq = RelMetadataQuery.instance();
+      final RelMetadataQuery mq = cluster.getMetadataQuery();
       final RelTraitSet traitSet =
           cluster.traitSetOf(BindableConvention.INSTANCE)
               .replaceIfs(RelCollationTraitDef.INSTANCE,

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/plan/ConventionTraitDef.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/ConventionTraitDef.java b/core/src/main/java/org/apache/calcite/plan/ConventionTraitDef.java
index a46ee73..2cb0d58 100644
--- a/core/src/main/java/org/apache/calcite/plan/ConventionTraitDef.java
+++ b/core/src/main/java/org/apache/calcite/plan/ConventionTraitDef.java
@@ -132,7 +132,7 @@ public class ConventionTraitDef extends RelTraitDef<Convention> {
       RelNode rel,
       Convention toConvention,
       boolean allowInfiniteCostConverters) {
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = rel.getCluster().getMetadataQuery();
     final ConversionData conversionData = getConversionData(planner);
 
     final Convention fromConvention = rel.getConvention();

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/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 f88e232..bce405c 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
@@ -22,6 +22,7 @@ import org.apache.calcite.rel.metadata.DefaultRelMetadataProvider;
 import org.apache.calcite.rel.metadata.MetadataFactory;
 import org.apache.calcite.rel.metadata.MetadataFactoryImpl;
 import org.apache.calcite.rel.metadata.RelMetadataProvider;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexNode;
@@ -48,6 +49,7 @@ public class RelOptCluster {
   private RelMetadataProvider metadataProvider;
   private MetadataFactory metadataFactory;
   private final RelTraitSet emptyTraitSet;
+  private RelMetadataQuery mq;
 
   //~ Constructors -----------------------------------------------------------
 
@@ -140,6 +142,27 @@ public class RelOptCluster {
     return metadataFactory;
   }
 
+  /** Returns the current RelMetadataQuery.
+   *
+   * <p>This method might be changed or moved in future.
+   * If you have a {@link RelOptRuleCall} available,
+   * for example if you are in a {@link RelOptRule#onMatch(RelOptRuleCall)}
+   * method, then use {@link RelOptRuleCall#getMetadataQuery()} instead. */
+  public RelMetadataQuery getMetadataQuery() {
+    if (mq == null) {
+      mq = RelMetadataQuery.instance();
+    }
+    return mq;
+  }
+
+  /**
+   * Should be called whenever the current {@link RelMetadataQuery} becomes
+   * invalid. Typically invoked from {@link RelOptRuleCall#transformTo}.
+   */
+  public void invalidateMetadataQuery() {
+    mq = null;
+  }
+
   /**
    * Constructs a new id for a correlating variable. It is unique within the
    * whole query.

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/plan/RelOptRuleCall.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptRuleCall.java b/core/src/main/java/org/apache/calcite/plan/RelOptRuleCall.java
index e3f78aa..d753b66 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptRuleCall.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptRuleCall.java
@@ -18,6 +18,7 @@ package org.apache.calcite.plan;
 
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.util.trace.CalciteTrace;
 
@@ -191,6 +192,14 @@ public abstract class RelOptRuleCall {
   }
 
   /**
+   * Returns the current RelMetadataQuery, to be used for instance by
+   * {@link RelOptRule#onMatch(RelOptRuleCall)}.
+   */
+  public RelMetadataQuery getMetadataQuery() {
+    return rel(0).getCluster().getMetadataQuery();
+  }
+
+  /**
    * @return list of parents of the first relational expression
    */
   public List<RelNode> getParents() {

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/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 e91dac5..e5419e5 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -3424,6 +3424,7 @@ public abstract class RelOptUtil {
     final RexBuilder rexBuilder = r.getCluster().getRexBuilder();
     final RelDataType rowType = r.getRowType();
     final List<RexNode> list = new ArrayList<>();
+    final RelMetadataQuery mq = r.getCluster().getMetadataQuery();
     for (RelDataTypeField field : rowType.getFieldList()) {
       if (field.getType().isNullable()) {
         list.add(
@@ -3435,8 +3436,7 @@ public abstract class RelOptUtil {
       // All columns are declared NOT NULL.
       return false;
     }
-    final RelOptPredicateList predicates =
-        RelMetadataQuery.instance().getPulledUpPredicates(r);
+    final RelOptPredicateList predicates = mq.getPulledUpPredicates(r);
     if (predicates.pulledUpPredicates.isEmpty()) {
       // We have no predicates, so cannot deduce that any of the fields
       // declared NULL are really NOT NULL.

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java b/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java
index 70ab6b9..1753305 100644
--- a/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java
+++ b/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java
@@ -640,7 +640,7 @@ public class HepPlanner extends AbstractRelOptPlanner {
       bestRel = call.getResults().get(0);
     } else {
       RelOptCost bestCost = null;
-      final RelMetadataQuery mq = RelMetadataQuery.instance();
+      final RelMetadataQuery mq = call.getMetadataQuery();
       for (RelNode rel : call.getResults()) {
         RelOptCost thisCost = getCost(rel, mq);
         if (LOGGER.isTraceEnabled()) {
@@ -939,7 +939,7 @@ public class HepPlanner extends AbstractRelOptPlanner {
 
     assertNoCycles();
 
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = root.getCluster().getMetadataQuery();
     final StringBuilder sb = new StringBuilder();
     sb.append("\nBreadth-first from root:  {\n");
     for (HepRelVertex vertex : BreadthFirstIterator.of(graph, root)) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/plan/hep/HepRuleCall.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/hep/HepRuleCall.java b/core/src/main/java/org/apache/calcite/plan/hep/HepRuleCall.java
index 7aacbb7..8a280f8 100644
--- a/core/src/main/java/org/apache/calcite/plan/hep/HepRuleCall.java
+++ b/core/src/main/java/org/apache/calcite/plan/hep/HepRuleCall.java
@@ -56,6 +56,7 @@ public class HepRuleCall extends RelOptRuleCall {
     final RelNode rel0 = rels[0];
     RelOptUtil.verifyTypeEquivalence(rel0, rel, rel0);
     results.add(rel);
+    rel(0).getCluster().invalidateMetadataQuery();
   }
 
   List<RelNode> getResults() {

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/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 098fc6a..2d93c87 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
@@ -354,7 +354,7 @@ class RelSet {
 
     // Make sure the cost changes as a result of merging are propagated.
     final Set<RelSubset> activeSet = new HashSet<>();
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = rel.getCluster().getMetadataQuery();
     for (RelNode parentRel : getParentRels()) {
       final RelSubset parentSubset = planner.getSubset(parentRel);
       parentSubset.propagateCostImprovements(

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/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 24e15e8..e556063 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
@@ -126,7 +126,7 @@ public class RelSubset extends AbstractRelNode {
    */
   private void computeBestCost(RelOptPlanner planner) {
     bestCost = planner.getCostFactory().makeInfiniteCost();
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = getCluster().getMetadataQuery();
     for (RelNode rel : getRels()) {
       final RelOptCost cost = planner.getCost(rel, mq);
       if (cost.isLt(bestCost)) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/plan/volcano/RuleQueue.java
----------------------------------------------------------------------
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 70b5133..439c653 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
@@ -371,12 +371,12 @@ class RuleQueue {
    * node's algorithm.</li>
    * </ul>
    *
-   * The formula for the importance I of node n is:
+   * <p>The formula for the importance I of node n is:
    *
    * <blockquote>I<sub>n</sub> = Sum<sub>parents p of n</sub>{I<sub>p</sub> .
    * W <sub>n, p</sub>}</blockquote>
    *
-   * where W<sub>n, p</sub>, the weight of n within its parent p, is
+   * <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>n<sub>0</sub></sub> + ... + Cost<sub>n<sub>k</sub></sub>)
@@ -388,7 +388,7 @@ class RuleQueue {
       // The root always has importance = 1
       importance = 1.0;
     } else {
-      final RelMetadataQuery mq = RelMetadataQuery.instance();
+      final RelMetadataQuery mq = subset.getCluster().getMetadataQuery();
 
       // The importance of a subset is the max of its importance to its
       // parents
@@ -535,15 +535,15 @@ class RuleQueue {
    * from root of the operand tree to one of the leaves.
    *
    * <p>It is OK for a match to have duplicate subsets if they are not on the
-   * same path. For example,</p>
+   * same path. For example,
    *
-   * <pre>
+   * <blockquote><pre>
    *   Join
    *  /   \
    * X     X
-   * </pre>
+   * </pre></blockquote>
    *
-   * <p>is a valid match.</p>
+   * <p>is a valid match.
    *
    * @throws org.apache.calcite.util.Util.FoundOne on match
    */

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/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 1e73adf..4b43658 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
@@ -888,7 +888,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
    * Checks internal consistency.
    */
   protected void validate() {
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = root.getCluster().getMetadataQuery();
     for (RelSet set : allSets) {
       if (set.equivalentSet != null) {
         throw new AssertionError(
@@ -1169,7 +1169,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
    * @see #normalizePlan(String)
    */
   public void dump(PrintWriter pw) {
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = root.getCluster().getMetadataQuery();
     pw.println("Root: " + root.getDescription());
     pw.println("Original rel:");
     pw.println(originalRootString);
@@ -1653,7 +1653,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
     // 100. We think this happens because the back-links to parents are
     // not established. So, give the subset another change to figure out
     // its cost.
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = rel.getCluster().getMetadataQuery();
     subset.propagateCostImprovements(this, mq, rel, new HashSet<RelSubset>());
 
     return subset;
@@ -1718,7 +1718,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
    * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MockTableImplRel.FENNEL_EXEC(
    * table=[CATALOG, SALES, EMP])</blockquote>
    *
-   * becomes
+   * <p>becomes
    *
    * <blockquote>
    * FennelAggRel.FENNEL_EXEC(child=Subset#{0}.FENNEL_EXEC, groupCount=1,

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleCall.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleCall.java b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleCall.java
index 7a629fe..392581d 100644
--- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleCall.java
+++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleCall.java
@@ -133,6 +133,7 @@ public class VolcanoRuleCall extends RelOptRuleCall {
             entry.getKey(), entry.getValue(), this);
       }
       volcanoPlanner.ensureRegistered(rel, rels[0], this);
+      rels[0].getCluster().invalidateMetadataQuery();
 
       if (volcanoPlanner.listener != null) {
         RelOptListener.RuleProductionEvent event =

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/core/Union.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Union.java b/core/src/main/java/org/apache/calcite/rel/core/Union.java
index f7c6a42..942df4a 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Union.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Union.java
@@ -53,8 +53,7 @@ public abstract class Union extends SetOp {
   //~ Methods ----------------------------------------------------------------
 
   @Override public double estimateRowCount(RelMetadataQuery mq) {
-    double dRows = RelMdUtil.getUnionAllRowCount(RelMetadataQuery.instance(),
-        this);
+    double dRows = RelMdUtil.getUnionAllRowCount(mq, this);
     if (!all) {
       dRows *= 0.5;
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java
index 9386f81..af2985e 100644
--- a/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java
+++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java
@@ -41,7 +41,6 @@ public class RelWriterImpl implements RelWriter {
   private final boolean withIdPrefix;
   protected final Spacer spacer = new Spacer();
   private final List<Pair<String, Object>> values = new ArrayList<>();
-  protected final RelMetadataQuery mq = RelMetadataQuery.instance();
 
   //~ Constructors -----------------------------------------------------------
 
@@ -62,7 +61,7 @@ public class RelWriterImpl implements RelWriter {
   protected void explain_(RelNode rel,
       List<Pair<String, Object>> values) {
     List<RelNode> inputs = rel.getInputs();
-
+    final RelMetadataQuery mq = rel.getCluster().getMetadataQuery();
     if (!mq.isVisibleInExplain(rel, detailLevel)) {
       // render children in place of this, at same level
       explainInputs(inputs);

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/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 3341ebf..0d37710 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
@@ -91,7 +91,7 @@ public final class LogicalCalc extends Calc {
   public static LogicalCalc create(final RelNode input,
       final RexProgram program) {
     final RelOptCluster cluster = input.getCluster();
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = cluster.getMetadataQuery();
     final RelTraitSet traitSet = cluster.traitSet()
         .replace(Convention.NONE)
         .replaceIfs(RelCollationTraitDef.INSTANCE,

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/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 1c25fba..fce9136 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
@@ -107,7 +107,7 @@ public final class LogicalFilter extends Filter {
   public static LogicalFilter create(final RelNode input, RexNode condition,
       ImmutableSet<CorrelationId> variablesSet) {
     final RelOptCluster cluster = input.getCluster();
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = cluster.getMetadataQuery();
     final RelTraitSet traitSet = cluster.traitSetOf(Convention.NONE)
         .replaceIfs(RelCollationTraitDef.INSTANCE,
             new Supplier<List<RelCollation>>() {

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java
index 6e9044e..9eec20b 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java
@@ -107,7 +107,7 @@ public final class LogicalProject extends Project {
   public static LogicalProject create(final RelNode input,
       final List<? extends RexNode> projects, RelDataType rowType) {
     final RelOptCluster cluster = input.getCluster();
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = cluster.getMetadataQuery();
     final RelTraitSet traitSet =
         cluster.traitSet().replace(Convention.NONE)
             .replaceIfs(

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
index 6d84d74..b2934f5 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
@@ -82,7 +82,7 @@ public class LogicalValues extends Values {
   public static LogicalValues create(RelOptCluster cluster,
       final RelDataType rowType,
       final ImmutableList<ImmutableList<RexLiteral>> tuples) {
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = cluster.getMetadataQuery();
     final RelTraitSet traitSet = cluster.traitSetOf(Convention.NONE)
         .replaceIfs(
             RelCollationTraitDef.INSTANCE, new Supplier<List<RelCollation>>() {

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
index 943f25a..932a18f 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
@@ -81,7 +81,7 @@ public class AggregateFilterTransposeRule extends RelOptRule {
     final ImmutableBitSet newGroupSet =
         aggregate.getGroupSet().union(filterColumns);
     final RelNode input = filter.getInput();
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = call.getMetadataQuery();
     final Boolean unique = mq.areColumnsUnique(input, newGroupSet);
     if (unique != null && unique) {
       // The input is already unique on the grouping columns, so there's little

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/AggregateJoinTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateJoinTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateJoinTransposeRule.java
index 3aba5bf..5794352 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateJoinTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateJoinTransposeRule.java
@@ -156,7 +156,7 @@ public class AggregateJoinTransposeRule extends RelOptRule {
 
     // Do the columns used by the join appear in the output of the aggregate?
     final ImmutableBitSet aggregateColumns = aggregate.getGroupSet();
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = call.getMetadataQuery();
     final ImmutableBitSet keyColumns = keyColumns(aggregateColumns,
         mq.getPulledUpPredicates(join).pulledUpPredicates);
     final ImmutableBitSet joinColumns =

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
index f39cdf9..6bb1d3b 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
@@ -106,7 +106,7 @@ public class AggregateProjectPullUpConstantsRule extends RelOptRule {
     }
 
     final RexBuilder rexBuilder = aggregate.getCluster().getRexBuilder();
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = call.getMetadataQuery();
     final RelOptPredicateList predicates =
         mq.getPulledUpPredicates(aggregate.getInput());
     if (predicates == null) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/AggregateRemoveRule.java
----------------------------------------------------------------------
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 0a73787..46a5826 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
@@ -61,7 +61,7 @@ public class AggregateRemoveRule extends RelOptRule {
     if (!aggregate.getAggCallList().isEmpty() || aggregate.indicator) {
       return;
     }
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = call.getMetadataQuery();
     if (!SqlFunctions.isTrue(mq.areColumnsUnique(input, aggregate.getGroupSet()))) {
       return;
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
index 3945924..6391a98 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
@@ -116,7 +116,7 @@ public class AggregateStarTableRule extends RelOptRule {
     final RelBuilder relBuilder = call.builder();
     final CalciteSchema.TableEntry tableEntry = pair.left;
     final TileKey tileKey = pair.right;
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = call.getMetadataQuery();
     final double rowCount = aggregate.estimateRowCount(mq);
     final Table aggregateTable = tableEntry.getTable();
     final RelDataType aggregateTableRowType =

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
index 12300d9..583aa5f 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
@@ -116,7 +116,7 @@ public class AggregateUnionTransposeRule extends RelOptRule {
     // create corresponding aggregates on top of each union child
     final RelBuilder relBuilder = call.builder();
     int transformCount = 0;
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = call.getMetadataQuery();
     for (RelNode input : union.getInputs()) {
       boolean alreadyUnique =
           RelMdUtil.areColumnsDefinitelyUnique(mq, input,

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/JoinPushTransitivePredicatesRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/JoinPushTransitivePredicatesRule.java b/core/src/main/java/org/apache/calcite/rel/rules/JoinPushTransitivePredicatesRule.java
index 0b020c7..acec023 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/JoinPushTransitivePredicatesRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/JoinPushTransitivePredicatesRule.java
@@ -59,7 +59,7 @@ public class JoinPushTransitivePredicatesRule extends RelOptRule {
 
   @Override public void onMatch(RelOptRuleCall call) {
     Join join = call.rel(0);
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = call.getMetadataQuery();
     RelOptPredicateList preds = mq.getPulledUpPredicates(join);
 
     if (preds.leftInferredPredicates.isEmpty()

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/LoptMultiJoin.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/LoptMultiJoin.java b/core/src/main/java/org/apache/calcite/rel/rules/LoptMultiJoin.java
index 4f71d01..7d865bf 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/LoptMultiJoin.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/LoptMultiJoin.java
@@ -701,7 +701,7 @@ public class LoptMultiJoin {
     // First, locate the originating column for all simple column
     // references in the left factor.
     final RelNode left = getJoinFactor(leftFactor);
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = left.getCluster().getMetadataQuery();
     final Map<Integer, Integer> leftFactorColMapping = new HashMap<>();
     for (int i = 0; i < left.getRowType().getFieldCount(); i++) {
       final RelColumnOrigin colOrigin = mq.getColumnOrigin(left, i);

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/LoptOptimizeJoinRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/LoptOptimizeJoinRule.java b/core/src/main/java/org/apache/calcite/rel/rules/LoptOptimizeJoinRule.java
index 2d17096..e17ac1e 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/LoptOptimizeJoinRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/LoptOptimizeJoinRule.java
@@ -88,12 +88,13 @@ public class LoptOptimizeJoinRule extends RelOptRule {
   public void onMatch(RelOptRuleCall call) {
     final MultiJoin multiJoinRel = call.rel(0);
     final LoptMultiJoin multiJoin = new LoptMultiJoin(multiJoinRel);
+    final RelMetadataQuery mq = call.getMetadataQuery();
 
-    findRemovableOuterJoins(multiJoin);
+    findRemovableOuterJoins(mq, multiJoin);
 
     final RexBuilder rexBuilder = multiJoinRel.getCluster().getRexBuilder();
     final LoptSemiJoinOptimizer semiJoinOpt =
-        new LoptSemiJoinOptimizer(multiJoin, rexBuilder);
+        new LoptSemiJoinOptimizer(call.getMetadataQuery(), multiJoin, rexBuilder);
 
     // determine all possible semijoins
     semiJoinOpt.makePossibleSemiJoins(multiJoin);
@@ -116,9 +117,9 @@ public class LoptOptimizeJoinRule extends RelOptRule {
 
     multiJoin.setFactorWeights();
 
-    findRemovableSelfJoins(multiJoin);
+    findRemovableSelfJoins(mq, multiJoin);
 
-    findBestOrderings(call.builder(), multiJoin, semiJoinOpt, call);
+    findBestOrderings(mq, call.builder(), multiJoin, semiJoinOpt, call);
   }
 
   /**
@@ -128,7 +129,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
    *
    * @param multiJoin join factors being optimized
    */
-  private void findRemovableOuterJoins(LoptMultiJoin multiJoin) {
+  private void findRemovableOuterJoins(RelMetadataQuery mq, LoptMultiJoin multiJoin) {
     final List<Integer> removalCandidates = new ArrayList<>();
     for (int factIdx = 0;
         factIdx < multiJoin.getNumJoinFactors();
@@ -209,7 +210,6 @@ public class LoptOptimizeJoinRule extends RelOptRule {
         // part of an equality join condition, nulls are filtered out
         // by the join.  So, it's ok if there are nulls in the join
         // keys.
-        final RelMetadataQuery mq = RelMetadataQuery.instance();
         if (RelMdUtil.areColumnsDefinitelyUniqueWhenNullsFiltered(mq,
             multiJoin.getJoinFactor(factIdx), joinKeys)) {
           multiJoin.addRemovableOuterJoinFactor(factIdx);
@@ -286,9 +286,9 @@ public class LoptOptimizeJoinRule extends RelOptRule {
    *
    * @param multiJoin join factors being optimized
    */
-  private void findRemovableSelfJoins(LoptMultiJoin multiJoin) {
+  private void findRemovableSelfJoins(RelMetadataQuery mq, LoptMultiJoin multiJoin) {
     // Candidates for self-joins must be simple factors
-    Map<Integer, RelOptTable> simpleFactors = getSimpleFactors(multiJoin);
+    Map<Integer, RelOptTable> simpleFactors = getSimpleFactors(mq, multiJoin);
 
     // See if a simple factor is repeated and therefore potentially is
     // part of a self-join.  Restrict each factor to at most one
@@ -332,6 +332,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
       }
       if ((selfJoinFilters.size() > 0)
           && isSelfJoinFilterUnique(
+            mq,
             multiJoin,
             factor1,
             factor2,
@@ -351,7 +352,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
    * @return map consisting of the simple factors and the tables they
    * correspond
    */
-  private Map<Integer, RelOptTable> getSimpleFactors(LoptMultiJoin multiJoin) {
+  private Map<Integer, RelOptTable> getSimpleFactors(RelMetadataQuery mq, LoptMultiJoin multiJoin) {
     final Map<Integer, RelOptTable> returnList = new HashMap<>();
 
     // Loop through all join factors and locate the ones where each
@@ -361,7 +362,6 @@ public class LoptOptimizeJoinRule extends RelOptRule {
     if (multiJoin.getMultiJoinRel().isFullOuterJoin()) {
       return returnList;
     }
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
     for (int factIdx = 0; factIdx < multiJoin.getNumJoinFactors(); factIdx++) {
       if (multiJoin.isNullGenerating(factIdx)
           || (multiJoin.getJoinRemovalFactor(factIdx) != null)) {
@@ -389,6 +389,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
    * @return true if the criteria are met
    */
   private boolean isSelfJoinFilterUnique(
+      RelMetadataQuery mq,
       LoptMultiJoin multiJoin,
       int leftFactor,
       int rightFactor,
@@ -422,7 +423,6 @@ public class LoptOptimizeJoinRule extends RelOptRule {
                 rightRel.getRowType().getFieldList(),
                 adjustments));
 
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
     return areSelfJoinKeysUnique(mq, leftRel, rightRel, joinFilters);
   }
 
@@ -435,6 +435,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
    * @param call RelOptRuleCall associated with this rule
    */
   private void findBestOrderings(
+      RelMetadataQuery mq,
       RelBuilder relBuilder,
       LoptMultiJoin multiJoin,
       LoptSemiJoinOptimizer semiJoinOpt,
@@ -452,6 +453,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
       }
       LoptJoinTree joinTree =
           createOrdering(
+              mq,
               relBuilder,
               multiJoin,
               semiJoinOpt,
@@ -563,6 +565,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
    * @return computed cardinality
    */
   private Double computeJoinCardinality(
+      RelMetadataQuery mq,
       LoptMultiJoin multiJoin,
       LoptSemiJoinOptimizer semiJoinOpt,
       LoptJoinTree joinTree,
@@ -605,7 +608,6 @@ public class LoptOptimizeJoinRule extends RelOptRule {
     if (joinKeys.isEmpty()) {
       return null;
     } else {
-      final RelMetadataQuery mq = semiJoinOpt.mq;
       return mq.getDistinctRowCount(semiJoinOpt.getChosenSemiJoin(factor),
           joinKeys.build(), null);
     }
@@ -667,6 +669,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
    * firstFactor to appear as the first factor in the join
    */
   private LoptJoinTree createOrdering(
+      RelMetadataQuery mq,
       RelBuilder relBuilder,
       LoptMultiJoin multiJoin,
       LoptSemiJoinOptimizer semiJoinOpt,
@@ -698,6 +701,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
         } else {
           nextFactor =
               getBestNextFactor(
+                  mq,
                   multiJoin,
                   factorsToAdd,
                   factorsAdded,
@@ -718,6 +722,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
       factorsNeeded.and(factorsAdded);
       joinTree =
           addFactorToTree(
+              mq,
               relBuilder,
               multiJoin,
               semiJoinOpt,
@@ -751,6 +756,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
    * @return index of the best factor to add next
    */
   private int getBestNextFactor(
+      RelMetadataQuery mq,
       LoptMultiJoin multiJoin,
       BitSet factorsToAdd,
       BitSet factorsAdded,
@@ -801,6 +807,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
           && ((dimWeight > bestWeight) || (dimWeight == bestWeight))) {
         cardinality =
             computeJoinCardinality(
+              mq,
                 multiJoin,
                 semiJoinOpt,
                 joinTree,
@@ -860,6 +867,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
    * add the factor; otherwise, null is returned
    */
   private LoptJoinTree addFactorToTree(
+      RelMetadataQuery mq,
       RelBuilder relBuilder,
       LoptMultiJoin multiJoin,
       LoptSemiJoinOptimizer semiJoinOpt,
@@ -868,7 +876,6 @@ public class LoptOptimizeJoinRule extends RelOptRule {
       BitSet factorsNeeded,
       List<RexNode> filtersToAdd,
       boolean selfJoin) {
-    final RelMetadataQuery mq = semiJoinOpt.mq;
 
     // if the factor corresponds to the null generating factor in an outer
     // join that can be removed, then create a replacement join
@@ -914,6 +921,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
     final List<RexNode> tmpFilters = new ArrayList<>(filtersToAdd);
     LoptJoinTree topTree =
         addToTop(
+            mq,
             relBuilder,
             multiJoin,
             semiJoinOpt,
@@ -923,6 +931,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
             selfJoin);
     LoptJoinTree pushDownTree =
         pushDownFactor(
+            mq,
             relBuilder,
             multiJoin,
             semiJoinOpt,
@@ -1013,6 +1022,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
    * returned
    */
   private LoptJoinTree pushDownFactor(
+      RelMetadataQuery mq,
       RelBuilder relBuilder,
       LoptMultiJoin multiJoin,
       LoptSemiJoinOptimizer semiJoinOpt,
@@ -1082,6 +1092,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
     LoptJoinTree subTree = (childNo == 0) ? left : right;
     subTree =
         addFactorToTree(
+            mq,
             relBuilder,
             multiJoin,
             semiJoinOpt,
@@ -1136,6 +1147,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
 
     // create the new join tree with the factor pushed down
     return createJoinSubtree(
+        mq,
         relBuilder,
         multiJoin,
         left,
@@ -1162,6 +1174,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
    * @return new join tree
    */
   private LoptJoinTree addToTop(
+      RelMetadataQuery mq,
       RelBuilder relBuilder,
       LoptMultiJoin multiJoin,
       LoptSemiJoinOptimizer semiJoinOpt,
@@ -1214,6 +1227,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
     }
 
     return createJoinSubtree(
+        mq,
         relBuilder,
         multiJoin,
         joinTree,
@@ -1428,7 +1442,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
    * left factor.
    * </ul>
    *
-   * Note that this only applies if both factors in the self-join are in the
+   * <p>Note that this only applies if both factors in the self-join are in the
    * join ordering. If they are, then the left factor always precedes the
    * right factor in the join ordering.
    *
@@ -1723,6 +1737,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
    * @return created LogicalJoin
    */
   private LoptJoinTree createJoinSubtree(
+      RelMetadataQuery mq,
       RelBuilder relBuilder,
       LoptMultiJoin multiJoin,
       LoptJoinTree left,
@@ -1736,7 +1751,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
         multiJoin.getMultiJoinRel().getCluster().getRexBuilder();
 
     // swap the inputs if beneficial
-    if (swapInputs(multiJoin, left, right, selfJoin)) {
+    if (swapInputs(mq, multiJoin, left, right, selfJoin)) {
       LoptJoinTree tmp = right;
       right = left;
       left = tmp;
@@ -1850,6 +1865,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
    * @return true if swapping should be done
    */
   private boolean swapInputs(
+      RelMetadataQuery mq,
       LoptMultiJoin multiJoin,
       LoptJoinTree left,
       LoptJoinTree right,
@@ -1861,7 +1877,6 @@ public class LoptOptimizeJoinRule extends RelOptRule {
           ((LoptJoinTree.Leaf) left.getFactorTree()).getId());
     }
 
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
     final Double leftRowCount = mq.getRowCount(left.getJoinTree());
     final Double rightRowCount = mq.getRowCount(right.getJoinTree());
 
@@ -1994,7 +2009,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
     }
 
     // Make sure the join is between the same simple factor
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = joinRel.getCluster().getMetadataQuery();
     final RelOptTable leftTable = mq.getTableOrigin(left);
     if (leftTable == null) {
       return false;

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/LoptSemiJoinOptimizer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/LoptSemiJoinOptimizer.java b/core/src/main/java/org/apache/calcite/rel/rules/LoptSemiJoinOptimizer.java
index c673df7..44c53bd 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/LoptSemiJoinOptimizer.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/LoptSemiJoinOptimizer.java
@@ -61,9 +61,7 @@ public class LoptSemiJoinOptimizer {
 
   private final RexBuilder rexBuilder;
 
-  /** Not thread-safe. But should be OK, because an optimizer is only used
-   * from within one thread.*/
-  final RelMetadataQuery mq = RelMetadataQuery.instance();
+  private final RelMetadataQuery mq;
 
   /**
    * Semijoins corresponding to each join factor, if they are going to be
@@ -85,10 +83,12 @@ public class LoptSemiJoinOptimizer {
   //~ Constructors -----------------------------------------------------------
 
   public LoptSemiJoinOptimizer(
+      RelMetadataQuery mq,
       LoptMultiJoin multiJoin,
       RexBuilder rexBuilder) {
     // there are no semijoins yet, so initialize to the original
     // factors
+    this.mq = mq;
     int nJoinFactors = multiJoin.getNumJoinFactors();
     chosenSemiJoins = new RelNode[nJoinFactors];
     for (int i = 0; i < nJoinFactors; i++) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java
index 790aed7..a482904 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java
@@ -88,7 +88,7 @@ public class MultiJoinOptimizeBushyRule extends RelOptRule {
     final MultiJoin multiJoinRel = call.rel(0);
     final RexBuilder rexBuilder = multiJoinRel.getCluster().getRexBuilder();
     final RelBuilder relBuilder = call.builder();
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = call.getMetadataQuery();
 
     final LoptMultiJoin multiJoin = new LoptMultiJoin(multiJoinRel);
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
index cc1b681..b640c82 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
@@ -143,7 +143,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
           Lists.newArrayList(filter.getCondition());
       RexNode newConditionExp;
       boolean reduced;
-      final RelMetadataQuery mq = RelMetadataQuery.instance();
+      final RelMetadataQuery mq = call.getMetadataQuery();
       final RelOptPredicateList predicates =
           mq.getPulledUpPredicates(filter.getInput());
       if (reduceExpressions(filter, expList, predicates, true)) {
@@ -259,7 +259,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
 
     @Override public void onMatch(RelOptRuleCall call) {
       final Project project = call.rel(0);
-      final RelMetadataQuery mq = RelMetadataQuery.instance();
+      final RelMetadataQuery mq = call.getMetadataQuery();
       final RelOptPredicateList predicates =
           mq.getPulledUpPredicates(project.getInput());
       final List<RexNode> expList =
@@ -291,7 +291,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
       final Join join = call.rel(0);
       final List<RexNode> expList = Lists.newArrayList(join.getCondition());
       final int fieldCount = join.getLeft().getRowType().getFieldCount();
-      final RelMetadataQuery mq = RelMetadataQuery.instance();
+      final RelMetadataQuery mq = call.getMetadataQuery();
       final RelOptPredicateList leftPredicates =
           mq.getPulledUpPredicates(join.getLeft());
       final RelOptPredicateList rightPredicates =

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/SortJoinTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SortJoinTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SortJoinTransposeRule.java
index d6bf453..86d3e5e 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/SortJoinTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/SortJoinTransposeRule.java
@@ -62,7 +62,7 @@ public class SortJoinTransposeRule extends RelOptRule {
   @Override public boolean matches(RelOptRuleCall call) {
     final Sort sort = call.rel(0);
     final Join join = call.rel(1);
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = call.getMetadataQuery();
     final JoinInfo joinInfo = JoinInfo.of(
         join.getLeft(), join.getRight(), join.getCondition());
 
@@ -117,7 +117,7 @@ public class SortJoinTransposeRule extends RelOptRule {
     // We create a new sort operator on the corresponding input
     final RelNode newLeftInput;
     final RelNode newRightInput;
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = call.getMetadataQuery();
     if (join.getJoinType() == JoinRelType.LEFT) {
       // If the input is already sorted and we are not reducing the number of tuples,
       // we bail out

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/SortUnionTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SortUnionTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SortUnionTransposeRule.java
index 26d2106..bd4df23 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/SortUnionTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/SortUnionTransposeRule.java
@@ -94,7 +94,7 @@ public class SortUnionTransposeRule extends RelOptRule {
     // Thus we use 'ret' as a flag to identify if we have finished pushing the
     // sort past a union.
     boolean ret = true;
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = call.getMetadataQuery();
     for (RelNode input : union.getInputs()) {
       if (!RelMdUtil.checkInputForCollationAndLimit(mq, input,
           sort.getCollation(), sort.offset, sort.fetch)) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
index f9315e2..bc886b6 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
@@ -157,7 +157,7 @@ public abstract class SubQueryRemoveRule extends RelOptRule {
     switch (e.getKind()) {
     case SCALAR_QUERY:
       builder.push(e.rel);
-      final RelMetadataQuery mq = RelMetadataQuery.instance();
+      final RelMetadataQuery mq = e.rel.getCluster().getMetadataQuery();
       final Boolean unique = mq.areColumnsUnique(builder.peek(),
           ImmutableBitSet.of());
       if (unique == null || !unique) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java
index 39fd9e1..05e42be 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java
@@ -68,7 +68,7 @@ public class UnionPullUpConstantsRule extends RelOptRule {
     }
 
     final RexBuilder rexBuilder = union.getCluster().getRexBuilder();
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = call.getMetadataQuery();
     final RelOptPredicateList predicates = mq.getPulledUpPredicates(union);
     if (predicates == null) {
       return;

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
index 18871e1..6e63528 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
@@ -1883,7 +1883,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
         // The join filters out the nulls.  So, it's ok if there are
         // nulls in the join keys.
-        final RelMetadataQuery mq = RelMetadataQuery.instance();
+        final RelMetadataQuery mq = call.getMetadataQuery();
         if (!RelMdUtil.areColumnsDefinitelyUniqueWhenNullsFiltered(mq, right,
             rightJoinKeys)) {
           SQL2REL_LOGGER.debug("{} are not unique keys for {}",
@@ -2097,7 +2097,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
 
         // The join filters out the nulls.  So, it's ok if there are
         // nulls in the join keys.
-        final RelMetadataQuery mq = RelMetadataQuery.instance();
+        final RelMetadataQuery mq = call.getMetadataQuery();
         if (!RelMdUtil.areColumnsDefinitelyUniqueWhenNullsFiltered(mq, left,
             correlatedInputRefJoinKeys)) {
           SQL2REL_LOGGER.debug("{} are not unique keys for {}",
@@ -2175,7 +2175,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
         // leftInput contains unique keys
         // i.e. each row is distinct and can group by on all the left
         // fields
-        final RelMetadataQuery mq = RelMetadataQuery.instance();
+        final RelMetadataQuery mq = call.getMetadataQuery();
         if (!RelMdUtil.areColumnsDefinitelyUnique(mq, left, allCols)) {
           SQL2REL_LOGGER.debug("There are no unique keys for {}", left);
           return;

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
index 853e69b..ba43ff2 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
@@ -187,7 +187,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
     final ImmutableBitSet.Builder fieldsUsedBuilder = fieldsUsed.rebuild();
 
     // Fields that define the collation cannot be discarded.
-    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelMetadataQuery mq = rel.getCluster().getMetadataQuery();
     final ImmutableList<RelCollation> collations = mq.collations(input);
     for (RelCollation collation : collations) {
       for (RelFieldCollation fieldCollation : collation.getFieldCollations()) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/56d5261a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index 2e6750d..06d8997 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -999,7 +999,7 @@ public class RelBuilder {
         ImmutableBitSet.of(registerExpressions(extraNodes, groupKey_.nodes));
   label:
     if (Iterables.isEmpty(aggCalls) && !groupKey_.indicator) {
-      final RelMetadataQuery mq = RelMetadataQuery.instance();
+      final RelMetadataQuery mq = peek().getCluster().getMetadataQuery();
       if (groupSet.isEmpty()) {
         final Double minRowCount = mq.getMinRowCount(peek());
         if (minRowCount == null || minRowCount < 1D) {