You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2016/01/23 01:30:32 UTC

[14/50] [abbrv] calcite git commit: [CALCITE-794] Detect cycles when computing statistics

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java
index d77ca13..cb1de64 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java
@@ -54,20 +54,23 @@ public class RelMdUniqueKeys {
 
   //~ Methods ----------------------------------------------------------------
 
-  public Set<ImmutableBitSet> getUniqueKeys(Filter rel, boolean ignoreNulls) {
-    return RelMetadataQuery.getUniqueKeys(rel.getInput(), ignoreNulls);
+  public Set<ImmutableBitSet> getUniqueKeys(Filter rel, RelMetadataQuery mq,
+      boolean ignoreNulls) {
+    return mq.getUniqueKeys(rel.getInput(), ignoreNulls);
   }
 
-  public Set<ImmutableBitSet> getUniqueKeys(Sort rel, boolean ignoreNulls) {
-    return RelMetadataQuery.getUniqueKeys(rel.getInput(), ignoreNulls);
+  public Set<ImmutableBitSet> getUniqueKeys(Sort rel, RelMetadataQuery mq,
+      boolean ignoreNulls) {
+    return mq.getUniqueKeys(rel.getInput(), ignoreNulls);
   }
 
-  public Set<ImmutableBitSet> getUniqueKeys(Correlate rel,
+  public Set<ImmutableBitSet> getUniqueKeys(Correlate rel, RelMetadataQuery mq,
       boolean ignoreNulls) {
-    return RelMetadataQuery.getUniqueKeys(rel.getLeft(), ignoreNulls);
+    return mq.getUniqueKeys(rel.getLeft(), ignoreNulls);
   }
 
-  public Set<ImmutableBitSet> getUniqueKeys(Project rel, boolean ignoreNulls) {
+  public Set<ImmutableBitSet> getUniqueKeys(Project rel, RelMetadataQuery mq,
+      boolean ignoreNulls) {
     // LogicalProject maps a set of rows to a different set;
     // Without knowledge of the mapping function(whether it
     // preserves uniqueness), it is only safe to derive uniqueness
@@ -75,11 +78,9 @@ public class RelMdUniqueKeys {
     //
     // Further more, the unique bitset coming from the child needs
     // to be mapped to match the output of the project.
-    Map<Integer, Integer> mapInToOutPos = new HashMap<>();
-
-    List<RexNode> projExprs = rel.getProjects();
-
-    Set<ImmutableBitSet> projUniqueKeySet = new HashSet<>();
+    final Map<Integer, Integer> mapInToOutPos = new HashMap<>();
+    final List<RexNode> projExprs = rel.getProjects();
+    final Set<ImmutableBitSet> projUniqueKeySet = new HashSet<>();
 
     // Build an input to output position map.
     for (int i = 0; i < projExprs.size(); i++) {
@@ -96,7 +97,7 @@ public class RelMdUniqueKeys {
     }
 
     Set<ImmutableBitSet> childUniqueKeySet =
-        RelMetadataQuery.getUniqueKeys(rel.getInput(), ignoreNulls);
+        mq.getUniqueKeys(rel.getInput(), ignoreNulls);
 
     if (childUniqueKeySet != null) {
       // Now add to the projUniqueKeySet the child keys that are fully
@@ -123,7 +124,8 @@ public class RelMdUniqueKeys {
     return projUniqueKeySet;
   }
 
-  public Set<ImmutableBitSet> getUniqueKeys(Join rel, boolean ignoreNulls) {
+  public Set<ImmutableBitSet> getUniqueKeys(Join rel, RelMetadataQuery mq,
+      boolean ignoreNulls) {
     final RelNode left = rel.getLeft();
     final RelNode right = rel.getRight();
 
@@ -136,13 +138,11 @@ public class RelMdUniqueKeys {
     // that is undesirable, use RelMetadataQuery.areColumnsUnique() as
     // an alternative way of getting unique key information.
 
-    Set<ImmutableBitSet> retSet = new HashSet<>();
-    Set<ImmutableBitSet> leftSet =
-        RelMetadataQuery.getUniqueKeys(left, ignoreNulls);
+    final Set<ImmutableBitSet> retSet = new HashSet<>();
+    final Set<ImmutableBitSet> leftSet = mq.getUniqueKeys(left, ignoreNulls);
     Set<ImmutableBitSet> rightSet = null;
 
-    Set<ImmutableBitSet> tmpRightSet =
-        RelMetadataQuery.getUniqueKeys(right, ignoreNulls);
+    final Set<ImmutableBitSet> tmpRightSet = mq.getUniqueKeys(right, ignoreNulls);
     int nFieldsOnLeft = left.getRowType().getFieldCount();
 
     if (tmpRightSet != null) {
@@ -169,12 +169,10 @@ public class RelMdUniqueKeys {
 
     // determine if either or both the LHS and RHS are unique on the
     // equijoin columns
-    Boolean leftUnique =
-        RelMetadataQuery.areColumnsUnique(left, joinInfo.leftSet(),
-            ignoreNulls);
-    Boolean rightUnique =
-        RelMetadataQuery.areColumnsUnique(right, joinInfo.rightSet(),
-            ignoreNulls);
+    final Boolean leftUnique =
+        mq.areColumnsUnique(left, joinInfo.leftSet(), ignoreNulls);
+    final Boolean rightUnique =
+        mq.areColumnsUnique(right, joinInfo.rightSet(), ignoreNulls);
 
     // if the right hand side is unique on its equijoin columns, then we can
     // add the unique keys from left if the left hand side is not null
@@ -197,19 +195,20 @@ public class RelMdUniqueKeys {
     return retSet;
   }
 
-  public Set<ImmutableBitSet> getUniqueKeys(SemiJoin rel, boolean ignoreNulls) {
+  public Set<ImmutableBitSet> getUniqueKeys(SemiJoin rel, RelMetadataQuery mq,
+      boolean ignoreNulls) {
     // only return the unique keys from the LHS since a semijoin only
     // returns the LHS
-    return RelMetadataQuery.getUniqueKeys(rel.getLeft(), ignoreNulls);
+    return mq.getUniqueKeys(rel.getLeft(), ignoreNulls);
   }
 
-  public Set<ImmutableBitSet> getUniqueKeys(Aggregate rel,
+  public Set<ImmutableBitSet> getUniqueKeys(Aggregate rel, RelMetadataQuery mq,
       boolean ignoreNulls) {
     // group by keys form a unique key
     return ImmutableSet.of(rel.getGroupSet());
   }
 
-  public Set<ImmutableBitSet> getUniqueKeys(SetOp rel,
+  public Set<ImmutableBitSet> getUniqueKeys(SetOp rel, RelMetadataQuery mq,
       boolean ignoreNulls) {
     if (!rel.all) {
       return ImmutableSet.of(
@@ -219,7 +218,8 @@ public class RelMdUniqueKeys {
   }
 
   // Catch-all rule when none of the others apply.
-  public Set<ImmutableBitSet> getUniqueKeys(RelNode rel, boolean ignoreNulls) {
+  public Set<ImmutableBitSet> getUniqueKeys(RelNode rel, RelMetadataQuery mq,
+      boolean ignoreNulls) {
     // no information available
     return null;
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
index dc1ea3e..5435979 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
@@ -26,11 +26,14 @@ import org.apache.calcite.rel.core.JoinRelType;
 import org.apache.calcite.rel.core.Minus;
 import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.core.SemiJoin;
+import org.apache.calcite.rel.core.Union;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexCall;
 import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexLocalRef;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexProgram;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.rex.RexVisitorImpl;
 import org.apache.calcite.sql.SqlFunction;
@@ -77,13 +80,11 @@ public class RelMdUtil {
    * @param rel the semijoin of interest
    * @return constructed rexnode
    */
-  public static RexNode makeSemiJoinSelectivityRexNode(SemiJoin rel) {
+  public static RexNode makeSemiJoinSelectivityRexNode(RelMetadataQuery mq,
+      SemiJoin rel) {
     RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
     double selectivity =
-        computeSemiJoinSelectivity(
-            rel.getLeft(),
-            rel.getRight(),
-            rel);
+        computeSemiJoinSelectivity(mq, rel.getLeft(), rel.getRight(), rel);
     RexNode selec =
         rexBuilder.makeApproxLiteral(new BigDecimal(selectivity));
     return rexBuilder.makeCall(ARTIFICIAL_SELECTIVITY_FUNC, selec);
@@ -113,12 +114,10 @@ public class RelMdUtil {
    * @param rel semijoin rel
    * @return calculated selectivity
    */
-  public static double computeSemiJoinSelectivity(SemiJoin rel) {
-    return computeSemiJoinSelectivity(
-        rel.getLeft(),
-        rel.getRight(),
-        rel.getLeftKeys(),
-        rel.getRightKeys());
+  public static double computeSemiJoinSelectivity(RelMetadataQuery mq,
+      SemiJoin rel) {
+    return computeSemiJoinSelectivity(mq, rel.getLeft(), rel.getRight(),
+        rel.getLeftKeys(), rel.getRightKeys());
   }
 
   /**
@@ -132,14 +131,9 @@ public class RelMdUtil {
    * @param rel     semijoin rel
    * @return calculated selectivity
    */
-  public static double computeSemiJoinSelectivity(
-      RelNode factRel,
-      RelNode dimRel,
-      SemiJoin rel) {
-    return computeSemiJoinSelectivity(
-        factRel,
-        dimRel,
-        rel.getLeftKeys(),
+  public static double computeSemiJoinSelectivity(RelMetadataQuery mq,
+      RelNode factRel, RelNode dimRel, SemiJoin rel) {
+    return computeSemiJoinSelectivity(mq, factRel, dimRel, rel.getLeftKeys(),
         rel.getRightKeys());
   }
 
@@ -155,10 +149,8 @@ public class RelMdUtil {
    * @param dimKeyList  RHS keys used in the filter
    * @return calculated selectivity
    */
-  public static double computeSemiJoinSelectivity(
-      RelNode factRel,
-      RelNode dimRel,
-      List<Integer> factKeyList,
+  public static double computeSemiJoinSelectivity(RelMetadataQuery mq,
+      RelNode factRel, RelNode dimRel, List<Integer> factKeyList,
       List<Integer> dimKeyList) {
     ImmutableBitSet.Builder factKeys = ImmutableBitSet.builder();
     for (int factCol : factKeyList) {
@@ -170,20 +162,19 @@ public class RelMdUtil {
     }
     final ImmutableBitSet dimKeys = dimKeyBuilder.build();
 
-    Double factPop =
-        RelMetadataQuery.getPopulationSize(factRel, factKeys.build());
+    Double factPop = mq.getPopulationSize(factRel, factKeys.build());
     if (factPop == null) {
       // use the dimension population if the fact population is
       // unavailable; since we're filtering the fact table, that's
       // the population we ideally want to use
-      factPop = RelMetadataQuery.getPopulationSize(dimRel, dimKeys);
+      factPop = mq.getPopulationSize(dimRel, dimKeys);
     }
 
     // if cardinality and population are available, use them; otherwise
     // use percentage original rows
     Double selectivity;
     Double dimCard =
-        RelMetadataQuery.getDistinctRowCount(
+        mq.getDistinctRowCount(
             dimRel,
             dimKeys,
             null);
@@ -194,7 +185,7 @@ public class RelMdUtil {
       }
       selectivity = dimCard / factPop;
     } else {
-      selectivity = RelMetadataQuery.getPercentageOriginalRows(dimRel);
+      selectivity = mq.getPercentageOriginalRows(dimRel);
     }
 
     if (selectivity == null) {
@@ -220,26 +211,24 @@ public class RelMdUtil {
    * @return true if bit mask represents a unique column set; false if not (or
    * if no metadata is available)
    */
-  public static boolean areColumnsDefinitelyUnique(
-      RelNode rel,
-      ImmutableBitSet colMask) {
-    Boolean b = RelMetadataQuery.areColumnsUnique(rel, colMask, false);
+  public static boolean areColumnsDefinitelyUnique(RelMetadataQuery mq,
+      RelNode rel, ImmutableBitSet colMask) {
+    Boolean b = mq.areColumnsUnique(rel, colMask, false);
     return b != null && b;
   }
 
-  public static Boolean areColumnsUnique(
-      RelNode rel,
+  public static Boolean areColumnsUnique(RelMetadataQuery mq, RelNode rel,
       List<RexInputRef> columnRefs) {
     ImmutableBitSet.Builder colMask = ImmutableBitSet.builder();
     for (RexInputRef columnRef : columnRefs) {
       colMask.set(columnRef.getIndex());
     }
-    return RelMetadataQuery.areColumnsUnique(rel, colMask.build());
+    return mq.areColumnsUnique(rel, colMask.build());
   }
 
-  public static boolean areColumnsDefinitelyUnique(RelNode rel,
-      List<RexInputRef> columnRefs) {
-    Boolean b = areColumnsUnique(rel, columnRefs);
+  public static boolean areColumnsDefinitelyUnique(RelMetadataQuery mq,
+      RelNode rel, List<RexInputRef> columnRefs) {
+    Boolean b = areColumnsUnique(mq, rel, columnRefs);
     return b != null && b;
   }
 
@@ -255,31 +244,29 @@ public class RelMdUtil {
    * @return true if bit mask represents a unique column set; false if not (or
    * if no metadata is available)
    */
-  public static boolean areColumnsDefinitelyUniqueWhenNullsFiltered(RelNode rel,
-      ImmutableBitSet colMask) {
-    Boolean b = RelMetadataQuery.areColumnsUnique(rel, colMask, true);
+  public static boolean areColumnsDefinitelyUniqueWhenNullsFiltered(
+      RelMetadataQuery mq, RelNode rel, ImmutableBitSet colMask) {
+    Boolean b = mq.areColumnsUnique(rel, colMask, true);
     if (b == null) {
       return false;
     }
     return b;
   }
 
-  public static Boolean areColumnsUniqueWhenNullsFiltered(
-      RelNode rel,
-      List<RexInputRef> columnRefs) {
+  public static Boolean areColumnsUniqueWhenNullsFiltered(RelMetadataQuery mq,
+      RelNode rel, List<RexInputRef> columnRefs) {
     ImmutableBitSet.Builder colMask = ImmutableBitSet.builder();
 
     for (RexInputRef columnRef : columnRefs) {
       colMask.set(columnRef.getIndex());
     }
 
-    return RelMetadataQuery.areColumnsUnique(rel, colMask.build(), true);
+    return mq.areColumnsUnique(rel, colMask.build(), true);
   }
 
   public static boolean areColumnsDefinitelyUniqueWhenNullsFiltered(
-      RelNode rel,
-      List<RexInputRef> columnRefs) {
-    Boolean b = areColumnsUniqueWhenNullsFiltered(rel, columnRefs);
+      RelMetadataQuery mq, RelNode rel, List<RexInputRef> columnRefs) {
+    Boolean b = areColumnsUniqueWhenNullsFiltered(mq, rel, columnRefs);
     if (b == null) {
       return false;
     }
@@ -476,9 +463,9 @@ public class RelMdUtil {
       RexBuilder rexBuilder,
       RexNode pred1,
       RexNode pred2) {
-    List<RexNode> list1 = RelOptUtil.conjunctions(pred1);
-    List<RexNode> list2 = RelOptUtil.conjunctions(pred2);
-    List<RexNode> minusList = new ArrayList<>();
+    final List<RexNode> list1 = RelOptUtil.conjunctions(pred1);
+    final List<RexNode> list2 = RelOptUtil.conjunctions(pred2);
+    final List<RexNode> minusList = new ArrayList<>();
 
     for (RexNode rex1 : list1) {
       boolean add = true;
@@ -550,14 +537,15 @@ public class RelMdUtil {
 
   /**
    * Computes the cardinality of a particular expression from the projection
-   * list
+   * list.
    *
    * @param rel  RelNode corresponding to the project
    * @param expr projection expression
    * @return cardinality
    */
-  public static Double cardOfProjExpr(Project rel, RexNode expr) {
-    return expr.accept(new CardOfProjExpr(rel));
+  public static Double cardOfProjExpr(RelMetadataQuery mq, Project rel,
+      RexNode expr) {
+    return expr.accept(new CardOfProjExpr(mq, rel));
   }
 
   /**
@@ -567,9 +555,8 @@ public class RelMdUtil {
    * @param groupKey keys to compute the population for
    * @return computed population size
    */
-  public static Double getJoinPopulationSize(
-      RelNode joinRel,
-      ImmutableBitSet groupKey) {
+  public static Double getJoinPopulationSize(RelMetadataQuery mq,
+      RelNode joinRel, ImmutableBitSet groupKey) {
     ImmutableBitSet.Builder leftMask = ImmutableBitSet.builder();
     ImmutableBitSet.Builder rightMask = ImmutableBitSet.builder();
     RelNode left = joinRel.getInputs().get(0);
@@ -577,23 +564,14 @@ public class RelMdUtil {
 
     // separate the mask into masks for the left and right
     RelMdUtil.setLeftRightBitmaps(
-        groupKey,
-        leftMask,
-        rightMask,
-        left.getRowType().getFieldCount());
+        groupKey, leftMask, rightMask, left.getRowType().getFieldCount());
 
     Double population =
         NumberUtil.multiply(
-            RelMetadataQuery.getPopulationSize(
-                left,
-                leftMask.build()),
-            RelMetadataQuery.getPopulationSize(
-                right,
-                rightMask.build()));
+            mq.getPopulationSize(left, leftMask.build()),
+            mq.getPopulationSize(right, rightMask.build()));
 
-    return RelMdUtil.numDistinctVals(
-        population,
-        RelMetadataQuery.getRowCount(joinRel));
+    return numDistinctVals(population, mq.getRowCount(joinRel));
   }
 
   /**
@@ -608,12 +586,9 @@ public class RelMdUtil {
    *                  otherwise use <code>left NDV * right NDV</code>.
    * @return number of distinct rows
    */
-  public static Double getJoinDistinctRowCount(
-      RelNode joinRel,
-      JoinRelType joinType,
-      ImmutableBitSet groupKey,
-      RexNode predicate,
-      boolean useMaxNdv) {
+  public static Double getJoinDistinctRowCount(RelMetadataQuery mq,
+      RelNode joinRel, JoinRelType joinType, ImmutableBitSet groupKey,
+      RexNode predicate, boolean useMaxNdv) {
     Double distRowCount;
     ImmutableBitSet.Builder leftMask = ImmutableBitSet.builder();
     ImmutableBitSet.Builder rightMask = ImmutableBitSet.builder();
@@ -630,10 +605,10 @@ public class RelMdUtil {
     RexNode leftPred = null;
     RexNode rightPred = null;
     if (predicate != null) {
-      List<RexNode> leftFilters = new ArrayList<>();
-      List<RexNode> rightFilters = new ArrayList<>();
-      List<RexNode> joinFilters = new ArrayList<>();
-      List<RexNode> predList = RelOptUtil.conjunctions(predicate);
+      final List<RexNode> leftFilters = new ArrayList<>();
+      final List<RexNode> rightFilters = new ArrayList<>();
+      final List<RexNode> joinFilters = new ArrayList<>();
+      final List<RexNode> predList = RelOptUtil.conjunctions(predicate);
 
       RelOptUtil.classifyFilters(
           joinRel,
@@ -655,35 +630,35 @@ public class RelMdUtil {
 
     if (useMaxNdv) {
       distRowCount = Math.max(
-          RelMetadataQuery.getDistinctRowCount(left, leftMask.build(),
-              leftPred),
-          RelMetadataQuery.getDistinctRowCount(right, rightMask.build(),
-              rightPred));
+          mq.getDistinctRowCount(left, leftMask.build(), leftPred),
+          mq.getDistinctRowCount(right, rightMask.build(), rightPred));
     } else {
       distRowCount =
         NumberUtil.multiply(
-            RelMetadataQuery.getDistinctRowCount(
-                left,
-                leftMask.build(),
-                leftPred),
-            RelMetadataQuery.getDistinctRowCount(
-                right,
-                rightMask.build(),
-                rightPred));
+            mq.getDistinctRowCount(left, leftMask.build(), leftPred),
+            mq.getDistinctRowCount(right, rightMask.build(), rightPred));
     }
 
-    return RelMdUtil.numDistinctVals(
-        distRowCount,
-        RelMetadataQuery.getRowCount(joinRel));
+    return RelMdUtil.numDistinctVals(distRowCount, mq.getRowCount(joinRel));
+  }
+
+  /** Returns an estimate of the number of rows returned by a {@link Union}
+   * (before duplicates are eliminated). */
+  public static double getUnionAllRowCount(RelMetadataQuery mq, Union rel) {
+    double rowCount = 0;
+    for (RelNode input : rel.getInputs()) {
+      rowCount += mq.getRowCount(input);
+    }
+    return rowCount;
   }
 
   /** Returns an estimate of the number of rows returned by a {@link Minus}. */
-  public static double getMinusRowCount(Minus minus) {
+  public static double getMinusRowCount(RelMetadataQuery mq, Minus minus) {
     // REVIEW jvs 30-May-2005:  I just pulled this out of a hat.
     final List<RelNode> inputs = minus.getInputs();
-    double dRows = RelMetadataQuery.getRowCount(inputs.get(0));
+    double dRows = mq.getRowCount(inputs.get(0));
     for (int i = 1; i < inputs.size(); i++) {
-      dRows -= 0.5 * RelMetadataQuery.getRowCount(inputs.get(i));
+      dRows -= 0.5 * mq.getRowCount(inputs.get(i));
     }
     if (dRows < 0) {
       dRows = 0;
@@ -692,16 +667,17 @@ public class RelMdUtil {
   }
 
   /** Returns an estimate of the number of rows returned by a {@link Join}. */
-  public static Double getJoinRowCount(Join join, RexNode condition) {
+  public static Double getJoinRowCount(RelMetadataQuery mq, Join join,
+      RexNode condition) {
     // Row count estimates of 0 will be rounded up to 1.
     // So, use maxRowCount where the product is very small.
-    final Double left = RelMetadataQuery.getRowCount(join.getLeft());
-    final Double right = RelMetadataQuery.getRowCount(join.getRight());
+    final Double left = mq.getRowCount(join.getLeft());
+    final Double right = mq.getRowCount(join.getRight());
     if (left == null || right == null) {
       return null;
     }
     if (left <= 1D || right <= 1D) {
-      Double max = RelMetadataQuery.getMaxRowCount(join);
+      Double max = mq.getMaxRowCount(join);
       if (max != null && max <= 1D) {
         return max;
       }
@@ -709,30 +685,51 @@ public class RelMdUtil {
     double product = left * right;
 
     // TODO:  correlation factor
-    return product * RelMetadataQuery.getSelectivity(join, condition);
+    return product * mq.getSelectivity(join, condition);
   }
 
   /** Returns an estimate of the number of rows returned by a
    * {@link SemiJoin}. */
-  public static Double getSemiJoinRowCount(RelNode left, RelNode right,
-      JoinRelType joinType, RexNode condition) {
+  public static Double getSemiJoinRowCount(RelMetadataQuery mq, RelNode left,
+      RelNode right, JoinRelType joinType, RexNode condition) {
     // TODO:  correlation factor
-    final Double leftCount = RelMetadataQuery.getRowCount(left);
+    final Double leftCount = mq.getRowCount(left);
     if (leftCount == null) {
       return null;
     }
     return leftCount * RexUtil.getSelectivity(condition);
   }
 
+  public static double estimateFilteredRows(RelNode child, RexProgram program,
+      RelMetadataQuery mq) {
+    // convert the program's RexLocalRef condition to an expanded RexNode
+    RexLocalRef programCondition = program.getCondition();
+    RexNode condition;
+    if (programCondition == null) {
+      condition = null;
+    } else {
+      condition = program.expandLocalRef(programCondition);
+    }
+    return estimateFilteredRows(child, condition, mq);
+  }
+
+  public static double estimateFilteredRows(RelNode child, RexNode condition,
+      RelMetadataQuery mq) {
+    return mq.getRowCount(child)
+        * mq.getSelectivity(child, condition);
+  }
+
   //~ Inner Classes ----------------------------------------------------------
 
   /** Visitor that walks over a scalar expression and computes the
    * cardinality of its result. */
   private static class CardOfProjExpr extends RexVisitorImpl<Double> {
+    private final RelMetadataQuery mq;
     private Project rel;
 
-    public CardOfProjExpr(Project rel) {
+    public CardOfProjExpr(RelMetadataQuery mq, Project rel) {
       super(true);
+      this.mq = mq;
       this.rel = rel;
     }
 
@@ -740,57 +737,45 @@ public class RelMdUtil {
       int index = var.getIndex();
       ImmutableBitSet col = ImmutableBitSet.of(index);
       Double distinctRowCount =
-          RelMetadataQuery.getDistinctRowCount(
-              rel.getInput(),
-              col,
-              null);
+          mq.getDistinctRowCount(rel.getInput(), col, null);
       if (distinctRowCount == null) {
         return null;
       } else {
-        return RelMdUtil.numDistinctVals(
-            distinctRowCount,
-            RelMetadataQuery.getRowCount(rel));
+        return numDistinctVals(distinctRowCount, mq.getRowCount(rel));
       }
     }
 
     public Double visitLiteral(RexLiteral literal) {
-      return RelMdUtil.numDistinctVals(
-          1.0,
-          RelMetadataQuery.getRowCount(rel));
+      return numDistinctVals(1.0, mq.getRowCount(rel));
     }
 
     public Double visitCall(RexCall call) {
       Double distinctRowCount;
-      Double rowCount = RelMetadataQuery.getRowCount(rel);
+      Double rowCount = mq.getRowCount(rel);
       if (call.isA(SqlKind.MINUS_PREFIX)) {
-        distinctRowCount =
-            cardOfProjExpr(rel, call.getOperands().get(0));
+        distinctRowCount = cardOfProjExpr(mq, rel, call.getOperands().get(0));
       } else if (call.isA(ImmutableList.of(SqlKind.PLUS, SqlKind.MINUS))) {
-        Double card0 = cardOfProjExpr(rel, call.getOperands().get(0));
+        Double card0 = cardOfProjExpr(mq, rel, call.getOperands().get(0));
         if (card0 == null) {
           return null;
         }
-        Double card1 = cardOfProjExpr(rel, call.getOperands().get(1));
+        Double card1 = cardOfProjExpr(mq, rel, call.getOperands().get(1));
         if (card1 == null) {
           return null;
         }
         distinctRowCount = Math.max(card0, card1);
-      } else if (call.isA(
-          ImmutableList.of(SqlKind.TIMES, SqlKind.DIVIDE))) {
+      } else if (call.isA(ImmutableList.of(SqlKind.TIMES, SqlKind.DIVIDE))) {
         distinctRowCount =
             NumberUtil.multiply(
-                cardOfProjExpr(rel, call.getOperands().get(0)),
-                cardOfProjExpr(rel, call.getOperands().get(1)));
+                cardOfProjExpr(mq, rel, call.getOperands().get(0)),
+                cardOfProjExpr(mq, rel, call.getOperands().get(1)));
 
         // TODO zfong 6/21/06 - Broadbase has code to handle date
         // functions like year, month, day; E.g., cardinality of Month()
         // is 12
       } else {
         if (call.getOperands().size() == 1) {
-          distinctRowCount =
-              cardOfProjExpr(
-                  rel,
-                  call.getOperands().get(0));
+          distinctRowCount = cardOfProjExpr(mq, rel, call.getOperands().get(0));
         } else {
           distinctRowCount = rowCount / 10;
         }
@@ -805,13 +790,11 @@ public class RelMdUtil {
    *
    * <p>If this is the case, it is safe to push down a
    * {@link org.apache.calcite.rel.core.Sort} with limit and optional offset. */
-  public static boolean checkInputForCollationAndLimit(RelNode input,
-      RelCollation collation, RexNode offset, RexNode fetch) {
+  public static boolean checkInputForCollationAndLimit(RelMetadataQuery mq,
+      RelNode input, RelCollation collation, RexNode offset, RexNode fetch) {
     // Check if the input is already sorted
-    ImmutableList<RelCollation> inputCollations =
-        RelMetadataQuery.collations(input);
     boolean alreadySorted = false;
-    for (RelCollation inputCollation : inputCollations) {
+    for (RelCollation inputCollation : mq.collations(input)) {
       if (inputCollation.satisfies(collation)) {
         alreadySorted = true;
         break;
@@ -819,7 +802,7 @@ public class RelMdUtil {
     }
     // Check if we are not reducing the number of tuples
     boolean alreadySmaller = true;
-    final Double rowCount = RelMetadataQuery.getMaxRowCount(input);
+    final Double rowCount = mq.getMaxRowCount(input);
     if (rowCount != null && fetch != null) {
       final int offsetVal = offset == null ? 0 : RexLiteral.intValue(offset);
       final int limit = RexLiteral.intValue(fetch);

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java
index 0e1bf4f..a2fd272 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java
@@ -18,8 +18,6 @@ package org.apache.calcite.rel.metadata;
 
 import org.apache.calcite.rel.RelNode;
 
-import com.google.common.base.Function;
-
 /**
  * RelMetadataProvider defines an interface for obtaining metadata about
  * relational expressions. This interface is weakly-typed and is not intended to
@@ -56,9 +54,9 @@ public interface RelMetadataProvider {
    * @return Function that will field a metadata instance; or null if this
    *     provider cannot supply metadata of this type
    */
-  Function<RelNode, Metadata> apply(
-      Class<? extends RelNode> relClass,
-      Class<? extends Metadata> metadataClass);
+  <M extends Metadata> UnboundMetadata<M>
+  apply(Class<? extends RelNode> relClass,
+      Class<? extends M> metadataClass);
 }
 
 // End RelMetadataProvider.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
index 44d0724..f4eb748 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
@@ -30,6 +30,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -64,14 +65,26 @@ import java.util.Set;
  * custom providers for the standard queries in order to handle additional
  * relational expressions (either logical or physical). In either case, the
  * process is the same: write a reflective provider and chain it on to an
- * instance of {@link DefaultRelMetadataProvider}, prepending it to the default
+ * instance of {@link DefaultRelMetadataProvider}, pre-pending it to the default
  * providers. Then supply that instance to the planner via the appropriate
  * plugin mechanism.
  */
 public abstract class RelMetadataQuery {
+  /** Set of active metadata queries. */
+  public final Set<List> set = new HashSet<>();
+
   //~ Methods ----------------------------------------------------------------
 
   /**
+   * Returns an instance of RelMetadataQuery. It ensures that cycles do not
+   * occur while computing metadata.
+   */
+  public static RelMetadataQuery instance() {
+    return new RelMetadataQuery() {
+    };
+  }
+
+  /**
    * Returns the
    * {@link BuiltInMetadata.RowCount#getRowCount()}
    * statistic.
@@ -80,9 +93,9 @@ public abstract class RelMetadataQuery {
    * @return estimated row count, or null if no reliable estimate can be
    * determined
    */
-  public static Double getRowCount(RelNode rel) {
+  public Double getRowCount(RelNode rel) {
     final BuiltInMetadata.RowCount metadata =
-        rel.metadata(BuiltInMetadata.RowCount.class);
+        rel.metadata(BuiltInMetadata.RowCount.class, this);
     Double result = metadata.getRowCount();
     return validateResult(result);
   }
@@ -95,9 +108,9 @@ public abstract class RelMetadataQuery {
    * @param rel the relational expression
    * @return max row count
    */
-  public static Double getMaxRowCount(RelNode rel) {
+  public Double getMaxRowCount(RelNode rel) {
     final BuiltInMetadata.MaxRowCount metadata =
-        rel.metadata(BuiltInMetadata.MaxRowCount.class);
+        rel.metadata(BuiltInMetadata.MaxRowCount.class, this);
     return metadata.getMaxRowCount();
   }
 
@@ -109,9 +122,9 @@ public abstract class RelMetadataQuery {
    * @param rel the relational expression
    * @return estimated cost, or null if no reliable estimate can be determined
    */
-  public static RelOptCost getCumulativeCost(RelNode rel) {
+  public RelOptCost getCumulativeCost(RelNode rel) {
     final BuiltInMetadata.CumulativeCost metadata =
-        rel.metadata(BuiltInMetadata.CumulativeCost.class);
+        rel.metadata(BuiltInMetadata.CumulativeCost.class, this);
     return metadata.getCumulativeCost();
   }
 
@@ -123,9 +136,9 @@ public abstract class RelMetadataQuery {
    * @param rel the relational expression
    * @return estimated cost, or null if no reliable estimate can be determined
    */
-  public static RelOptCost getNonCumulativeCost(RelNode rel) {
+  public RelOptCost getNonCumulativeCost(RelNode rel) {
     final BuiltInMetadata.NonCumulativeCost metadata =
-        rel.metadata(BuiltInMetadata.NonCumulativeCost.class);
+        rel.metadata(BuiltInMetadata.NonCumulativeCost.class, this);
     return metadata.getNonCumulativeCost();
   }
 
@@ -138,9 +151,9 @@ public abstract class RelMetadataQuery {
    * @return estimated percentage (between 0.0 and 1.0), or null if no
    * reliable estimate can be determined
    */
-  public static Double getPercentageOriginalRows(RelNode rel) {
+  public Double getPercentageOriginalRows(RelNode rel) {
     final BuiltInMetadata.PercentageOriginalRows metadata =
-        rel.metadata(BuiltInMetadata.PercentageOriginalRows.class);
+        rel.metadata(BuiltInMetadata.PercentageOriginalRows.class, this);
     Double result = metadata.getPercentageOriginalRows();
     assert isPercentage(result, true);
     return result;
@@ -157,9 +170,9 @@ public abstract class RelMetadataQuery {
    * determined (whereas empty set indicates definitely no origin columns at
    * all)
    */
-  public static Set<RelColumnOrigin> getColumnOrigins(RelNode rel, int column) {
+  public Set<RelColumnOrigin> getColumnOrigins(RelNode rel, int column) {
     final BuiltInMetadata.ColumnOrigin metadata =
-        rel.metadata(BuiltInMetadata.ColumnOrigin.class);
+        rel.metadata(BuiltInMetadata.ColumnOrigin.class, this);
     return metadata.getColumnOrigins(column);
   }
 
@@ -176,7 +189,7 @@ public abstract class RelMetadataQuery {
    * @return the origin of a column provided it's a simple column; otherwise,
    * returns null
    */
-  public static RelColumnOrigin getColumnOrigin(RelNode rel, int column) {
+  public RelColumnOrigin getColumnOrigin(RelNode rel, int column) {
     final Set<RelColumnOrigin> origins = getColumnOrigins(rel, column);
     if (origins == null || origins.size() != 1) {
       return null;
@@ -193,12 +206,11 @@ public abstract class RelMetadataQuery {
    *
    * @return the table, if the RelNode is a simple table; otherwise null
    */
-  public static RelOptTable getTableOrigin(RelNode rel) {
+  public RelOptTable getTableOrigin(RelNode rel) {
     // Determine the simple origin of the first column in the
     // RelNode.  If it's simple, then that means that the underlying
     // table is also simple, even if the column itself is derived.
-    final Set<RelColumnOrigin> colOrigins =
-        getColumnOrigins(rel, 0);
+    final Set<RelColumnOrigin> colOrigins = getColumnOrigins(rel, 0);
     if (colOrigins == null || colOrigins.size() == 0) {
       return null;
     }
@@ -212,13 +224,13 @@ public abstract class RelMetadataQuery {
    *
    * @param rel       the relational expression
    * @param predicate predicate whose selectivity is to be estimated against
-   *                  rel's output
+   *                  {@code rel}'s output
    * @return estimated selectivity (between 0.0 and 1.0), or null if no
    * reliable estimate can be determined
    */
-  public static Double getSelectivity(RelNode rel, RexNode predicate) {
+  public Double getSelectivity(RelNode rel, RexNode predicate) {
     final BuiltInMetadata.Selectivity metadata =
-        rel.metadata(BuiltInMetadata.Selectivity.class);
+        rel.metadata(BuiltInMetadata.Selectivity.class, this);
     Double result = metadata.getSelectivity(predicate);
     assert isPercentage(result, true);
     return result;
@@ -233,9 +245,9 @@ public abstract class RelMetadataQuery {
    * @return set of keys, or null if this information cannot be determined
    * (whereas empty set indicates definitely no keys at all)
    */
-  public static Set<ImmutableBitSet> getUniqueKeys(RelNode rel) {
+  public Set<ImmutableBitSet> getUniqueKeys(RelNode rel) {
     final BuiltInMetadata.UniqueKeys metadata =
-        rel.metadata(BuiltInMetadata.UniqueKeys.class);
+        rel.metadata(BuiltInMetadata.UniqueKeys.class, this);
     return metadata.getUniqueKeys(false);
   }
 
@@ -247,13 +259,14 @@ public abstract class RelMetadataQuery {
    * @param rel         the relational expression
    * @param ignoreNulls if true, ignore null values when determining
    *                    whether the keys are unique
+   *
    * @return set of keys, or null if this information cannot be determined
    * (whereas empty set indicates definitely no keys at all)
    */
-  public static Set<ImmutableBitSet> getUniqueKeys(RelNode rel,
+  public Set<ImmutableBitSet> getUniqueKeys(RelNode rel,
       boolean ignoreNulls) {
     final BuiltInMetadata.UniqueKeys metadata =
-        rel.metadata(BuiltInMetadata.UniqueKeys.class);
+        rel.metadata(BuiltInMetadata.UniqueKeys.class, this);
     return metadata.getUniqueKeys(ignoreNulls);
   }
 
@@ -264,12 +277,13 @@ public abstract class RelMetadataQuery {
    * statistic over all columns.
    *
    * @param rel     the relational expression
+   *
    * @return true or false depending on whether the rows are unique, or
    * null if not enough information is available to make that determination
    */
-  public static Boolean areRowsUnique(RelNode rel) {
+  public Boolean areRowsUnique(RelNode rel) {
     final BuiltInMetadata.ColumnUniqueness metadata =
-        rel.metadata(BuiltInMetadata.ColumnUniqueness.class);
+        rel.metadata(BuiltInMetadata.ColumnUniqueness.class, this);
     final ImmutableBitSet columns =
         ImmutableBitSet.range(rel.getRowType().getFieldCount());
     return metadata.areColumnsUnique(columns, false);
@@ -277,24 +291,25 @@ public abstract class RelMetadataQuery {
 
   /**
    * Returns the
-   * {@link BuiltInMetadata.ColumnUniqueness#areColumnsUnique(org.apache.calcite.util.ImmutableBitSet, boolean)}
+   * {@link BuiltInMetadata.ColumnUniqueness#areColumnsUnique(ImmutableBitSet, boolean)}
    * statistic.
    *
    * @param rel     the relational expression
    * @param columns column mask representing the subset of columns for which
    *                uniqueness will be determined
+   *
    * @return true or false depending on whether the columns are unique, or
    * null if not enough information is available to make that determination
    */
-  public static Boolean areColumnsUnique(RelNode rel, ImmutableBitSet columns) {
+  public Boolean areColumnsUnique(RelNode rel, ImmutableBitSet columns) {
     final BuiltInMetadata.ColumnUniqueness metadata =
-        rel.metadata(BuiltInMetadata.ColumnUniqueness.class);
+        rel.metadata(BuiltInMetadata.ColumnUniqueness.class, this);
     return metadata.areColumnsUnique(columns, false);
   }
 
   /**
    * Returns the
-   * {@link BuiltInMetadata.ColumnUniqueness#areColumnsUnique(org.apache.calcite.util.ImmutableBitSet, boolean)}
+   * {@link BuiltInMetadata.ColumnUniqueness#areColumnsUnique(ImmutableBitSet, boolean)}
    * statistic.
    *
    * @param rel         the relational expression
@@ -305,46 +320,46 @@ public abstract class RelMetadataQuery {
    * @return true or false depending on whether the columns are unique, or
    * null if not enough information is available to make that determination
    */
-  public static Boolean areColumnsUnique(RelNode rel, ImmutableBitSet columns,
+  public Boolean areColumnsUnique(RelNode rel, ImmutableBitSet columns,
       boolean ignoreNulls) {
     final BuiltInMetadata.ColumnUniqueness metadata =
-        rel.metadata(BuiltInMetadata.ColumnUniqueness.class);
+        rel.metadata(BuiltInMetadata.ColumnUniqueness.class, this);
     return metadata.areColumnsUnique(columns, ignoreNulls);
   }
 
   /**
    * Returns the
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Collation#collations()}
+   * {@link BuiltInMetadata.Collation#collations()}
    * statistic.
    *
    * @param rel         the relational expression
    * @return List of sorted column combinations, or
    * null if not enough information is available to make that determination
    */
-  public static ImmutableList<RelCollation> collations(RelNode rel) {
+  public ImmutableList<RelCollation> collations(RelNode rel) {
     final BuiltInMetadata.Collation metadata =
-        rel.metadata(BuiltInMetadata.Collation.class);
+        rel.metadata(BuiltInMetadata.Collation.class, this);
     return metadata.collations();
   }
 
   /**
    * Returns the
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Distribution#distribution()}
+   * {@link BuiltInMetadata.Distribution#distribution()}
    * statistic.
    *
    * @param rel         the relational expression
    * @return List of sorted column combinations, or
    * null if not enough information is available to make that determination
    */
-  public static RelDistribution distribution(RelNode rel) {
+  public RelDistribution distribution(RelNode rel) {
     final BuiltInMetadata.Distribution metadata =
-        rel.metadata(BuiltInMetadata.Distribution.class);
+        rel.metadata(BuiltInMetadata.Distribution.class, this);
     return metadata.distribution();
   }
 
   /**
    * Returns the
-   * {@link BuiltInMetadata.PopulationSize#getPopulationSize(org.apache.calcite.util.ImmutableBitSet)}
+   * {@link BuiltInMetadata.PopulationSize#getPopulationSize(ImmutableBitSet)}
    * statistic.
    *
    * @param rel      the relational expression
@@ -354,31 +369,31 @@ public abstract class RelMetadataQuery {
    * estimate can be determined
    *
    */
-  public static Double getPopulationSize(RelNode rel,
+  public Double getPopulationSize(RelNode rel,
       ImmutableBitSet groupKey) {
     final BuiltInMetadata.PopulationSize metadata =
-        rel.metadata(BuiltInMetadata.PopulationSize.class);
+        rel.metadata(BuiltInMetadata.PopulationSize.class, this);
     Double result = metadata.getPopulationSize(groupKey);
     return validateResult(result);
   }
 
   /**
    * Returns the
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Size#averageRowSize()}
+   * {@link BuiltInMetadata.Size#averageRowSize()}
    * statistic.
    *
    * @param rel      the relational expression
    * @return average size of a row, in bytes, or null if not known
      */
-  public static Double getAverageRowSize(RelNode rel) {
+  public Double getAverageRowSize(RelNode rel) {
     final BuiltInMetadata.Size metadata =
-        rel.metadata(BuiltInMetadata.Size.class);
+        rel.metadata(BuiltInMetadata.Size.class, this);
     return metadata.averageRowSize();
   }
 
   /**
    * Returns the
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Size#averageColumnSizes()}
+   * {@link BuiltInMetadata.Size#averageColumnSizes()}
    * statistic.
    *
    * @param rel      the relational expression
@@ -386,17 +401,17 @@ public abstract class RelMetadataQuery {
    * value, in bytes. Each value or the entire list may be null if the
    * metadata is not available
    */
-  public static List<Double> getAverageColumnSizes(RelNode rel) {
+  public List<Double> getAverageColumnSizes(RelNode rel) {
     final BuiltInMetadata.Size metadata =
-        rel.metadata(BuiltInMetadata.Size.class);
+        rel.metadata(BuiltInMetadata.Size.class, this);
     return metadata.averageColumnSizes();
   }
 
   /** As {@link #getAverageColumnSizes(org.apache.calcite.rel.RelNode)} but
    * never returns a null list, only ever a list of nulls. */
-  public static List<Double> getAverageColumnSizesNotNull(RelNode rel) {
+  public List<Double> getAverageColumnSizesNotNull(RelNode rel) {
     final BuiltInMetadata.Size metadata =
-        rel.metadata(BuiltInMetadata.Size.class);
+        rel.metadata(BuiltInMetadata.Size.class, this);
     final List<Double> averageColumnSizes = metadata.averageColumnSizes();
     return averageColumnSizes == null
         ? Collections.<Double>nCopies(rel.getRowType().getFieldCount(), null)
@@ -405,7 +420,7 @@ public abstract class RelMetadataQuery {
 
   /**
    * Returns the
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Parallelism#isPhaseTransition()}
+   * {@link BuiltInMetadata.Parallelism#isPhaseTransition()}
    * statistic.
    *
    * @param rel      the relational expression
@@ -413,29 +428,29 @@ public abstract class RelMetadataQuery {
    * expression belongs to a different process than its inputs, or null if not
    * known
    */
-  public static Boolean isPhaseTransition(RelNode rel) {
+  public Boolean isPhaseTransition(RelNode rel) {
     final BuiltInMetadata.Parallelism metadata =
-        rel.metadata(BuiltInMetadata.Parallelism.class);
+        rel.metadata(BuiltInMetadata.Parallelism.class, this);
     return metadata.isPhaseTransition();
   }
 
   /**
    * Returns the
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Parallelism#splitCount()}
+   * {@link BuiltInMetadata.Parallelism#splitCount()}
    * statistic.
    *
    * @param rel      the relational expression
    * @return the number of distinct splits of the data, or null if not known
    */
-  public static Integer splitCount(RelNode rel) {
+  public Integer splitCount(RelNode rel) {
     final BuiltInMetadata.Parallelism metadata =
-        rel.metadata(BuiltInMetadata.Parallelism.class);
+        rel.metadata(BuiltInMetadata.Parallelism.class, this);
     return metadata.splitCount();
   }
 
   /**
    * Returns the
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Memory#memory()}
+   * {@link BuiltInMetadata.Memory#memory()}
    * statistic.
    *
    * @param rel      the relational expression
@@ -443,15 +458,15 @@ public abstract class RelMetadataQuery {
    * operator implementing this relational expression, across all splits,
    * or null if not known
    */
-  public static Double memory(RelNode rel) {
+  public Double memory(RelNode rel) {
     final BuiltInMetadata.Memory metadata =
-        rel.metadata(BuiltInMetadata.Memory.class);
+        rel.metadata(BuiltInMetadata.Memory.class, this);
     return metadata.memory();
   }
 
   /**
    * Returns the
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Memory#cumulativeMemoryWithinPhase()}
+   * {@link BuiltInMetadata.Memory#cumulativeMemoryWithinPhase()}
    * statistic.
    *
    * @param rel      the relational expression
@@ -459,15 +474,15 @@ public abstract class RelMetadataQuery {
    * physical operator implementing this relational expression, and all other
    * operators within the same phase, across all splits, or null if not known
    */
-  public static Double cumulativeMemoryWithinPhase(RelNode rel) {
+  public Double cumulativeMemoryWithinPhase(RelNode rel) {
     final BuiltInMetadata.Memory metadata =
-        rel.metadata(BuiltInMetadata.Memory.class);
+        rel.metadata(BuiltInMetadata.Memory.class, this);
     return metadata.cumulativeMemoryWithinPhase();
   }
 
   /**
    * Returns the
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Memory#cumulativeMemoryWithinPhaseSplit()}
+   * {@link BuiltInMetadata.Memory#cumulativeMemoryWithinPhaseSplit()}
    * statistic.
    *
    * @param rel      the relational expression
@@ -475,15 +490,15 @@ public abstract class RelMetadataQuery {
    * the physical operator implementing this relational expression, and all
    * operators within the same phase, within each split, or null if not known
    */
-  public static Double cumulativeMemoryWithinPhaseSplit(RelNode rel) {
+  public Double cumulativeMemoryWithinPhaseSplit(RelNode rel) {
     final BuiltInMetadata.Memory metadata =
-        rel.metadata(BuiltInMetadata.Memory.class);
+        rel.metadata(BuiltInMetadata.Memory.class, this);
     return metadata.cumulativeMemoryWithinPhaseSplit();
   }
 
   /**
    * Returns the
-   * {@link BuiltInMetadata.DistinctRowCount#getDistinctRowCount(org.apache.calcite.util.ImmutableBitSet, org.apache.calcite.rex.RexNode)}
+   * {@link BuiltInMetadata.DistinctRowCount#getDistinctRowCount(ImmutableBitSet, RexNode)}
    * statistic.
    *
    * @param rel       the relational expression
@@ -492,27 +507,27 @@ public abstract class RelMetadataQuery {
    * @return distinct row count for groupKey, filtered by predicate, or null
    * if no reliable estimate can be determined
    */
-  public static Double getDistinctRowCount(
+  public Double getDistinctRowCount(
       RelNode rel,
       ImmutableBitSet groupKey,
       RexNode predicate) {
     final BuiltInMetadata.DistinctRowCount metadata =
-        rel.metadata(BuiltInMetadata.DistinctRowCount.class);
+        rel.metadata(BuiltInMetadata.DistinctRowCount.class, this);
     Double result = metadata.getDistinctRowCount(groupKey, predicate);
     return validateResult(result);
   }
 
   /**
    * Returns the
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Predicates#getPredicates()}
+   * {@link BuiltInMetadata.Predicates#getPredicates()}
    * statistic.
    *
    * @param rel the relational expression
    * @return Predicates that can be pulled above this RelNode
    */
-  public static RelOptPredicateList getPulledUpPredicates(RelNode rel) {
+  public RelOptPredicateList getPulledUpPredicates(RelNode rel) {
     final BuiltInMetadata.Predicates metadata =
-        rel.metadata(BuiltInMetadata.Predicates.class);
+        rel.metadata(BuiltInMetadata.Predicates.class, this);
     return metadata.getPredicates();
   }
 
@@ -526,10 +541,10 @@ public abstract class RelMetadataQuery {
    * @return true for visible, false for invisible; if no metadata is available,
    * defaults to true
    */
-  public static boolean isVisibleInExplain(RelNode rel,
+  public boolean isVisibleInExplain(RelNode rel,
       SqlExplainLevel explainLevel) {
     final BuiltInMetadata.ExplainVisibility metadata =
-        rel.metadata(BuiltInMetadata.ExplainVisibility.class);
+        rel.metadata(BuiltInMetadata.ExplainVisibility.class, this);
     Boolean b = metadata.isVisibleInExplain(explainLevel);
     return b == null || b;
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/UnboundMetadata.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/UnboundMetadata.java b/core/src/main/java/org/apache/calcite/rel/metadata/UnboundMetadata.java
new file mode 100644
index 0000000..ef8dc4a
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/UnboundMetadata.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel.metadata;
+
+import org.apache.calcite.rel.RelNode;
+
+/**
+ * Metadata that needs to be bound to a {@link RelNode} and
+ * {@link RelMetadataQuery} before it can be used.
+ *
+ * @param <M> Metadata type
+ */
+public interface UnboundMetadata<M extends Metadata> {
+  M bind(RelNode rel, RelMetadataQuery mq);
+}
+
+// End UnboundMetadata.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/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 52b5497..b2ebf84 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
@@ -72,8 +72,8 @@ public class AggregateFilterTransposeRule extends RelOptRule {
     final ImmutableBitSet newGroupSet =
         aggregate.getGroupSet().union(filterColumns);
     final RelNode input = filter.getInput();
-    final Boolean unique =
-        RelMetadataQuery.areColumnsUnique(input, newGroupSet);
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final Boolean unique = mq.areColumnsUnique(input, newGroupSet);
     if (unique != null && unique) {
       // The input is already unique on the grouping columns, so there's little
       // advantage of aggregating again. More important, without this check,

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/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 016e45a..6978e04 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
@@ -38,7 +38,9 @@ import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.SqlSplittableAggFunction;
 import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.tools.RelBuilderFactory;
+import org.apache.calcite.util.Bug;
 import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.Util;
 import org.apache.calcite.util.mapping.Mapping;
 import org.apache.calcite.util.mapping.Mappings;
 
@@ -153,8 +155,9 @@ 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 ImmutableBitSet keyColumns = keyColumns(aggregateColumns,
-        RelMetadataQuery.getPulledUpPredicates(join).pulledUpPredicates);
+        mq.getPulledUpPredicates(join).pulledUpPredicates);
     final ImmutableBitSet joinColumns =
         RelOptUtil.InputFinder.bits(join.getCondition());
     final boolean allColumnsInAggregate =
@@ -202,17 +205,18 @@ public class AggregateJoinTransposeRule extends RelOptRule {
         // any functions experiencing a cartesian product effect.
         //
         // But finding out whether the input is already unique requires a call
-        // to areColumnsUnique that currently (until [CALCITE-794] "Detect
-        // cycles when computing statistics" is fixed) places a heavy load on
+        // to areColumnsUnique that currently (until [CALCITE-1048] "Make
+        // metadata more robust" is fixed) places a heavy load on
         // the metadata system.
         //
         // So we choose to imagine the the input is already unique, which is
         // untrue but harmless.
         //
+        Util.discard(Bug.CALCITE_1048_FIXED);
         unique = true;
       } else {
         final Boolean unique0 =
-            RelMetadataQuery.areColumnsUnique(joinInput, belowAggregateKey);
+            mq.areColumnsUnique(joinInput, belowAggregateKey);
         unique = unique0 != null && unique0;
       }
       if (unique) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/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 a0ff130..bb7797e 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
@@ -109,8 +109,9 @@ public class AggregateProjectPullUpConstantsRule extends RelOptRule {
     }
 
     final RexBuilder rexBuilder = aggregate.getCluster().getRexBuilder();
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
     final RelOptPredicateList predicates =
-        RelMetadataQuery.getPulledUpPredicates(aggregate.getInput());
+        mq.getPulledUpPredicates(aggregate.getInput());
     if (predicates == null) {
       return;
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/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 aa74448..b98ba64 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
@@ -57,10 +57,11 @@ public class AggregateRemoveRule extends RelOptRule {
   public void onMatch(RelOptRuleCall call) {
     final LogicalAggregate aggregate = call.rel(0);
     final RelNode input = call.rel(1);
-    if (!aggregate.getAggCallList().isEmpty()
-        || aggregate.indicator
-        || !SqlFunctions.isTrue(
-            RelMetadataQuery.areColumnsUnique(input, aggregate.getGroupSet()))) {
+    if (!aggregate.getAggCallList().isEmpty() || aggregate.indicator) {
+      return;
+    }
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    if (!SqlFunctions.isTrue(mq.areColumnsUnique(input, aggregate.getGroupSet()))) {
       return;
     }
     // Distinct is "GROUP BY c1, c2" (where c1, c2 are a set of columns on

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/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 7464a0f..3945924 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
@@ -33,6 +33,7 @@ import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.schema.Table;
 import org.apache.calcite.schema.impl.StarTable;
@@ -115,7 +116,8 @@ public class AggregateStarTableRule extends RelOptRule {
     final RelBuilder relBuilder = call.builder();
     final CalciteSchema.TableEntry tableEntry = pair.left;
     final TileKey tileKey = pair.right;
-    final double rowCount = aggregate.getRows();
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final double rowCount = aggregate.estimateRowCount(mq);
     final Table aggregateTable = tableEntry.getTable();
     final RelDataType aggregateTableRowType =
         aggregateTable.getRowType(cluster.getTypeFactory());

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/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 2a2ef98..12300d9 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
@@ -27,6 +27,7 @@ import org.apache.calcite.rel.core.Union;
 import org.apache.calcite.rel.logical.LogicalAggregate;
 import org.apache.calcite.rel.logical.LogicalUnion;
 import org.apache.calcite.rel.metadata.RelMdUtil;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.fun.SqlCountAggFunction;
@@ -115,10 +116,10 @@ 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();
     for (RelNode input : union.getInputs()) {
       boolean alreadyUnique =
-          RelMdUtil.areColumnsDefinitelyUnique(
-              input,
+          RelMdUtil.areColumnsDefinitelyUnique(mq, input,
               aggRel.getGroupSet());
 
       relBuilder.push(input);

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/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 1d375c6..0b020c7 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,8 @@ public class JoinPushTransitivePredicatesRule extends RelOptRule {
 
   @Override public void onMatch(RelOptRuleCall call) {
     Join join = call.rel(0);
-    RelOptPredicateList preds = RelMetadataQuery.getPulledUpPredicates(join);
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    RelOptPredicateList preds = mq.getPulledUpPredicates(join);
 
     if (preds.leftInferredPredicates.isEmpty()
         && preds.rightInferredPredicates.isEmpty()) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/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 a41e1aa..ee4eeaa 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
@@ -192,7 +192,7 @@ public class LoptMultiJoin {
     joinFilters =
         Lists.newArrayList(RelOptUtil.conjunctions(multiJoin.getJoinFilter()));
 
-    allJoinFilters = new ArrayList<RexNode>(joinFilters);
+    allJoinFilters = new ArrayList<>(joinFilters);
     List<RexNode> outerJoinFilters = multiJoin.getOuterJoinConditions();
     for (int i = 0; i < nJoinFactors; i++) {
       allJoinFilters.addAll(RelOptUtil.conjunctions(outerJoinFilters.get(i)));
@@ -235,8 +235,8 @@ public class LoptMultiJoin {
     joinRemovalFactors = new Integer[nJoinFactors];
     joinRemovalSemiJoins = new SemiJoin[nJoinFactors];
 
-    removableOuterJoinFactors = new HashSet<Integer>();
-    removableSelfJoinPairs = new HashMap<Integer, RemovableSelfJoin>();
+    removableOuterJoinFactors = new HashSet<>();
+    removableSelfJoinPairs = new HashMap<>();
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -695,15 +695,15 @@ public class LoptMultiJoin {
     // Compute a column mapping such that if a column from the right
     // factor is also referenced in the left factor, we will map the
     // right reference to the left to avoid redundant references.
-    Map<Integer, Integer> columnMapping = new HashMap<Integer, Integer>();
+    final Map<Integer, Integer> columnMapping = new HashMap<>();
 
     // First, locate the originating column for all simple column
     // references in the left factor.
-    RelNode left = getJoinFactor(leftFactor);
-    Map<Integer, Integer> leftFactorColMapping =
-        new HashMap<Integer, Integer>();
+    final RelNode left = getJoinFactor(leftFactor);
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final Map<Integer, Integer> leftFactorColMapping = new HashMap<>();
     for (int i = 0; i < left.getRowType().getFieldCount(); i++) {
-      RelColumnOrigin colOrigin = RelMetadataQuery.getColumnOrigin(left, i);
+      final RelColumnOrigin colOrigin = mq.getColumnOrigin(left, i);
       if (colOrigin != null) {
         leftFactorColMapping.put(
             colOrigin.getOriginColumnOrdinal(),
@@ -717,8 +717,7 @@ public class LoptMultiJoin {
     // factor.
     RelNode right = getJoinFactor(rightFactor);
     for (int i = 0; i < right.getRowType().getFieldCount(); i++) {
-      final RelColumnOrigin colOrigin =
-          RelMetadataQuery.getColumnOrigin(right, i);
+      final RelColumnOrigin colOrigin = mq.getColumnOrigin(right, i);
       if (colOrigin == null) {
         continue;
       }

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/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 66ae671..e522ade 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
@@ -209,9 +209,9 @@ 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.
-        if (RelMdUtil.areColumnsDefinitelyUniqueWhenNullsFiltered(
-            multiJoin.getJoinFactor(factIdx),
-            joinKeys)) {
+        final RelMetadataQuery mq = RelMetadataQuery.instance();
+        if (RelMdUtil.areColumnsDefinitelyUniqueWhenNullsFiltered(mq,
+            multiJoin.getJoinFactor(factIdx), joinKeys)) {
           multiJoin.addRemovableOuterJoinFactor(factIdx);
 
           // Since we are no longer joining this factor,
@@ -319,7 +319,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
     // the appropriate join condition between the two factors that will
     // allow the join to be removed.
     for (Integer factor1 : selfJoinPairs.keySet()) {
-      int factor2 = selfJoinPairs.get(factor1);
+      final int factor2 = selfJoinPairs.get(factor1);
       final List<RexNode> selfJoinFilters = new ArrayList<>();
       for (RexNode filter : multiJoin.getJoinFilters()) {
         ImmutableBitSet joinFactors =
@@ -361,13 +361,14 @@ 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)) {
         continue;
       }
       final RelNode rel = multiJoin.getJoinFactor(factIdx);
-      final RelOptTable table = RelMetadataQuery.getTableOrigin(rel);
+      final RelOptTable table = mq.getTableOrigin(rel);
       if (table != null) {
         returnList.put(factIdx, table);
       }
@@ -421,7 +422,8 @@ public class LoptOptimizeJoinRule extends RelOptRule {
                 rightRel.getRowType().getFieldList(),
                 adjustments));
 
-    return areSelfJoinKeysUnique(leftRel, rightRel, joinFilters);
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    return areSelfJoinKeysUnique(mq, leftRel, rightRel, joinFilters);
   }
 
   /**
@@ -439,7 +441,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
       RelOptRuleCall call) {
     final List<RelNode> plans = new ArrayList<>();
 
-    List<String> fieldNames =
+    final List<String> fieldNames =
         multiJoin.getMultiJoinRel().getRowType().getFieldNames();
 
     // generate the N join orderings
@@ -603,10 +605,9 @@ public class LoptOptimizeJoinRule extends RelOptRule {
     if (joinKeys.isEmpty()) {
       return null;
     } else {
-      return RelMetadataQuery.getDistinctRowCount(
-          semiJoinOpt.getChosenSemiJoin(factor),
-          joinKeys.build(),
-          null);
+      final RelMetadataQuery mq = semiJoinOpt.mq;
+      return mq.getDistinctRowCount(semiJoinOpt.getChosenSemiJoin(factor),
+          joinKeys.build(), null);
     }
   }
 
@@ -671,9 +672,9 @@ public class LoptOptimizeJoinRule extends RelOptRule {
       LoptSemiJoinOptimizer semiJoinOpt,
       int firstFactor) {
     LoptJoinTree joinTree = null;
-    int nJoinFactors = multiJoin.getNumJoinFactors();
-    BitSet factorsToAdd = BitSets.range(0, nJoinFactors);
-    BitSet factorsAdded = new BitSet(nJoinFactors);
+    final int nJoinFactors = multiJoin.getNumJoinFactors();
+    final BitSet factorsToAdd = BitSets.range(0, nJoinFactors);
+    final BitSet factorsAdded = new BitSet(nJoinFactors);
     final List<RexNode> filtersToAdd =
         new ArrayList<>(multiJoin.getJoinFilters());
 
@@ -867,6 +868,8 @@ 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
     if (multiJoin.isRemovableOuterJoinFactor(factorToAdd)) {
@@ -935,11 +938,10 @@ public class LoptOptimizeJoinRule extends RelOptRule {
     RelOptCost costPushDown = null;
     RelOptCost costTop = null;
     if (pushDownTree != null) {
-      costPushDown =
-          RelMetadataQuery.getCumulativeCost(pushDownTree.getJoinTree());
+      costPushDown = mq.getCumulativeCost(pushDownTree.getJoinTree());
     }
     if (topTree != null) {
-      costTop = RelMetadataQuery.getCumulativeCost(topTree.getJoinTree());
+      costTop = mq.getCumulativeCost(topTree.getJoinTree());
     }
 
     if (pushDownTree == null) {
@@ -1337,7 +1339,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
       int factorAdded,
       List<Integer> origJoinOrder,
       List<RelDataTypeField> origFields) {
-    List<Integer> newJoinOrder = new ArrayList<>();
+    final List<Integer> newJoinOrder = new ArrayList<>();
     left.getTreeOrder(newJoinOrder);
     right.getTreeOrder(newJoinOrder);
 
@@ -1858,8 +1860,9 @@ public class LoptOptimizeJoinRule extends RelOptRule {
           ((LoptJoinTree.Leaf) left.getFactorTree()).getId());
     }
 
-    Double leftRowCount = RelMetadataQuery.getRowCount(left.getJoinTree());
-    Double rightRowCount = RelMetadataQuery.getRowCount(right.getJoinTree());
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final Double leftRowCount = mq.getRowCount(left.getJoinTree());
+    final Double rightRowCount = mq.getRowCount(right.getJoinTree());
 
     // The left side is smaller than the right if it has fewer rows,
     // or if it has the same number of rows as the right (excluding
@@ -1990,11 +1993,12 @@ public class LoptOptimizeJoinRule extends RelOptRule {
     }
 
     // Make sure the join is between the same simple factor
-    final RelOptTable leftTable = RelMetadataQuery.getTableOrigin(left);
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelOptTable leftTable = mq.getTableOrigin(left);
     if (leftTable == null) {
       return false;
     }
-    final RelOptTable rightTable = RelMetadataQuery.getTableOrigin(right);
+    final RelOptTable rightTable = mq.getTableOrigin(right);
     if (rightTable == null) {
       return false;
     }
@@ -2003,35 +2007,34 @@ public class LoptOptimizeJoinRule extends RelOptRule {
     }
 
     // Determine if the join keys are identical and unique
-    return areSelfJoinKeysUnique(left, right, joinRel.getCondition());
+    return areSelfJoinKeysUnique(mq, left, right, joinRel.getCondition());
   }
 
   /**
    * Determines if the equality portion of a self-join condition is between
    * identical keys that are unique.
    *
+   * @param mq Metadata query
    * @param leftRel left side of the join
    * @param rightRel right side of the join
    * @param joinFilters the join condition
    *
    * @return true if the equality join keys are the same and unique
    */
-  private static boolean areSelfJoinKeysUnique(
-      RelNode leftRel,
-      RelNode rightRel,
-      RexNode joinFilters) {
+  private static boolean areSelfJoinKeysUnique(RelMetadataQuery mq,
+      RelNode leftRel, RelNode rightRel, RexNode joinFilters) {
     final JoinInfo joinInfo = JoinInfo.of(leftRel, rightRel, joinFilters);
 
     // Make sure each key on the left maps to the same simple column as the
     // corresponding key on the right
     for (IntPair pair : joinInfo.pairs()) {
       final RelColumnOrigin leftOrigin =
-          RelMetadataQuery.getColumnOrigin(leftRel, pair.source);
+          mq.getColumnOrigin(leftRel, pair.source);
       if (leftOrigin == null) {
         return false;
       }
       final RelColumnOrigin rightOrigin =
-          RelMetadataQuery.getColumnOrigin(rightRel, pair.target);
+          mq.getColumnOrigin(rightRel, pair.target);
       if (rightOrigin == null) {
         return false;
       }
@@ -2045,7 +2048,7 @@ public class LoptOptimizeJoinRule extends RelOptRule {
     // are unique.  When removing self-joins, if needed, we'll later add an
     // IS NOT NULL filter on the join keys that are nullable.  Therefore,
     // it's ok if there are nulls in the unique key.
-    return RelMdUtil.areColumnsDefinitelyUniqueWhenNullsFiltered(leftRel,
+    return RelMdUtil.areColumnsDefinitelyUniqueWhenNullsFiltered(mq, leftRel,
         joinInfo.leftSet());
   }
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/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 080cb9c..308091e 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
@@ -58,10 +58,11 @@ public class LoptSemiJoinOptimizer {
 
   //~ Instance fields --------------------------------------------------------
 
-  /**
-   * RexBuilder for constructing new RexNodes
-   */
-  private RexBuilder rexBuilder;
+  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();
 
   /**
    * Semijoins corresponding to each join factor, if they are going to be
@@ -406,7 +407,7 @@ public class LoptSemiJoinOptimizer {
     while (keyIter.hasNext()) {
       boolean removeKey = false;
       final RelColumnOrigin colOrigin =
-          RelMetadataQuery.getColumnOrigin(factRel, keyIter.next());
+          mq.getColumnOrigin(factRel, keyIter.next());
 
       // can't use the rid column as a semijoin key
       if ((colOrigin == null)
@@ -635,13 +636,13 @@ public class LoptSemiJoinOptimizer {
     // a middle ground based on testing that was done with a large
     // data set.
     final ImmutableBitSet dimCols = ImmutableBitSet.of(semiJoin.getRightKeys());
-    double selectivity =
-        RelMdUtil.computeSemiJoinSelectivity(factRel, dimRel, semiJoin);
+    final double selectivity =
+        RelMdUtil.computeSemiJoinSelectivity(mq, factRel, dimRel, semiJoin);
     if (selectivity > .5) {
       return 0;
     }
 
-    RelOptCost factCost = RelMetadataQuery.getCumulativeCost(factRel);
+    final RelOptCost factCost = mq.getCumulativeCost(factRel);
 
     // if not enough information, return a low score
     if (factCost == null) {
@@ -654,9 +655,8 @@ public class LoptSemiJoinOptimizer {
     // Additional savings if the dimension columns are unique.  We can
     // ignore nulls since they will be filtered out by the semijoin.
     boolean uniq =
-        RelMdUtil.areColumnsDefinitelyUniqueWhenNullsFiltered(
-            dimRel,
-            dimCols);
+        RelMdUtil.areColumnsDefinitelyUniqueWhenNullsFiltered(mq,
+            dimRel, dimCols);
     if (uniq) {
       savings *= 2.0;
     }
@@ -664,9 +664,9 @@ public class LoptSemiJoinOptimizer {
     // compute the cost of doing an extra scan on the dimension table,
     // including the distinct sort on top of the scan; if the dimension
     // columns are already unique, no need to add on the dup removal cost
-    Double dimSortCost = RelMetadataQuery.getRowCount(dimRel);
-    Double dupRemCost = uniq ? 0 : dimSortCost;
-    RelOptCost dimCost = RelMetadataQuery.getCumulativeCost(dimRel);
+    final Double dimSortCost = mq.getRowCount(dimRel);
+    final Double dupRemCost = uniq ? 0 : dimSortCost;
+    final RelOptCost dimCost = mq.getCumulativeCost(dimRel);
     if ((dimSortCost == null)
         || (dupRemCost == null)
         || (dimCost == null)) {
@@ -708,9 +708,8 @@ public class LoptSemiJoinOptimizer {
     // Check if the semijoin keys corresponding to the dimension table
     // are unique.  The semijoin will filter out the nulls.
     final ImmutableBitSet dimKeys = ImmutableBitSet.of(semiJoin.getRightKeys());
-    RelNode dimRel = multiJoin.getJoinFactor(dimIdx);
-    if (!RelMdUtil.areColumnsDefinitelyUniqueWhenNullsFiltered(
-        dimRel,
+    final RelNode dimRel = multiJoin.getJoinFactor(dimIdx);
+    if (!RelMdUtil.areColumnsDefinitelyUniqueWhenNullsFiltered(mq, dimRel,
         dimKeys)) {
       return;
     }
@@ -800,14 +799,14 @@ public class LoptSemiJoinOptimizer {
 
   //~ Inner Classes ----------------------------------------------------------
 
-  /** . */
+  /** Compares factors. */
   private class FactorCostComparator
       implements Comparator<Integer> {
     public int compare(Integer rel1Idx, Integer rel2Idx) {
       RelOptCost c1 =
-          RelMetadataQuery.getCumulativeCost(chosenSemiJoins[rel1Idx]);
+          mq.getCumulativeCost(chosenSemiJoins[rel1Idx]);
       RelOptCost c2 =
-          RelMetadataQuery.getCumulativeCost(chosenSemiJoins[rel2Idx]);
+          mq.getCumulativeCost(chosenSemiJoins[rel2Idx]);
 
       // nulls are arbitrarily sorted
       if ((c1 == null) || (c2 == null)) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/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 477b74a..b0b372d 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
@@ -87,6 +87,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 LoptMultiJoin multiJoin = new LoptMultiJoin(multiJoinRel);
 
@@ -94,7 +95,7 @@ public class MultiJoinOptimizeBushyRule extends RelOptRule {
     int x = 0;
     for (int i = 0; i < multiJoin.getNumJoinFactors(); i++) {
       final RelNode rel = multiJoin.getJoinFactor(i);
-      double cost = RelMetadataQuery.getRowCount(rel);
+      double cost = mq.getRowCount(rel);
       vertexes.add(new LeafVertex(i, rel, cost, x));
       x += rel.getRowType().getFieldCount();
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/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 759748c..3fce8a6 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
@@ -140,8 +140,9 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
           Lists.newArrayList(filter.getCondition());
       RexNode newConditionExp;
       boolean reduced;
+      final RelMetadataQuery mq = RelMetadataQuery.instance();
       final RelOptPredicateList predicates =
-          RelMetadataQuery.getPulledUpPredicates(filter.getInput());
+          mq.getPulledUpPredicates(filter.getInput());
       if (reduceExpressions(filter, expList, predicates)) {
         assert expList.size() == 1;
         newConditionExp = expList.get(0);
@@ -229,9 +230,10 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
     }
 
     @Override public void onMatch(RelOptRuleCall call) {
-      Project project = call.rel(0);
+      final Project project = call.rel(0);
+      final RelMetadataQuery mq = RelMetadataQuery.instance();
       final RelOptPredicateList predicates =
-          RelMetadataQuery.getPulledUpPredicates(project.getInput());
+          mq.getPulledUpPredicates(project.getInput());
       final List<RexNode> expList =
           Lists.newArrayList(project.getProjects());
       if (reduceExpressions(project, expList, predicates)) {
@@ -261,10 +263,11 @@ 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 RelOptPredicateList leftPredicates =
-          RelMetadataQuery.getPulledUpPredicates(join.getLeft());
+          mq.getPulledUpPredicates(join.getLeft());
       final RelOptPredicateList rightPredicates =
-          RelMetadataQuery.getPulledUpPredicates(join.getRight());
+          mq.getPulledUpPredicates(join.getRight());
       final RelOptPredicateList predicates =
           leftPredicates.union(rightPredicates.shift(fieldCount));
       if (!reduceExpressions(join, expList, predicates)) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/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 66b0afb..2da9a35 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
@@ -29,6 +29,7 @@ import org.apache.calcite.rel.core.Sort;
 import org.apache.calcite.rel.logical.LogicalJoin;
 import org.apache.calcite.rel.logical.LogicalSort;
 import org.apache.calcite.rel.metadata.RelMdUtil;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 
 
 /**
@@ -99,11 +100,12 @@ 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();
     if (join.getJoinType() == JoinRelType.LEFT) {
       // If the input is already sorted and we are not reducing the number of tuples,
       // we bail out
-      if (RelMdUtil.checkInputForCollationAndLimit(join.getLeft(), sort.getCollation(),
-          sort.offset, sort.fetch)) {
+      if (RelMdUtil.checkInputForCollationAndLimit(mq, join.getLeft(),
+          sort.getCollation(), sort.offset, sort.fetch)) {
         return;
       }
       newLeftInput = sort.copy(sort.getTraitSet(), join.getLeft(), sort.getCollation(),
@@ -116,8 +118,8 @@ public class SortJoinTransposeRule extends RelOptRule {
                   -join.getLeft().getRowType().getFieldCount()));
       // If the input is already sorted and we are not reducing the number of tuples,
       // we bail out
-      if (RelMdUtil.checkInputForCollationAndLimit(join.getRight(), rightCollation,
-          sort.offset, sort.fetch)) {
+      if (RelMdUtil.checkInputForCollationAndLimit(mq, join.getRight(),
+          rightCollation, sort.offset, sort.fetch)) {
         return;
       }
       newLeftInput = join.getLeft();

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/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 b195e3f..26d2106 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
@@ -23,6 +23,7 @@ import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.core.Sort;
 import org.apache.calcite.rel.core.Union;
 import org.apache.calcite.rel.metadata.RelMdUtil;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.tools.RelBuilderFactory;
 
 import java.util.ArrayList;
@@ -93,9 +94,10 @@ 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();
     for (RelNode input : union.getInputs()) {
-      if (!RelMdUtil.checkInputForCollationAndLimit(input, sort.getCollation(),
-          sort.offset, sort.fetch)) {
+      if (!RelMdUtil.checkInputForCollationAndLimit(mq, input,
+          sort.getCollation(), sort.offset, sort.fetch)) {
         ret = false;
         Sort branchSort = sort.copy(sort.getTraitSet(), input,
             sort.getCollation(), sort.offset, sort.fetch);

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/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 c3daf49..56b362d 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
@@ -148,7 +148,8 @@ public abstract class SubQueryRemoveRule extends RelOptRule {
     switch (e.getKind()) {
     case SCALAR_QUERY:
       builder.push(e.rel);
-      final Boolean unique = RelMetadataQuery.areColumnsUnique(builder.peek(),
+      final RelMetadataQuery mq = RelMetadataQuery.instance();
+      final Boolean unique = mq.areColumnsUnique(builder.peek(),
           ImmutableBitSet.of());
       if (unique == null || !unique) {
         builder.aggregate(builder.groupKey(),