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/21 23:39:08 UTC

[31/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/RelMdColumnUniqueness.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java
index 2a6431d..c872075 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java
@@ -44,7 +44,6 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.util.BuiltInMethod;
 import org.apache.calcite.util.ImmutableBitSet;
 
-import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 
 import java.util.ArrayList;
@@ -67,55 +66,53 @@ public class RelMdColumnUniqueness {
 
   //~ Methods ----------------------------------------------------------------
 
-  public Boolean areColumnsUnique(
-      TableScan rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
+  public Boolean areColumnsUnique(TableScan rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, boolean ignoreNulls) {
     return rel.getTable().isKey(columns);
   }
 
-  public Boolean areColumnsUnique(
-      Filter rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
-    return RelMetadataQuery.areColumnsUnique(
-        rel.getInput(),
-        columns,
-        ignoreNulls);
+  public Boolean areColumnsUnique(Filter rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, boolean ignoreNulls) {
+    return mq.areColumnsUnique(rel.getInput(), columns, ignoreNulls);
   }
 
-  public Boolean areColumnsUnique(
-      Sort rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
-    return RelMetadataQuery.areColumnsUnique(
-        rel.getInput(),
-        columns,
-        ignoreNulls);
+  /** Catch-all implementation for
+   * {@link BuiltInMetadata.ColumnUniqueness#areColumnsUnique(ImmutableBitSet, boolean)},
+   * invoked using reflection, for any relational expression not
+   * handled by a more specific method.
+   *
+   * @param rel Relational expression
+   * @param mq Metadata query
+   * @param columns column mask representing the subset of columns for which
+   *                uniqueness will be determined
+   * @param ignoreNulls if true, ignore null values when determining column
+   *                    uniqueness
+   * @return whether the columns are unique, or
+   * null if not enough information is available to make that determination
+   *
+   * @see org.apache.calcite.rel.metadata.RelMetadataQuery#areColumnsUnique(RelNode, ImmutableBitSet, boolean)
+   */
+  public Boolean areColumnsUnique(RelNode rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, boolean ignoreNulls) {
+    // no information available
+    return null;
   }
 
-  public Boolean areColumnsUnique(
-      SetOp rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
+  public Boolean areColumnsUnique(SetOp rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, boolean ignoreNulls) {
     // If not ALL then the rows are distinct.
     // Therefore the set of all columns is a key.
     return !rel.all
         && columns.nextClearBit(0) >= rel.getRowType().getFieldCount();
   }
 
-  public Boolean areColumnsUnique(
-      Intersect rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
-    if (areColumnsUnique((SetOp) rel, columns, ignoreNulls)) {
+  public Boolean areColumnsUnique(Intersect rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, boolean ignoreNulls) {
+    if (areColumnsUnique((SetOp) rel, mq, columns, ignoreNulls)) {
       return true;
     }
     for (RelNode input : rel.getInputs()) {
-      Boolean b = RelMetadataQuery.areColumnsUnique(
-          input,
-          columns,
-          ignoreNulls);
+      Boolean b = mq.areColumnsUnique(input, columns, ignoreNulls);
       if (b != null && b) {
         return true;
       }
@@ -123,43 +120,31 @@ public class RelMdColumnUniqueness {
     return false;
   }
 
-  public Boolean areColumnsUnique(
-      Minus rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
-    if (areColumnsUnique((SetOp) rel, columns, ignoreNulls)) {
+  public Boolean areColumnsUnique(Minus rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, boolean ignoreNulls) {
+    if (areColumnsUnique((SetOp) rel, mq, columns, ignoreNulls)) {
       return true;
     }
-    return RelMetadataQuery.areColumnsUnique(
-        rel.getInput(0),
-        columns,
-        ignoreNulls);
+    return mq.areColumnsUnique(rel.getInput(0), columns, ignoreNulls);
   }
 
-  public Boolean areColumnsUnique(
-      Exchange rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
-    return RelMetadataQuery.areColumnsUnique(
-        rel.getInput(),
-        columns,
-        ignoreNulls);
+  public Boolean areColumnsUnique(Sort rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, boolean ignoreNulls) {
+    return mq.areColumnsUnique(rel.getInput(), columns, ignoreNulls);
   }
 
-  public Boolean areColumnsUnique(
-      Correlate rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
-    return RelMetadataQuery.areColumnsUnique(
-        rel.getLeft(),
-        columns,
-        ignoreNulls);
+  public Boolean areColumnsUnique(Exchange rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, boolean ignoreNulls) {
+    return mq.areColumnsUnique(rel.getInput(), columns, ignoreNulls);
+  }
+
+  public Boolean areColumnsUnique(Correlate rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, boolean ignoreNulls) {
+    return mq.areColumnsUnique(rel.getLeft(), columns, ignoreNulls);
   }
 
-  public Boolean areColumnsUnique(
-      Project rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
+  public Boolean areColumnsUnique(Project rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, 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
@@ -211,16 +196,12 @@ public class RelMdColumnUniqueness {
       return null;
     }
 
-    return RelMetadataQuery.areColumnsUnique(
-        rel.getInput(),
-        childColumns.build(),
+    return mq.areColumnsUnique(rel.getInput(), childColumns.build(),
         ignoreNulls);
   }
 
-  public Boolean areColumnsUnique(
-      Join rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
+  public Boolean areColumnsUnique(Join rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, boolean ignoreNulls) {
     if (columns.cardinality() == 0) {
       return false;
     }
@@ -245,11 +226,9 @@ public class RelMdColumnUniqueness {
     // right hand side, then the columns are unique if and only if they're
     // unique for their respective join inputs
     final ImmutableBitSet leftColumns = leftBuilder.build();
-    Boolean leftUnique =
-        RelMetadataQuery.areColumnsUnique(left, leftColumns, ignoreNulls);
+    Boolean leftUnique = mq.areColumnsUnique(left, leftColumns, ignoreNulls);
     final ImmutableBitSet rightColumns = rightBuilder.build();
-    Boolean rightUnique =
-        RelMetadataQuery.areColumnsUnique(right, rightColumns, ignoreNulls);
+    Boolean rightUnique = mq.areColumnsUnique(right, rightColumns, ignoreNulls);
     if ((leftColumns.cardinality() > 0)
         && (rightColumns.cardinality() > 0)) {
       if ((leftUnique == null) || (rightUnique == null)) {
@@ -271,8 +250,7 @@ public class RelMdColumnUniqueness {
         return false;
       }
       Boolean rightJoinColsUnique =
-          RelMetadataQuery.areColumnsUnique(right, joinInfo.rightSet(),
-              ignoreNulls);
+          mq.areColumnsUnique(right, joinInfo.rightSet(), ignoreNulls);
       if ((rightJoinColsUnique == null) || (leftUnique == null)) {
         return null;
       }
@@ -282,8 +260,7 @@ public class RelMdColumnUniqueness {
         return false;
       }
       Boolean leftJoinColsUnique =
-          RelMetadataQuery.areColumnsUnique(left, joinInfo.leftSet(),
-              ignoreNulls);
+          mq.areColumnsUnique(left, joinInfo.leftSet(), ignoreNulls);
       if ((leftJoinColsUnique == null) || (rightUnique == null)) {
         return null;
       }
@@ -293,56 +270,28 @@ public class RelMdColumnUniqueness {
     throw new AssertionError();
   }
 
-  public Boolean areColumnsUnique(
-      SemiJoin rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
+  public Boolean areColumnsUnique(SemiJoin rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, boolean ignoreNulls) {
     // only return the unique keys from the LHS since a semijoin only
     // returns the LHS
-    return RelMetadataQuery.areColumnsUnique(
-        rel.getLeft(),
-        columns,
-        ignoreNulls);
+    return mq.areColumnsUnique(rel.getLeft(), columns, ignoreNulls);
   }
 
-  public Boolean areColumnsUnique(
-      Aggregate rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
+  public Boolean areColumnsUnique(Aggregate rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, boolean ignoreNulls) {
     // group by keys form a unique key
     ImmutableBitSet groupKey = ImmutableBitSet.range(rel.getGroupCount());
     return columns.contains(groupKey);
   }
 
-  // Catch-all rule when none of the others apply.
-  public Boolean areColumnsUnique(
-      RelNode rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
-    // no information available
-    return null;
-  }
-
-  public Boolean areColumnsUnique(
-      Converter rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
-    return RelMetadataQuery.areColumnsUnique(
-        rel.getInput(),
-        columns,
-        ignoreNulls);
-  }
-
-  public Boolean areColumnsUnique(
-      Values rel,
-      RelMetadataQuery query,
-      boolean ignoreNulls) {
-    if (rel.getTuples().size() < 2) {
+  public Boolean areColumnsUnique(Values rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, boolean ignoreNulls) {
+    if (rel.tuples.size() < 2) {
       return true;
     }
     final Set<List<Comparable>> set = new HashSet<>();
     final List<Comparable> values = new ArrayList<>();
-    for (ImmutableList<RexLiteral> tuple : rel.getTuples()) {
+    for (ImmutableList<RexLiteral> tuple : rel.tuples) {
       for (RexLiteral literal : tuple) {
         values.add(NullSentinel.mask(literal.getValue()));
       }
@@ -354,36 +303,64 @@ public class RelMdColumnUniqueness {
     return true;
   }
 
-  public Boolean areColumnsUnique(
+  public Boolean areColumnsUnique(Converter rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, boolean ignoreNulls) {
+    return mq.areColumnsUnique(rel.getInput(), columns, ignoreNulls);
+  }
+
+  public Boolean areColumnsUnique(HepRelVertex rel, RelMetadataQuery mq,
       boolean dummy, // prevent method from being used
-      HepRelVertex rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
-    return RelMetadataQuery.areColumnsUnique(
-        rel.getCurrentRel(),
-        columns,
-        ignoreNulls);
+      ImmutableBitSet columns, boolean ignoreNulls) {
+    return mq.areColumnsUnique(rel.getCurrentRel(), columns, ignoreNulls);
   }
 
-  public Boolean areColumnsUnique(
-      RelSubset rel,
-      ImmutableBitSet columns,
-      boolean ignoreNulls) {
-    final RelNode best = rel.getBest();
-    if (best == null) {
-      return null;
-    } else {
-      return RelMetadataQuery.areColumnsUnique(best, columns, ignoreNulls);
+  public Boolean areColumnsUnique(RelSubset rel, RelMetadataQuery mq,
+      ImmutableBitSet columns, boolean ignoreNulls) {
+    int nullCount = 0;
+    for (RelNode rel2 : rel.getRels()) {
+      if (rel2 instanceof Aggregate
+          || rel2 instanceof Filter
+          || rel2 instanceof Values
+          || rel2 instanceof TableScan
+          || simplyProjects(rel2, columns)) {
+        try {
+          final Boolean unique = mq.areColumnsUnique(rel2, columns, ignoreNulls);
+          if (unique != null) {
+            if (unique) {
+              return true;
+            }
+          } else {
+            ++nullCount;
+          }
+        } catch (CyclicMetadataException e) {
+          // Ignore this relational expression; there will be non-cyclic ones
+          // in this set.
+        }
+      }
     }
+    return nullCount == 0 ? false : null;
   }
 
-  /** Aggregate and Calc are "safe" children of a RelSubset to delve into. */
-  private static final Predicate<RelNode> SAFE_REL =
-      new Predicate<RelNode>() {
-        public boolean apply(RelNode r) {
-          return r instanceof Aggregate || r instanceof Project;
-        }
-      };
+  private boolean simplyProjects(RelNode rel, ImmutableBitSet columns) {
+    if (!(rel instanceof Project)) {
+      return false;
+    }
+    Project project = (Project) rel;
+    final List<RexNode> projects = project.getProjects();
+    for (int column : columns) {
+      if (column >= projects.size()) {
+        return false;
+      }
+      if (!(projects.get(column) instanceof RexInputRef)) {
+        return false;
+      }
+      final RexInputRef ref = (RexInputRef) projects.get(column);
+      if (ref.getIndex() != column) {
+        return false;
+      }
+    }
+    return true;
+  }
 }
 
 // End RelMdColumnUniqueness.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java
index 2fb2b25..eef245e 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.rel.metadata;
 
 import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.plan.volcano.RelSubset;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.Exchange;
@@ -31,6 +32,7 @@ import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.util.Bug;
 import org.apache.calcite.util.BuiltInMethod;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.NumberUtil;
@@ -54,10 +56,27 @@ public class RelMdDistinctRowCount {
 
   //~ Methods ----------------------------------------------------------------
 
-  public Double getDistinctRowCount(
-      Union rel,
-      ImmutableBitSet groupKey,
-      RexNode predicate) {
+  /** Catch-all implementation for
+   * {@link BuiltInMetadata.DistinctRowCount#getDistinctRowCount(ImmutableBitSet, RexNode)},
+   * invoked using reflection.
+   *
+   * @see org.apache.calcite.rel.metadata.RelMetadataQuery#getDistinctRowCount(RelNode, ImmutableBitSet, RexNode)
+   */
+  public Double getDistinctRowCount(RelNode rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey, RexNode predicate) {
+    // REVIEW zfong 4/19/06 - Broadbase code does not take into
+    // consideration selectivity of predicates passed in.  Also, they
+    // assume the rows are unique even if the table is not
+    boolean uniq = RelMdUtil.areColumnsDefinitelyUnique(mq, rel, groupKey);
+    if (uniq) {
+      return NumberUtil.multiply(mq.getRowCount(rel),
+          mq.getSelectivity(rel, predicate));
+    }
+    return null;
+  }
+
+  public Double getDistinctRowCount(Union rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey, RexNode predicate) {
     Double rowCount = 0.0;
     int[] adjustments = new int[rel.getRowType().getFieldCount()];
     RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
@@ -76,10 +95,7 @@ public class RelMdDistinctRowCount {
                     adjustments));
       }
       Double partialRowCount =
-          RelMetadataQuery.getDistinctRowCount(
-              input,
-              groupKey,
-              modifiedPred);
+          mq.getDistinctRowCount(input, groupKey, modifiedPred);
       if (partialRowCount == null) {
         return null;
       }
@@ -88,30 +104,18 @@ public class RelMdDistinctRowCount {
     return rowCount;
   }
 
-  public Double getDistinctRowCount(
-      Sort rel,
-      ImmutableBitSet groupKey,
-      RexNode predicate) {
-    return RelMetadataQuery.getDistinctRowCount(
-        rel.getInput(),
-        groupKey,
-        predicate);
+  public Double getDistinctRowCount(Sort rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey, RexNode predicate) {
+    return mq.getDistinctRowCount(rel.getInput(), groupKey, predicate);
   }
 
-  public Double getDistinctRowCount(
-      Exchange rel,
-      ImmutableBitSet groupKey,
-      RexNode predicate) {
-    return RelMetadataQuery.getDistinctRowCount(
-        rel.getInput(),
-        groupKey,
-        predicate);
+  public Double getDistinctRowCount(Exchange rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey, RexNode predicate) {
+    return mq.getDistinctRowCount(rel.getInput(), groupKey, predicate);
   }
 
-  public Double getDistinctRowCount(
-      Filter rel,
-      ImmutableBitSet groupKey,
-      RexNode predicate) {
+  public Double getDistinctRowCount(Filter rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey, RexNode predicate) {
     if (predicate == null || predicate.isAlwaysTrue()) {
       if (groupKey.isEmpty()) {
         return 1D;
@@ -126,33 +130,22 @@ public class RelMdDistinctRowCount {
             predicate,
             rel.getCondition());
 
-    return RelMetadataQuery.getDistinctRowCount(
-        rel.getInput(),
-        groupKey,
-        unionPreds);
+    return mq.getDistinctRowCount(rel.getInput(), groupKey, unionPreds);
   }
 
-  public Double getDistinctRowCount(
-      Join rel,
-      ImmutableBitSet groupKey,
-      RexNode predicate) {
+  public Double getDistinctRowCount(Join rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey, RexNode predicate) {
     if (predicate == null || predicate.isAlwaysTrue()) {
       if (groupKey.isEmpty()) {
         return 1D;
       }
     }
-    return RelMdUtil.getJoinDistinctRowCount(
-        rel,
-        rel.getJoinType(),
-        groupKey,
-        predicate,
-        false);
+    return RelMdUtil.getJoinDistinctRowCount(mq, rel, rel.getJoinType(),
+        groupKey, predicate, false);
   }
 
-  public Double getDistinctRowCount(
-      SemiJoin rel,
-      ImmutableBitSet groupKey,
-      RexNode predicate) {
+  public Double getDistinctRowCount(SemiJoin rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey, RexNode predicate) {
     if (predicate == null || predicate.isAlwaysTrue()) {
       if (groupKey.isEmpty()) {
         return 1D;
@@ -160,7 +153,7 @@ public class RelMdDistinctRowCount {
     }
     // create a RexNode representing the selectivity of the
     // semijoin filter and pass it to getDistinctRowCount
-    RexNode newPred = RelMdUtil.makeSemiJoinSelectivityRexNode(rel);
+    RexNode newPred = RelMdUtil.makeSemiJoinSelectivityRexNode(mq, rel);
     if (predicate != null) {
       RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
       newPred =
@@ -170,16 +163,11 @@ public class RelMdDistinctRowCount {
               predicate);
     }
 
-    return RelMetadataQuery.getDistinctRowCount(
-        rel.getLeft(),
-        groupKey,
-        newPred);
+    return mq.getDistinctRowCount(rel.getLeft(), groupKey, newPred);
   }
 
-  public Double getDistinctRowCount(
-      Aggregate rel,
-      ImmutableBitSet groupKey,
-      RexNode predicate) {
+  public Double getDistinctRowCount(Aggregate rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey, RexNode predicate) {
     if (predicate == null || predicate.isAlwaysTrue()) {
       if (groupKey.isEmpty()) {
         return 1D;
@@ -187,8 +175,8 @@ public class RelMdDistinctRowCount {
     }
     // determine which predicates can be applied on the child of the
     // aggregate
-    List<RexNode> notPushable = new ArrayList<RexNode>();
-    List<RexNode> pushable = new ArrayList<RexNode>();
+    final List<RexNode> notPushable = new ArrayList<>();
+    final List<RexNode> pushable = new ArrayList<>();
     RelOptUtil.splitFilters(
         rel.getGroupSet(),
         predicate,
@@ -203,10 +191,7 @@ public class RelMdDistinctRowCount {
     RelMdUtil.setAggChildKeys(groupKey, rel, childKey);
 
     Double distinctRowCount =
-        RelMetadataQuery.getDistinctRowCount(
-            rel.getInput(),
-            childKey.build(),
-            childPreds);
+        mq.getDistinctRowCount(rel.getInput(), childKey.build(), childPreds);
     if (distinctRowCount == null) {
       return null;
     } else if (notPushable.isEmpty()) {
@@ -218,10 +203,8 @@ public class RelMdDistinctRowCount {
     }
   }
 
-  public Double getDistinctRowCount(
-      Values rel,
-      ImmutableBitSet groupKey,
-      RexNode predicate) {
+  public Double getDistinctRowCount(Values rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey, RexNode predicate) {
     if (predicate == null || predicate.isAlwaysTrue()) {
       if (groupKey.isEmpty()) {
         return 1D;
@@ -230,14 +213,12 @@ public class RelMdDistinctRowCount {
     Double selectivity = RelMdUtil.guessSelectivity(predicate);
 
     // assume half the rows are duplicates
-    Double nRows = rel.getRows() / 2;
+    Double nRows = rel.estimateRowCount(mq) / 2;
     return RelMdUtil.numDistinctVals(nRows, nRows * selectivity);
   }
 
-  public Double getDistinctRowCount(
-      Project rel,
-      ImmutableBitSet groupKey,
-      RexNode predicate) {
+  public Double getDistinctRowCount(Project rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey, RexNode predicate) {
     if (predicate == null || predicate.isAlwaysTrue()) {
       if (groupKey.isEmpty()) {
         return 1D;
@@ -248,8 +229,8 @@ public class RelMdDistinctRowCount {
     List<RexNode> projExprs = rel.getProjects();
     RelMdUtil.splitCols(projExprs, groupKey, baseCols, projCols);
 
-    List<RexNode> notPushable = new ArrayList<RexNode>();
-    List<RexNode> pushable = new ArrayList<RexNode>();
+    final List<RexNode> notPushable = new ArrayList<>();
+    final List<RexNode> pushable = new ArrayList<>();
     RelOptUtil.splitFilters(
         ImmutableBitSet.range(rel.getRowType().getFieldCount()),
         predicate,
@@ -269,9 +250,7 @@ public class RelMdDistinctRowCount {
       modifiedPred = RelOptUtil.pushPastProject(childPred, rel);
     }
     Double distinctRowCount =
-        RelMetadataQuery.getDistinctRowCount(
-            rel.getInput(),
-            baseCols.build(),
+        mq.getDistinctRowCount(rel.getInput(), baseCols.build(),
             modifiedPred);
 
     if (distinctRowCount == null) {
@@ -291,38 +270,36 @@ public class RelMdDistinctRowCount {
     // multiply by the cardinality of the non-child projection expressions
     for (int bit : projCols.build()) {
       Double subRowCount =
-          RelMdUtil.cardOfProjExpr(rel, projExprs.get(bit));
+          RelMdUtil.cardOfProjExpr(mq, rel, projExprs.get(bit));
       if (subRowCount == null) {
         return null;
       }
       distinctRowCount *= subRowCount;
     }
 
-    return RelMdUtil.numDistinctVals(
-        distinctRowCount,
-        RelMetadataQuery.getRowCount(rel));
+    return RelMdUtil.numDistinctVals(distinctRowCount, mq.getRowCount(rel));
   }
 
-  // Catch-all rule when none of the others apply.
-  public Double getDistinctRowCount(
-      RelNode rel,
-      ImmutableBitSet groupKey,
-      RexNode predicate) {
-    if (predicate == null || predicate.isAlwaysTrue()) {
-      if (groupKey.isEmpty()) {
-        return 1D;
-      }
+  public Double getDistinctRowCount(RelSubset rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey, RexNode predicate) {
+    final RelNode best = rel.getBest();
+    if (best != null) {
+      return mq.getDistinctRowCount(best, groupKey, predicate);
     }
-    // REVIEW zfong 4/19/06 - Broadbase code does not take into
-    // consideration selectivity of predicates passed in.  Also, they
-    // assume the rows are unique even if the table is not
-    boolean uniq = RelMdUtil.areColumnsDefinitelyUnique(rel, groupKey);
-    if (uniq) {
-      return NumberUtil.multiply(
-          RelMetadataQuery.getRowCount(rel),
-          RelMetadataQuery.getSelectivity(rel, predicate));
+    if (!Bug.CALCITE_1048_FIXED) {
+      return getDistinctRowCount((RelNode) rel, mq, groupKey, predicate);
     }
-    return null;
+    Double d = null;
+    for (RelNode r2 : rel.getRels()) {
+      try {
+        Double d2 = mq.getDistinctRowCount(r2, groupKey, predicate);
+        d = NumberUtil.min(d, d2);
+      } catch (CyclicMetadataException e) {
+        // Ignore this relational expression; there will be non-cyclic ones
+        // in this set.
+      }
+    }
+    return d;
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java
index 67ac6a2..f7b5c83 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java
@@ -63,40 +63,40 @@ public class RelMdDistribution {
    * @param rel Relational expression
    * @return Relational expression's distribution
    */
-  public RelDistribution distribution(RelNode rel) {
+  public RelDistribution distribution(RelMetadataQuery mq, RelNode rel) {
     return RelDistributions.SINGLETON;
   }
 
-  public RelDistribution distribution(SingleRel rel) {
-    return RelMetadataQuery.distribution(rel.getInput());
+  public RelDistribution distribution(RelMetadataQuery mq, SingleRel rel) {
+    return mq.distribution(rel.getInput());
   }
 
-  public RelDistribution distribution(BiRel rel) {
-    return RelMetadataQuery.distribution(rel.getLeft());
+  public RelDistribution distribution(RelMetadataQuery mq, BiRel rel) {
+    return mq.distribution(rel.getLeft());
   }
 
-  public RelDistribution distribution(SetOp rel) {
-    return RelMetadataQuery.distribution(rel.getInputs().get(0));
+  public RelDistribution distribution(RelMetadataQuery mq, SetOp rel) {
+    return mq.distribution(rel.getInputs().get(0));
   }
 
-  public RelDistribution distribution(TableScan scan) {
+  public RelDistribution distribution(RelMetadataQuery mq, TableScan scan) {
     return table(scan.getTable());
   }
 
-  public RelDistribution distribution(Project project) {
-    return project(project.getInput(), project.getProjects());
+  public RelDistribution distribution(RelMetadataQuery mq, Project project) {
+    return project(mq, project.getInput(), project.getProjects());
   }
 
-  public RelDistribution distribution(Values values) {
+  public RelDistribution distribution(RelMetadataQuery mq, Values values) {
     return values(values.getRowType(), values.getTuples());
   }
 
-  public RelDistribution distribution(Exchange exchange) {
+  public RelDistribution distribution(RelMetadataQuery mq, Exchange exchange) {
     return exchange(exchange.distribution);
   }
 
-  public RelDistribution distribution(HepRelVertex rel) {
-    return RelMetadataQuery.distribution(rel.getCurrentRel());
+  public RelDistribution distribution(RelMetadataQuery mq, HepRelVertex rel) {
+    return mq.distribution(rel.getCurrentRel());
   }
 
   // Helper methods
@@ -109,34 +109,33 @@ public class RelMdDistribution {
 
   /** Helper method to determine a
    * {@link Sort}'s distribution. */
-  public static RelDistribution sort(RelNode input) {
-    return RelMetadataQuery.distribution(input);
+  public static RelDistribution sort(RelMetadataQuery mq, RelNode input) {
+    return mq.distribution(input);
   }
 
   /** Helper method to determine a
    * {@link Filter}'s distribution. */
-  public static RelDistribution filter(RelNode input) {
-    return RelMetadataQuery.distribution(input);
+  public static RelDistribution filter(RelMetadataQuery mq, RelNode input) {
+    return mq.distribution(input);
   }
 
   /** Helper method to determine a
    * limit's distribution. */
-  public static RelDistribution limit(RelNode input) {
-    return RelMetadataQuery.distribution(input);
+  public static RelDistribution limit(RelMetadataQuery mq, RelNode input) {
+    return mq.distribution(input);
   }
 
   /** Helper method to determine a
    * {@link org.apache.calcite.rel.core.Calc}'s distribution. */
-  public static RelDistribution calc(RelNode input,
+  public static RelDistribution calc(RelMetadataQuery mq, RelNode input,
       RexProgram program) {
     throw new AssertionError(); // TODO:
   }
 
   /** Helper method to determine a {@link Project}'s collation. */
-  public static RelDistribution project(RelNode input,
+  public static RelDistribution project(RelMetadataQuery mq, RelNode input,
       List<? extends RexNode> projects) {
-    final RelDistribution inputDistribution =
-        RelMetadataQuery.distribution(input);
+    final RelDistribution inputDistribution = mq.distribution(input);
     final Mappings.TargetMapping mapping =
         Project.getPartialMapping(input.getRowType().getFieldCount(),
             projects);

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java
index b0d926e..bf51f2f 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java
@@ -36,8 +36,14 @@ public class RelMdExplainVisibility {
 
   //~ Methods ----------------------------------------------------------------
 
-  // Catch-all rule when none of the others apply.
-  public Boolean isVisibleInExplain(RelNode rel, SqlExplainLevel explainLevel) {
+  /** Catch-all implementation for
+   * {@link BuiltInMetadata.ExplainVisibility#isVisibleInExplain(SqlExplainLevel)},
+   * invoked using reflection.
+   *
+   * @see org.apache.calcite.rel.metadata.RelMetadataQuery#isVisibleInExplain(RelNode, SqlExplainLevel)
+   */
+  public Boolean isVisibleInExplain(RelNode rel, RelMetadataQuery mq,
+      SqlExplainLevel explainLevel) {
     // 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/RelMdMaxRowCount.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java
index 1d2a378..ab9739f 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.rel.metadata;
 
+import org.apache.calcite.adapter.enumerable.EnumerableLimit;
 import org.apache.calcite.plan.volcano.RelSubset;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Aggregate;
@@ -29,7 +30,9 @@ import org.apache.calcite.rel.core.TableScan;
 import org.apache.calcite.rel.core.Union;
 import org.apache.calcite.rel.core.Values;
 import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.util.Bug;
 import org.apache.calcite.util.BuiltInMethod;
+import org.apache.calcite.util.Util;
 
 /**
  * RelMdMaxRowCount supplies a default implementation of
@@ -42,10 +45,10 @@ public class RelMdMaxRowCount {
 
   //~ Methods ----------------------------------------------------------------
 
-  public Double getMaxRowCount(Union rel) {
+  public Double getMaxRowCount(Union rel, RelMetadataQuery mq) {
     double rowCount = 0.0;
     for (RelNode input : rel.getInputs()) {
-      Double partialRowCount = RelMetadataQuery.getMaxRowCount(input);
+      Double partialRowCount = mq.getMaxRowCount(input);
       if (partialRowCount == null) {
         return null;
       }
@@ -54,11 +57,11 @@ public class RelMdMaxRowCount {
     return rowCount;
   }
 
-  public Double getMaxRowCount(Intersect rel) {
+  public Double getMaxRowCount(Intersect rel, RelMetadataQuery mq) {
     // max row count is the smallest of the inputs
     Double rowCount = null;
     for (RelNode input : rel.getInputs()) {
-      Double partialRowCount = RelMetadataQuery.getMaxRowCount(input);
+      Double partialRowCount = mq.getMaxRowCount(input);
       if (rowCount == null
           || partialRowCount != null && partialRowCount < rowCount) {
         rowCount = partialRowCount;
@@ -67,22 +70,39 @@ public class RelMdMaxRowCount {
     return rowCount;
   }
 
-  public Double getMaxRowCount(Minus rel) {
-    return RelMetadataQuery.getMaxRowCount(rel.getInput(0));
+  public Double getMaxRowCount(Minus rel, RelMetadataQuery mq) {
+    return mq.getMaxRowCount(rel.getInput(0));
   }
 
-  public Double getMaxRowCount(Filter rel) {
-    return RelMetadataQuery.getMaxRowCount(rel.getInput());
+  public Double getMaxRowCount(Filter rel, RelMetadataQuery mq) {
+    return mq.getMaxRowCount(rel.getInput());
   }
 
-  public Double getMaxRowCount(Project rel) {
-    return RelMetadataQuery.getMaxRowCount(rel.getInput());
+  public Double getMaxRowCount(Project rel, RelMetadataQuery mq) {
+    return mq.getMaxRowCount(rel.getInput());
   }
 
-  public Double getMaxRowCount(Sort rel) {
-    Double rowCount = RelMetadataQuery.getMaxRowCount(rel.getInput());
+  public Double getMaxRowCount(Sort rel, RelMetadataQuery mq) {
+    Double rowCount = mq.getMaxRowCount(rel.getInput());
     if (rowCount == null) {
-      return null;
+      rowCount = Double.POSITIVE_INFINITY;
+    }
+    final int offset = rel.offset == null ? 0 : RexLiteral.intValue(rel.offset);
+    rowCount = Math.max(rowCount - offset, 0D);
+
+    if (rel.fetch != null) {
+      final int limit = RexLiteral.intValue(rel.fetch);
+      if (limit < rowCount) {
+        return (double) limit;
+      }
+    }
+    return rowCount;
+  }
+
+  public Double getMaxRowCount(EnumerableLimit rel, RelMetadataQuery mq) {
+    Double rowCount = mq.getMaxRowCount(rel.getInput());
+    if (rowCount == null) {
+      rowCount = Double.POSITIVE_INFINITY;
     }
     final int offset = rel.offset == null ? 0 : RexLiteral.intValue(rel.offset);
     rowCount = Math.max(rowCount - offset, 0D);
@@ -96,21 +116,21 @@ public class RelMdMaxRowCount {
     return rowCount;
   }
 
-  public Double getMaxRowCount(Aggregate rel) {
+  public Double getMaxRowCount(Aggregate rel, RelMetadataQuery mq) {
     if (rel.getGroupSet().isEmpty()) {
       // Aggregate with no GROUP BY always returns 1 row (even on empty table).
       return 1D;
     }
-    final Double rowCount = RelMetadataQuery.getMaxRowCount(rel.getInput());
+    final Double rowCount = mq.getMaxRowCount(rel.getInput());
     if (rowCount == null) {
       return null;
     }
     return rowCount * rel.getGroupSets().size();
   }
 
-  public Double getMaxRowCount(Join rel) {
-    Double left = RelMetadataQuery.getMaxRowCount(rel.getLeft());
-    Double right = RelMetadataQuery.getMaxRowCount(rel.getRight());
+  public Double getMaxRowCount(Join rel, RelMetadataQuery mq) {
+    Double left = mq.getMaxRowCount(rel.getLeft());
+    Double right = mq.getMaxRowCount(rel.getRight());
     if (left == null || right == null) {
       return null;
     }
@@ -123,20 +143,21 @@ public class RelMdMaxRowCount {
     return left * right;
   }
 
-  public Double getMaxRowCount(TableScan rel) {
+  public Double getMaxRowCount(TableScan rel, RelMetadataQuery mq) {
     // For typical tables, there is no upper bound to the number of rows.
     return Double.POSITIVE_INFINITY;
   }
 
-  public Double getMaxRowCount(Values values) {
+  public Double getMaxRowCount(Values values, RelMetadataQuery mq) {
     // For Values, the maximum row count is the actual row count.
     // This is especially useful if Values is empty.
     return (double) values.getTuples().size();
   }
 
-  public Double getMaxRowCount(RelSubset rel) {
-    // FIXME This is a short-term fix for CALCITE-1018. A complete
-    // solution will come with CALCITE-794.
+  public Double getMaxRowCount(RelSubset rel, RelMetadataQuery mq) {
+    // FIXME This is a short-term fix for [CALCITE-1018]. A complete
+    // solution will come with [CALCITE-1048].
+    Util.discard(Bug.CALCITE_1048_FIXED);
     for (RelNode node : rel.getRels()) {
       if (node instanceof Sort) {
         Sort sort = (Sort) node;
@@ -150,7 +171,7 @@ public class RelMdMaxRowCount {
   }
 
   // Catch-all rule when none of the others apply.
-  public Double getMaxRowCount(RelNode rel) {
+  public Double getMaxRowCount(RelNode rel, RelMetadataQuery mq) {
     return null;
   }
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java
index be53b5c..c760b0e 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java
@@ -43,34 +43,34 @@ public class RelMdMemory {
   //~ Methods ----------------------------------------------------------------
 
   /** Catch-all implementation for
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Memory#memory()},
+   * {@link BuiltInMetadata.Memory#memory()},
    * invoked using reflection.
    *
    * @see org.apache.calcite.rel.metadata.RelMetadataQuery#memory
    */
-  public Double memory(RelNode rel) {
+  public Double memory(RelNode rel, RelMetadataQuery mq) {
     return null;
   }
 
   /** Catch-all implementation for
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Memory#cumulativeMemoryWithinPhase()},
+   * {@link BuiltInMetadata.Memory#cumulativeMemoryWithinPhase()},
    * invoked using reflection.
    *
    * @see org.apache.calcite.rel.metadata.RelMetadataQuery#memory
    */
-  public Double cumulativeMemoryWithinPhase(RelNode rel) {
-    Double nullable = RelMetadataQuery.memory(rel);
+  public Double cumulativeMemoryWithinPhase(RelNode rel, RelMetadataQuery mq) {
+    Double nullable = mq.memory(rel);
     if (nullable == null) {
       return null;
     }
-    Boolean isPhaseTransition = RelMetadataQuery.isPhaseTransition(rel);
+    Boolean isPhaseTransition = mq.isPhaseTransition(rel);
     if (isPhaseTransition == null) {
       return null;
     }
     double d = nullable;
     if (!isPhaseTransition) {
       for (RelNode input : rel.getInputs()) {
-        nullable = RelMetadataQuery.cumulativeMemoryWithinPhase(input);
+        nullable = mq.cumulativeMemoryWithinPhase(input);
         if (nullable == null) {
           return null;
         }
@@ -81,15 +81,15 @@ public class RelMdMemory {
   }
 
   /** Catch-all implementation for
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Memory#cumulativeMemoryWithinPhaseSplit()},
+   * {@link BuiltInMetadata.Memory#cumulativeMemoryWithinPhaseSplit()},
    * invoked using reflection.
    *
    * @see org.apache.calcite.rel.metadata.RelMetadataQuery#cumulativeMemoryWithinPhaseSplit
    */
-  public Double cumulativeMemoryWithinPhaseSplit(RelNode rel) {
-    final Double memoryWithinPhase =
-        RelMetadataQuery.cumulativeMemoryWithinPhase(rel);
-    final Integer splitCount = RelMetadataQuery.splitCount(rel);
+  public Double cumulativeMemoryWithinPhaseSplit(RelNode rel,
+      RelMetadataQuery mq) {
+    final Double memoryWithinPhase = mq.cumulativeMemoryWithinPhase(rel);
+    final Integer splitCount = mq.splitCount(rel);
     if (memoryWithinPhase == null || splitCount == null) {
       return null;
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMdParallelism.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdParallelism.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdParallelism.java
index ca9c244..dcd69f1 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdParallelism.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdParallelism.java
@@ -45,34 +45,34 @@ public class RelMdParallelism {
   //~ Methods ----------------------------------------------------------------
 
   /** Catch-all implementation for
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Parallelism#isPhaseTransition()},
+   * {@link BuiltInMetadata.Parallelism#isPhaseTransition()},
    * invoked using reflection.
    *
    * @see org.apache.calcite.rel.metadata.RelMetadataQuery#isPhaseTransition
    */
-  public Boolean isPhaseTransition(RelNode rel) {
+  public Boolean isPhaseTransition(RelNode rel, RelMetadataQuery mq) {
     return false;
   }
 
-  public Boolean isPhaseTransition(TableScan rel) {
+  public Boolean isPhaseTransition(TableScan rel, RelMetadataQuery mq) {
     return true;
   }
 
-  public Boolean isPhaseTransition(Values rel) {
+  public Boolean isPhaseTransition(Values rel, RelMetadataQuery mq) {
     return true;
   }
 
-  public Boolean isPhaseTransition(Exchange rel) {
+  public Boolean isPhaseTransition(Exchange rel, RelMetadataQuery mq) {
     return true;
   }
 
   /** Catch-all implementation for
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Parallelism#splitCount()},
+   * {@link BuiltInMetadata.Parallelism#splitCount()},
    * invoked using reflection.
    *
    * @see org.apache.calcite.rel.metadata.RelMetadataQuery#splitCount
    */
-  public Integer splitCount(RelNode rel) {
+  public Integer splitCount(RelNode rel, RelMetadataQuery mq) {
     return 1;
   }
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java
index 991a488..bde07a9 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java
@@ -53,16 +53,14 @@ public class RelMdPercentageOriginalRows {
 
   private RelMdPercentageOriginalRows() {}
 
-  public Double getPercentageOriginalRows(Aggregate rel) {
+  public Double getPercentageOriginalRows(Aggregate rel, RelMetadataQuery mq) {
     // REVIEW jvs 28-Mar-2006: The assumption here seems to be that
     // aggregation does not apply any filtering, so it does not modify the
     // percentage.  That's very much oversimplified.
-
-    return RelMetadataQuery.getPercentageOriginalRows(
-        rel.getInput());
+    return mq.getPercentageOriginalRows(rel.getInput());
   }
 
-  public Double getPercentageOriginalRows(Union rel) {
+  public Double getPercentageOriginalRows(Union rel, RelMetadataQuery mq) {
     double numerator = 0.0;
     double denominator = 0.0;
 
@@ -77,9 +75,8 @@ public class RelMdPercentageOriginalRows {
     // case where a huge table has been completely filtered away.
 
     for (RelNode input : rel.getInputs()) {
-      double rowCount = RelMetadataQuery.getRowCount(input);
-      double percentage =
-          RelMetadataQuery.getPercentageOriginalRows(input);
+      double rowCount = mq.getRowCount(input);
+      double percentage = mq.getPercentageOriginalRows(input);
       if (percentage != 0.0) {
         denominator += rowCount / percentage;
         numerator += rowCount;
@@ -89,7 +86,7 @@ public class RelMdPercentageOriginalRows {
     return quotientForPercentage(numerator, denominator);
   }
 
-  public Double getPercentageOriginalRows(Join rel) {
+  public Double getPercentageOriginalRows(Join rel, RelMetadataQuery mq) {
     // Assume any single-table filter conditions have already
     // been pushed down.
 
@@ -98,16 +95,13 @@ public class RelMdPercentageOriginalRows {
 
     // REVIEW jvs 28-Mar-2006:  need any special casing for SemiJoin?
 
-    double left = RelMetadataQuery.getPercentageOriginalRows(rel.getLeft());
-
-    double right =
-        RelMetadataQuery.getPercentageOriginalRows(rel.getRight());
-
+    double left = mq.getPercentageOriginalRows(rel.getLeft());
+    double right = mq.getPercentageOriginalRows(rel.getRight());
     return left * right;
   }
 
   // Catch-all rule when none of the others apply.
-  public Double getPercentageOriginalRows(RelNode rel) {
+  public Double getPercentageOriginalRows(RelNode rel, RelMetadataQuery mq) {
     if (rel.getInputs().size() > 1) {
       // No generic formula available for multiple inputs.
       return null;
@@ -120,8 +114,7 @@ public class RelMdPercentageOriginalRows {
 
     RelNode child = rel.getInputs().get(0);
 
-    Double childPercentage =
-        RelMetadataQuery.getPercentageOriginalRows(child);
+    Double childPercentage = mq.getPercentageOriginalRows(child);
     if (childPercentage == null) {
       return null;
     }
@@ -130,9 +123,7 @@ public class RelMdPercentageOriginalRows {
     // filtering is the effect of single-table filters) with the percentage
     // filtering performed by the child.
     Double relPercentage =
-        quotientForPercentage(
-            RelMetadataQuery.getRowCount(rel),
-            RelMetadataQuery.getRowCount(child));
+        quotientForPercentage(mq.getRowCount(rel), mq.getRowCount(child));
     if (relPercentage == null) {
       return null;
     }
@@ -147,22 +138,23 @@ public class RelMdPercentageOriginalRows {
   }
 
   // Ditto for getNonCumulativeCost
-  public RelOptCost getCumulativeCost(RelNode rel) {
-    RelOptCost cost = RelMetadataQuery.getNonCumulativeCost(rel);
+  public RelOptCost getCumulativeCost(RelNode rel, RelMetadataQuery mq) {
+    RelOptCost cost = mq.getNonCumulativeCost(rel);
     List<RelNode> inputs = rel.getInputs();
     for (RelNode input : inputs) {
-      cost = cost.plus(RelMetadataQuery.getCumulativeCost(input));
+      cost = cost.plus(mq.getCumulativeCost(input));
     }
     return cost;
   }
 
-  public RelOptCost getCumulativeCost(EnumerableInterpreter rel) {
-    return RelMetadataQuery.getNonCumulativeCost(rel);
+  public RelOptCost getCumulativeCost(EnumerableInterpreter rel,
+      RelMetadataQuery mq) {
+    return mq.getNonCumulativeCost(rel);
   }
 
   // Ditto for getNonCumulativeCost
-  public RelOptCost getNonCumulativeCost(RelNode rel) {
-    return rel.computeSelfCost(rel.getCluster().getPlanner());
+  public RelOptCost getNonCumulativeCost(RelNode rel, RelMetadataQuery mq) {
+    return rel.computeSelfCost(rel.getCluster().getPlanner(), mq);
   }
 
   private static Double quotientForPercentage(

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java
index 47c0fcc..180883f 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java
@@ -47,28 +47,26 @@ public class RelMdPopulationSize {
 
   //~ Methods ----------------------------------------------------------------
 
-  public Double getPopulationSize(Filter rel, ImmutableBitSet groupKey) {
-    return RelMetadataQuery.getPopulationSize(
-        rel.getInput(),
-        groupKey);
+  public Double getPopulationSize(Filter rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey) {
+    return mq.getPopulationSize(rel.getInput(), groupKey);
   }
 
-  public Double getPopulationSize(Sort rel, ImmutableBitSet groupKey) {
-    return RelMetadataQuery.getPopulationSize(
-        rel.getInput(),
-        groupKey);
+  public Double getPopulationSize(Sort rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey) {
+    return mq.getPopulationSize(rel.getInput(), groupKey);
   }
 
-  public Double getPopulationSize(Exchange rel, ImmutableBitSet groupKey) {
-    return RelMetadataQuery.getPopulationSize(
-        rel.getInput(),
-        groupKey);
+  public Double getPopulationSize(Exchange rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey) {
+    return mq.getPopulationSize(rel.getInput(), groupKey);
   }
 
-  public Double getPopulationSize(Union rel, ImmutableBitSet groupKey) {
+  public Double getPopulationSize(Union rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey) {
     Double population = 0.0;
     for (RelNode input : rel.getInputs()) {
-      Double subPop = RelMetadataQuery.getPopulationSize(input, groupKey);
+      Double subPop = mq.getPopulationSize(input, groupKey);
       if (subPop == null) {
         return null;
       }
@@ -77,35 +75,38 @@ public class RelMdPopulationSize {
     return population;
   }
 
-  public Double getPopulationSize(Join rel, ImmutableBitSet groupKey) {
-    return RelMdUtil.getJoinPopulationSize(rel, groupKey);
+  public Double getPopulationSize(Join rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey) {
+    return RelMdUtil.getJoinPopulationSize(mq, rel, groupKey);
   }
 
-  public Double getPopulationSize(SemiJoin rel, ImmutableBitSet groupKey) {
-    return RelMetadataQuery.getPopulationSize(rel.getLeft(), groupKey);
+  public Double getPopulationSize(SemiJoin rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey) {
+    return mq.getPopulationSize(rel.getLeft(), groupKey);
   }
 
-  public Double getPopulationSize(Aggregate rel, ImmutableBitSet groupKey) {
+  public Double getPopulationSize(Aggregate rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey) {
     ImmutableBitSet.Builder childKey = ImmutableBitSet.builder();
     RelMdUtil.setAggChildKeys(groupKey, rel, childKey);
-    return RelMetadataQuery.getPopulationSize(rel.getInput(), childKey.build());
+    return mq.getPopulationSize(rel.getInput(), childKey.build());
   }
 
-  public Double getPopulationSize(Values rel, ImmutableBitSet groupKey) {
+  public Double getPopulationSize(Values rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey) {
     // assume half the rows are duplicates
-    return rel.getRows() / 2;
+    return rel.estimateRowCount(mq) / 2;
   }
 
-  public Double getPopulationSize(Project rel, ImmutableBitSet groupKey) {
+  public Double getPopulationSize(Project rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey) {
     ImmutableBitSet.Builder baseCols = ImmutableBitSet.builder();
     ImmutableBitSet.Builder projCols = ImmutableBitSet.builder();
     List<RexNode> projExprs = rel.getProjects();
     RelMdUtil.splitCols(projExprs, groupKey, baseCols, projCols);
 
     Double population =
-        RelMetadataQuery.getPopulationSize(
-            rel.getInput(),
-            baseCols.build());
+        mq.getPopulationSize(rel.getInput(), baseCols.build());
     if (population == null) {
       return null;
     }
@@ -118,7 +119,7 @@ public class RelMdPopulationSize {
 
     for (int bit : projCols.build()) {
       Double subRowCount =
-          RelMdUtil.cardOfProjExpr(rel, projExprs.get(bit));
+          RelMdUtil.cardOfProjExpr(mq, rel, projExprs.get(bit));
       if (subRowCount == null) {
         return null;
       }
@@ -128,22 +129,26 @@ public class RelMdPopulationSize {
     // REVIEW zfong 6/22/06 - Broadbase did not have the call to
     // numDistinctVals.  This is needed; otherwise, population can be
     // larger than the number of rows in the RelNode.
-    return RelMdUtil.numDistinctVals(
-        population,
-        RelMetadataQuery.getRowCount(rel));
+    return RelMdUtil.numDistinctVals(population, mq.getRowCount(rel));
   }
 
-  // Catch-all rule when none of the others apply.
-  public Double getPopulationSize(RelNode rel, ImmutableBitSet groupKey) {
+  /** Catch-all implementation for
+   * {@link BuiltInMetadata.PopulationSize#getPopulationSize(ImmutableBitSet)},
+   * invoked using reflection.
+   *
+   * @see org.apache.calcite.rel.metadata.RelMetadataQuery#getPopulationSize(RelNode, ImmutableBitSet)
+   */
+  public Double getPopulationSize(RelNode rel, RelMetadataQuery mq,
+      ImmutableBitSet groupKey) {
     // if the keys are unique, return the row count; otherwise, we have
     // no further information on which to return any legitimate value
 
     // REVIEW zfong 4/11/06 - Broadbase code returns the product of each
     // unique key, which would result in the population being larger
     // than the total rows in the relnode
-    boolean uniq = RelMdUtil.areColumnsDefinitelyUnique(rel, groupKey);
+    boolean uniq = RelMdUtil.areColumnsDefinitelyUnique(mq, rel, groupKey);
     if (uniq) {
-      return RelMetadataQuery.getRowCount(rel);
+      return mq.getRowCount(rel);
     }
 
     return null;

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
index 2119abf..4b1a894 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
@@ -21,6 +21,7 @@ import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.linq4j.function.Predicate1;
 import org.apache.calcite.plan.RelOptPredicateList;
 import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.plan.hep.HepRelVertex;
 import org.apache.calcite.plan.volcano.RelSubset;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Aggregate;
@@ -120,15 +121,26 @@ public class RelMdPredicates {
 
   private static final List<RexNode> EMPTY_LIST = ImmutableList.of();
 
-  // Catch-all rule when none of the others apply.
-  public RelOptPredicateList getPredicates(RelNode rel) {
+  /** Catch-all implementation for
+   * {@link BuiltInMetadata.Predicates#getPredicates()},
+   * invoked using reflection.
+   *
+   * @see org.apache.calcite.rel.metadata.RelMetadataQuery#getPulledUpPredicates(RelNode)
+   */
+  public RelOptPredicateList getPredicates(RelNode rel, RelMetadataQuery mq) {
     return RelOptPredicateList.EMPTY;
   }
 
+  public RelOptPredicateList getPredicates(HepRelVertex rel,
+      RelMetadataQuery mq) {
+    return mq.getPulledUpPredicates(rel.getCurrentRel());
+  }
+
   /**
    * Infers predicates for a table scan.
    */
-  public RelOptPredicateList getPredicates(TableScan table) {
+  public RelOptPredicateList getPredicates(TableScan table,
+      RelMetadataQuery mq) {
     return RelOptPredicateList.EMPTY;
   }
 
@@ -144,24 +156,23 @@ public class RelMdPredicates {
    * is not in the projection list.
    *
    * <pre>
-   * childPullUpExprs:      {a &gt; 7, b + c &lt; 10, a + e = 9}
+   * inputPullUpExprs:      {a &gt; 7, b + c &lt; 10, a + e = 9}
    * projectionExprs:       {a, b, c, e / 2}
    * projectionPullupExprs: {a &gt; 7, b + c &lt; 10}
    * </pre>
    *
    * </ol>
    */
-  public RelOptPredicateList getPredicates(Project project) {
-    RelNode child = project.getInput();
+  public RelOptPredicateList getPredicates(Project project,
+      RelMetadataQuery mq) {
+    final RelNode input = project.getInput();
     final RexBuilder rexBuilder = project.getCluster().getRexBuilder();
-    RelOptPredicateList childInfo =
-        RelMetadataQuery.getPulledUpPredicates(child);
-
-    List<RexNode> projectPullUpPredicates = new ArrayList<RexNode>();
+    final RelOptPredicateList inputInfo = mq.getPulledUpPredicates(input);
+    final List<RexNode> projectPullUpPredicates = new ArrayList<>();
 
     ImmutableBitSet.Builder columnsMappedBuilder = ImmutableBitSet.builder();
     Mapping m = Mappings.create(MappingType.PARTIAL_FUNCTION,
-        child.getRowType().getFieldCount(),
+        input.getRowType().getFieldCount(),
         project.getRowType().getFieldCount());
 
     for (Ord<RexNode> o : Ord.zip(project.getProjects())) {
@@ -175,10 +186,10 @@ public class RelMdPredicates {
     // Go over childPullUpPredicates. If a predicate only contains columns in
     // 'columnsMapped' construct a new predicate based on mapping.
     final ImmutableBitSet columnsMapped = columnsMappedBuilder.build();
-    for (RexNode r : childInfo.pulledUpPredicates) {
+    for (RexNode r : inputInfo.pulledUpPredicates) {
       ImmutableBitSet rCols = RelOptUtil.InputFinder.bits(r);
       if (columnsMapped.contains(rCols)) {
-        r = r.accept(new RexPermuteInputsShuttle(m, child));
+        r = r.accept(new RexPermuteInputsShuttle(m, input));
         projectPullUpPredicates.add(r);
       }
     }
@@ -203,29 +214,27 @@ public class RelMdPredicates {
   }
 
   /**
-   * Add the Filter condition to the pulledPredicates list from the child.
+   * Add the Filter condition to the pulledPredicates list from the input.
    */
-  public RelOptPredicateList getPredicates(Filter filter) {
-    RelNode child = filter.getInput();
-    RelOptPredicateList childInfo =
-        RelMetadataQuery.getPulledUpPredicates(child);
+  public RelOptPredicateList getPredicates(Filter filter, RelMetadataQuery mq) {
+    final RelNode input = filter.getInput();
+    final RelOptPredicateList inputInfo = mq.getPulledUpPredicates(input);
 
-    return Util.first(childInfo, RelOptPredicateList.EMPTY)
+    return Util.first(inputInfo, RelOptPredicateList.EMPTY)
         .union(
             RelOptPredicateList.of(
                 RelOptUtil.conjunctions(filter.getCondition())));
   }
 
   /** Infers predicates for a {@link org.apache.calcite.rel.core.SemiJoin}. */
-  public RelOptPredicateList getPredicates(SemiJoin semiJoin) {
+  public RelOptPredicateList getPredicates(SemiJoin semiJoin,
+      RelMetadataQuery mq) {
     RexBuilder rB = semiJoin.getCluster().getRexBuilder();
-    RelNode left = semiJoin.getInput(0);
-    RelNode right = semiJoin.getInput(1);
+    final RelNode left = semiJoin.getInput(0);
+    final RelNode right = semiJoin.getInput(1);
 
-    RelOptPredicateList leftInfo =
-        RelMetadataQuery.getPulledUpPredicates(left);
-    RelOptPredicateList rightInfo =
-        RelMetadataQuery.getPulledUpPredicates(right);
+    final RelOptPredicateList leftInfo = mq.getPulledUpPredicates(left);
+    final RelOptPredicateList rightInfo = mq.getPulledUpPredicates(right);
 
     JoinConditionBasedPredicateInference jI =
         new JoinConditionBasedPredicateInference(semiJoin,
@@ -236,15 +245,13 @@ public class RelMdPredicates {
   }
 
   /** Infers predicates for a {@link org.apache.calcite.rel.core.Join}. */
-  public RelOptPredicateList getPredicates(Join join) {
+  public RelOptPredicateList getPredicates(Join join, RelMetadataQuery mq) {
     RexBuilder rB = join.getCluster().getRexBuilder();
     RelNode left = join.getInput(0);
     RelNode right = join.getInput(1);
 
-    RelOptPredicateList leftInfo =
-        RelMetadataQuery.getPulledUpPredicates(left);
-    RelOptPredicateList rightInfo =
-        RelMetadataQuery.getPulledUpPredicates(right);
+    final RelOptPredicateList leftInfo = mq.getPulledUpPredicates(left);
+    final RelOptPredicateList rightInfo = mq.getPulledUpPredicates(right);
 
     JoinConditionBasedPredicateInference jI =
         new JoinConditionBasedPredicateInference(join,
@@ -262,31 +269,29 @@ public class RelMdPredicates {
    * GroupSet. For e.g.
    *
    * <pre>
-   * childPullUpExprs : { a &gt; 7, b + c &lt; 10, a + e = 9}
+   * inputPullUpExprs : { a &gt; 7, b + c &lt; 10, a + e = 9}
    * groupSet         : { a, b}
    * pulledUpExprs    : { a &gt; 7}
    * </pre>
    */
-  public RelOptPredicateList getPredicates(Aggregate agg) {
-    RelNode child = agg.getInput();
-    RelOptPredicateList childInfo =
-        RelMetadataQuery.getPulledUpPredicates(child);
-
-    List<RexNode> aggPullUpPredicates = new ArrayList<RexNode>();
+  public RelOptPredicateList getPredicates(Aggregate agg, RelMetadataQuery mq) {
+    final RelNode input = agg.getInput();
+    final RelOptPredicateList inputInfo = mq.getPulledUpPredicates(input);
+    final List<RexNode> aggPullUpPredicates = new ArrayList<>();
 
     ImmutableBitSet groupKeys = agg.getGroupSet();
     Mapping m = Mappings.create(MappingType.PARTIAL_FUNCTION,
-        child.getRowType().getFieldCount(), agg.getRowType().getFieldCount());
+        input.getRowType().getFieldCount(), agg.getRowType().getFieldCount());
 
     int i = 0;
     for (int j : groupKeys) {
       m.set(j, i++);
     }
 
-    for (RexNode r : childInfo.pulledUpPredicates) {
+    for (RexNode r : inputInfo.pulledUpPredicates) {
       ImmutableBitSet rCols = RelOptUtil.InputFinder.bits(r);
       if (groupKeys.contains(rCols)) {
-        r = r.accept(new RexPermuteInputsShuttle(m, child));
+        r = r.accept(new RexPermuteInputsShuttle(m, input));
         aggPullUpPredicates.add(r);
       }
     }
@@ -298,11 +303,11 @@ public class RelMdPredicates {
    *
    * <p>The pulled up expression is a disjunction of its children's predicates.
    */
-  public RelOptPredicateList getPredicates(Union union) {
+  public RelOptPredicateList getPredicates(Union union, RelMetadataQuery mq) {
     RexBuilder rB = union.getCluster().getRexBuilder();
     List<RexNode> orList = Lists.newArrayList();
     for (RelNode input : union.getInputs()) {
-      RelOptPredicateList info = RelMetadataQuery.getPulledUpPredicates(input);
+      RelOptPredicateList info = mq.getPulledUpPredicates(input);
       if (info.pulledUpPredicates.isEmpty()) {
         return RelOptPredicateList.EMPTY;
       }
@@ -321,27 +326,29 @@ public class RelMdPredicates {
   /**
    * Infers predicates for a Sort.
    */
-  public RelOptPredicateList getPredicates(Sort sort) {
-    RelNode child = sort.getInput();
-    return RelMetadataQuery.getPulledUpPredicates(child);
+  public RelOptPredicateList getPredicates(Sort sort, RelMetadataQuery mq) {
+    RelNode input = sort.getInput();
+    return mq.getPulledUpPredicates(input);
   }
 
   /**
    * Infers predicates for an Exchange.
    */
-  public RelOptPredicateList getPredicates(Exchange exchange) {
-    RelNode child = exchange.getInput();
-    return RelMetadataQuery.getPulledUpPredicates(child);
+  public RelOptPredicateList getPredicates(Exchange exchange,
+      RelMetadataQuery mq) {
+    RelNode input = exchange.getInput();
+    return mq.getPulledUpPredicates(input);
   }
 
   /** @see RelMetadataQuery#getPulledUpPredicates(RelNode) */
-  public RelOptPredicateList getPredicates(RelSubset r) {
-    if (!Bug.CALCITE_794_FIXED) {
+  public RelOptPredicateList getPredicates(RelSubset r,
+      RelMetadataQuery mq) {
+    if (!Bug.CALCITE_1048_FIXED) {
       return RelOptPredicateList.EMPTY;
     }
     RelOptPredicateList list = null;
     for (RelNode r2 : r.getRels()) {
-      RelOptPredicateList list2 = RelMetadataQuery.getPulledUpPredicates(r2);
+      RelOptPredicateList list2 = mq.getPulledUpPredicates(r2);
       if (list2 != null) {
         list = list == null ? list2 : list.union(list2);
       }
@@ -409,7 +416,7 @@ public class RelMdPredicates {
           nSysFields + nFieldsLeft + nFieldsRight);
 
       exprFields = Maps.newHashMap();
-      allExprsDigests = new HashSet<String>();
+      allExprsDigests = new HashSet<>();
 
       if (lPreds == null) {
         leftChildPredicates = null;
@@ -440,7 +447,7 @@ public class RelMdPredicates {
       }
 
       equivalence = Maps.newTreeMap();
-      equalityPredicates = new HashSet<String>();
+      equalityPredicates = new HashSet<>();
       for (int i = 0; i < nSysFields + nFieldsLeft + nFieldsRight; i++) {
         equivalence.put(i, BitSets.of(i));
       }
@@ -454,11 +461,13 @@ public class RelMdPredicates {
               compose(rexBuilder, ImmutableList.of(joinRel.getCondition())));
 
       final EquivalenceFinder eF = new EquivalenceFinder();
-      new ArrayList<Void>(Lists.transform(exprs, new Function<RexNode, Void>() {
-        public Void apply(RexNode input) {
-          return input.accept(eF);
-        }
-      }));
+      new ArrayList<>(
+          Lists.transform(exprs,
+              new Function<RexNode, Void>() {
+                public Void apply(RexNode input) {
+                  return input.accept(eF);
+                }
+              }));
 
       equivalence = BitSets.closure(equivalence);
     }
@@ -478,8 +487,8 @@ public class RelMdPredicates {
      */
     public RelOptPredicateList inferPredicates(
         boolean includeEqualityInference) {
-      List<RexNode> inferredPredicates = new ArrayList<RexNode>();
-      Set<String> allExprsDigests = new HashSet<String>(this.allExprsDigests);
+      final List<RexNode> inferredPredicates = new ArrayList<>();
+      final Set<String> allExprsDigests = new HashSet<>(this.allExprsDigests);
       final JoinRelType joinType = joinRel.getJoinType();
       switch (joinType) {
       case INNER:
@@ -509,9 +518,8 @@ public class RelMdPredicates {
           nSysFields + nFieldsLeft, 0, nSysFields, nFieldsLeft);
       final RexPermuteInputsShuttle leftPermute =
           new RexPermuteInputsShuttle(leftMapping, joinRel);
-
-      List<RexNode> leftInferredPredicates = new ArrayList<RexNode>();
-      List<RexNode> rightInferredPredicates = new ArrayList<RexNode>();
+      final List<RexNode> leftInferredPredicates = new ArrayList<>();
+      final List<RexNode> rightInferredPredicates = new ArrayList<>();
 
       for (RexNode iP : inferredPredicates) {
         ImmutableBitSet iPBitSet = RelOptUtil.InputFinder.bits(iP);

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
index aac3aae..ea647a6 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
@@ -16,20 +16,29 @@
  */
 package org.apache.calcite.rel.metadata;
 
+import org.apache.calcite.adapter.enumerable.EnumerableLimit;
+import org.apache.calcite.plan.volcano.RelSubset;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.SingleRel;
 import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Calc;
 import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.core.Intersect;
+import org.apache.calcite.rel.core.Join;
 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.Sort;
+import org.apache.calcite.rel.core.TableScan;
 import org.apache.calcite.rel.core.Union;
+import org.apache.calcite.rel.core.Values;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.util.Bug;
 import org.apache.calcite.util.BuiltInMethod;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.NumberUtil;
+import org.apache.calcite.util.Util;
 
 /**
  * RelMdRowCount supplies a default implementation of
@@ -42,10 +51,37 @@ public class RelMdRowCount {
 
   //~ Methods ----------------------------------------------------------------
 
-  public Double getRowCount(Union rel) {
-    double rowCount = 0D;
+  /** Catch-all implementation for
+   * {@link BuiltInMetadata.RowCount#getRowCount()},
+   * invoked using reflection.
+   *
+   * @see org.apache.calcite.rel.metadata.RelMetadataQuery#getRowCount(RelNode)
+   */
+  public Double getRowCount(RelNode rel, RelMetadataQuery mq) {
+    return rel.estimateRowCount(mq);
+  }
+
+  public Double getRowCount(RelSubset subset, RelMetadataQuery mq) {
+    if (!Bug.CALCITE_1048_FIXED) {
+      return mq.getRowCount(Util.first(subset.getBest(), subset.getOriginal()));
+    }
+    Double v = null;
+    for (RelNode r : subset.getRels()) {
+      try {
+        v = NumberUtil.min(v, mq.getRowCount(r));
+      } catch (CyclicMetadataException e) {
+        // ignore this rel; there will be other, non-cyclic ones
+      } catch (Throwable e) {
+        e.printStackTrace();
+      }
+    }
+    return Util.first(v, 1e6d); // if set is empty, estimate large
+  }
+
+  public Double getRowCount(Union rel, RelMetadataQuery mq) {
+    double rowCount = 0.0;
     for (RelNode input : rel.getInputs()) {
-      Double partialRowCount = RelMetadataQuery.getRowCount(input);
+      Double partialRowCount = mq.getRowCount(input);
       if (partialRowCount == null) {
         return null;
       }
@@ -54,10 +90,10 @@ public class RelMdRowCount {
     return rowCount;
   }
 
-  public Double getRowCount(Intersect rel) {
+  public Double getRowCount(Intersect rel, RelMetadataQuery mq) {
     Double rowCount = null;
     for (RelNode input : rel.getInputs()) {
-      Double partialRowCount = RelMetadataQuery.getRowCount(input);
+      Double partialRowCount = mq.getRowCount(input);
       if (rowCount == null
           || partialRowCount != null && partialRowCount < rowCount) {
         rowCount = partialRowCount;
@@ -66,10 +102,10 @@ public class RelMdRowCount {
     return rowCount;
   }
 
-  public Double getRowCount(Minus rel) {
+  public Double getRowCount(Minus rel, RelMetadataQuery mq) {
     Double rowCount = null;
     for (RelNode input : rel.getInputs()) {
-      Double partialRowCount = RelMetadataQuery.getRowCount(input);
+      Double partialRowCount = mq.getRowCount(input);
       if (rowCount == null
           || partialRowCount != null && partialRowCount < rowCount) {
         rowCount = partialRowCount;
@@ -78,20 +114,21 @@ public class RelMdRowCount {
     return rowCount;
   }
 
-  public Double getRowCount(Filter rel) {
-    return NumberUtil.multiply(
-        RelMetadataQuery.getSelectivity(
-            rel.getInput(),
-            rel.getCondition()),
-        RelMetadataQuery.getRowCount(rel.getInput()));
+  public Double getRowCount(Filter rel, RelMetadataQuery mq) {
+    return RelMdUtil.estimateFilteredRows(rel.getInput(), rel.getCondition(),
+        mq);
   }
 
-  public Double getRowCount(Project rel) {
-    return RelMetadataQuery.getRowCount(rel.getInput());
+  public Double getRowCount(Calc rel, RelMetadataQuery mq) {
+    return RelMdUtil.estimateFilteredRows(rel.getInput(), rel.getProgram(), mq);
   }
 
-  public Double getRowCount(Sort rel) {
-    Double rowCount = RelMetadataQuery.getRowCount(rel.getInput());
+  public Double getRowCount(Project rel, RelMetadataQuery mq) {
+    return mq.getRowCount(rel.getInput());
+  }
+
+  public Double getRowCount(Sort rel, RelMetadataQuery mq) {
+    Double rowCount = mq.getRowCount(rel.getInput());
     if (rowCount == null) {
       return null;
     }
@@ -107,30 +144,51 @@ public class RelMdRowCount {
     return rowCount;
   }
 
-  public Double getRowCount(SemiJoin rel) {
+  public Double getRowCount(EnumerableLimit rel, RelMetadataQuery mq) {
+    Double rowCount = mq.getRowCount(rel.getInput());
+    if (rowCount == null) {
+      return null;
+    }
+    final int offset = rel.offset == null ? 0 : RexLiteral.intValue(rel.offset);
+    rowCount = Math.max(rowCount - offset, 0D);
+
+    if (rel.fetch != null) {
+      final int limit = RexLiteral.intValue(rel.fetch);
+      if (limit < rowCount) {
+        return (double) limit;
+      }
+    }
+    return rowCount;
+  }
+
+  // Covers Converter, Interpreter
+  public Double getRowCount(SingleRel rel, RelMetadataQuery mq) {
+    return mq.getRowCount(rel.getInput());
+  }
+
+  public Double getRowCount(Join rel, RelMetadataQuery mq) {
+    return RelMdUtil.getJoinRowCount(mq, rel, rel.getCondition());
+  }
+
+  public Double getRowCount(SemiJoin rel, RelMetadataQuery mq) {
     // create a RexNode representing the selectivity of the
     // semijoin filter and pass it to getSelectivity
     RexNode semiJoinSelectivity =
-        RelMdUtil.makeSemiJoinSelectivityRexNode(rel);
+        RelMdUtil.makeSemiJoinSelectivityRexNode(mq, rel);
 
     return NumberUtil.multiply(
-        RelMetadataQuery.getSelectivity(
-            rel.getLeft(),
-            semiJoinSelectivity),
-        RelMetadataQuery.getRowCount(rel.getLeft()));
+        mq.getSelectivity(rel.getLeft(), semiJoinSelectivity),
+        mq.getRowCount(rel.getLeft()));
   }
 
-  public Double getRowCount(Aggregate rel) {
-    ImmutableBitSet groupKey = rel.getGroupSet();
+  public Double getRowCount(Aggregate rel, RelMetadataQuery mq) {
+    ImmutableBitSet groupKey = rel.getGroupSet(); // .range(rel.getGroupCount());
 
-    // rowcount is the cardinality of the group by columns
+    // rowCount is the cardinality of the group by columns
     Double distinctRowCount =
-        RelMetadataQuery.getDistinctRowCount(
-            rel.getInput(),
-            groupKey,
-            null);
+        mq.getDistinctRowCount(rel.getInput(), groupKey, null);
     if (distinctRowCount == null) {
-      distinctRowCount = RelMetadataQuery.getRowCount(rel.getInput()) / 10D;
+      distinctRowCount = mq.getRowCount(rel.getInput()) / 10;
     }
 
     // Grouping sets multiply
@@ -139,9 +197,12 @@ public class RelMdRowCount {
     return distinctRowCount;
   }
 
-  // Catch-all rule when none of the others apply.
-  public Double getRowCount(RelNode rel) {
-    return rel.getRows();
+  public Double getRowCount(TableScan rel, RelMetadataQuery mq) {
+    return rel.estimateRowCount(mq);
+  }
+
+  public Double getRowCount(Values rel, RelMetadataQuery mq) {
+    return rel.estimateRowCount(mq);
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java
index c59a005..b7ffcdd 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java
@@ -50,7 +50,8 @@ public class RelMdSelectivity {
 
   //~ Methods ----------------------------------------------------------------
 
-  public Double getSelectivity(Union rel, RexNode predicate) {
+  public Double getSelectivity(Union rel, RelMetadataQuery mq,
+      RexNode predicate) {
     if ((rel.getInputs().size() == 0) || (predicate == null)) {
       return 1.0;
     }
@@ -60,7 +61,7 @@ public class RelMdSelectivity {
     int[] adjustments = new int[rel.getRowType().getFieldCount()];
     RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
     for (RelNode input : rel.getInputs()) {
-      Double nRows = RelMetadataQuery.getRowCount(input);
+      Double nRows = mq.getRowCount(input);
       if (nRows == null) {
         return null;
       }
@@ -73,7 +74,7 @@ public class RelMdSelectivity {
                   null,
                   input.getRowType().getFieldList(),
                   adjustments));
-      double sel = RelMetadataQuery.getSelectivity(input, modifiedPred);
+      double sel = mq.getSelectivity(input, modifiedPred);
 
       sumRows += nRows;
       sumSelectedRows += nRows * sel;
@@ -85,36 +86,34 @@ public class RelMdSelectivity {
     return sumSelectedRows / sumRows;
   }
 
-  public Double getSelectivity(Sort rel, RexNode predicate) {
-    return RelMetadataQuery.getSelectivity(
-        rel.getInput(),
-        predicate);
+  public Double getSelectivity(Sort rel, RelMetadataQuery mq,
+      RexNode predicate) {
+    return mq.getSelectivity(rel.getInput(), predicate);
   }
 
-  public Double getSelectivity(Filter rel, RexNode predicate) {
+  public Double getSelectivity(Filter rel, RelMetadataQuery mq,
+      RexNode predicate) {
     // Take the difference between the predicate passed in and the
     // predicate in the filter's condition, so we don't apply the
     // selectivity of the filter twice.  If no predicate is passed in,
     // use the filter's condition.
     if (predicate != null) {
-      return RelMetadataQuery.getSelectivity(
-          rel.getInput(),
+      return mq.getSelectivity(rel.getInput(),
           RelMdUtil.minusPreds(
               rel.getCluster().getRexBuilder(),
               predicate,
               rel.getCondition()));
     } else {
-      return RelMetadataQuery.getSelectivity(
-          rel.getInput(),
-          rel.getCondition());
+      return mq.getSelectivity(rel.getInput(), rel.getCondition());
     }
   }
 
-  public Double getSelectivity(SemiJoin rel, RexNode predicate) {
+  public Double getSelectivity(SemiJoin rel, RelMetadataQuery mq,
+      RexNode predicate) {
     // create a RexNode representing the selectivity of the
     // semijoin filter and pass it to getSelectivity
     RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
-    RexNode newPred = RelMdUtil.makeSemiJoinSelectivityRexNode(rel);
+    RexNode newPred = RelMdUtil.makeSemiJoinSelectivityRexNode(mq, rel);
     if (predicate != null) {
       newPred =
           rexBuilder.makeCall(
@@ -123,14 +122,13 @@ public class RelMdSelectivity {
               predicate);
     }
 
-    return RelMetadataQuery.getSelectivity(
-        rel.getLeft(),
-        newPred);
+    return mq.getSelectivity(rel.getLeft(), newPred);
   }
 
-  public Double getSelectivity(Aggregate rel, RexNode predicate) {
-    List<RexNode> notPushable = new ArrayList<RexNode>();
-    List<RexNode> pushable = new ArrayList<RexNode>();
+  public Double getSelectivity(Aggregate rel, RelMetadataQuery mq,
+      RexNode predicate) {
+    final List<RexNode> notPushable = new ArrayList<>();
+    final List<RexNode> pushable = new ArrayList<>();
     RelOptUtil.splitFilters(
         rel.getGroupSet(),
         predicate,
@@ -140,10 +138,7 @@ public class RelMdSelectivity {
     RexNode childPred =
         RexUtil.composeConjunction(rexBuilder, pushable, true);
 
-    Double selectivity =
-        RelMetadataQuery.getSelectivity(
-            rel.getInput(),
-            childPred);
+    Double selectivity = mq.getSelectivity(rel.getInput(), childPred);
     if (selectivity == null) {
       return null;
     } else {
@@ -153,9 +148,10 @@ public class RelMdSelectivity {
     }
   }
 
-  public Double getSelectivity(Project rel, RexNode predicate) {
-    List<RexNode> notPushable = new ArrayList<RexNode>();
-    List<RexNode> pushable = new ArrayList<RexNode>();
+  public Double getSelectivity(Project rel, RelMetadataQuery mq,
+      RexNode predicate) {
+    final List<RexNode> notPushable = new ArrayList<>();
+    final List<RexNode> pushable = new ArrayList<>();
     RelOptUtil.splitFilters(
         ImmutableBitSet.range(rel.getRowType().getFieldCount()),
         predicate,
@@ -171,10 +167,7 @@ public class RelMdSelectivity {
     } else {
       modifiedPred = RelOptUtil.pushPastProject(childPred, rel);
     }
-    Double selectivity =
-        RelMetadataQuery.getSelectivity(
-            rel.getInput(),
-            modifiedPred);
+    Double selectivity = mq.getSelectivity(rel.getInput(), modifiedPred);
     if (selectivity == null) {
       return null;
     } else {
@@ -185,7 +178,8 @@ public class RelMdSelectivity {
   }
 
   // Catch-all rule when none of the others apply.
-  public Double getSelectivity(RelNode rel, RexNode predicate) {
+  public Double getSelectivity(RelNode rel, RelMetadataQuery mq,
+      RexNode predicate) {
     return RelMdUtil.guessSelectivity(predicate);
   }
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
index af60e73..8c2502e 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
@@ -74,14 +74,13 @@ public class RelMdSize {
   //~ Methods ----------------------------------------------------------------
 
   /** Catch-all implementation for
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Size#averageRowSize()},
+   * {@link BuiltInMetadata.Size#averageRowSize()},
    * invoked using reflection.
    *
    * @see org.apache.calcite.rel.metadata.RelMetadataQuery#getAverageRowSize
    */
-  public Double averageRowSize(RelNode rel) {
-    final List<Double> averageColumnSizes =
-        RelMetadataQuery.getAverageColumnSizes(rel);
+  public Double averageRowSize(RelNode rel, RelMetadataQuery mq) {
+    final List<Double> averageColumnSizes = mq.getAverageColumnSizes(rel);
     if (averageColumnSizes == null) {
       return null;
     }
@@ -99,30 +98,30 @@ public class RelMdSize {
   }
 
   /** Catch-all implementation for
-   * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Size#averageColumnSizes()},
+   * {@link BuiltInMetadata.Size#averageColumnSizes()},
    * invoked using reflection.
    *
    * @see org.apache.calcite.rel.metadata.RelMetadataQuery#getAverageColumnSizes
    */
-  public List<Double> averageColumnSizes(RelNode rel) {
+  public List<Double> averageColumnSizes(RelNode rel, RelMetadataQuery mq) {
     return null; // absolutely no idea
   }
 
-  public List<Double> averageColumnSizes(Filter rel) {
-    return RelMetadataQuery.getAverageColumnSizes(rel.getInput());
+  public List<Double> averageColumnSizes(Filter rel, RelMetadataQuery mq) {
+    return mq.getAverageColumnSizes(rel.getInput());
   }
 
-  public List<Double> averageColumnSizes(Sort rel) {
-    return RelMetadataQuery.getAverageColumnSizes(rel.getInput());
+  public List<Double> averageColumnSizes(Sort rel, RelMetadataQuery mq) {
+    return mq.getAverageColumnSizes(rel.getInput());
   }
 
-  public List<Double> averageColumnSizes(Exchange rel) {
-    return RelMetadataQuery.getAverageColumnSizes(rel.getInput());
+  public List<Double> averageColumnSizes(Exchange rel, RelMetadataQuery mq) {
+    return mq.getAverageColumnSizes(rel.getInput());
   }
 
-  public List<Double> averageColumnSizes(Project rel) {
+  public List<Double> averageColumnSizes(Project rel, RelMetadataQuery mq) {
     final List<Double> inputColumnSizes =
-        RelMetadataQuery.getAverageColumnSizesNotNull(rel.getInput());
+        mq.getAverageColumnSizesNotNull(rel.getInput());
     final ImmutableNullableList.Builder<Double> sizes =
         ImmutableNullableList.builder();
     for (RexNode project : rel.getProjects()) {
@@ -131,7 +130,7 @@ public class RelMdSize {
     return sizes.build();
   }
 
-  public List<Double> averageColumnSizes(Values rel) {
+  public List<Double> averageColumnSizes(Values rel, RelMetadataQuery mq) {
     final List<RelDataTypeField> fields = rel.getRowType().getFieldList();
     final ImmutableList.Builder<Double> list = ImmutableList.builder();
     for (int i = 0; i < fields.size(); i++) {
@@ -151,7 +150,7 @@ public class RelMdSize {
     return list.build();
   }
 
-  public List<Double> averageColumnSizes(TableScan rel) {
+  public List<Double> averageColumnSizes(TableScan rel, RelMetadataQuery mq) {
     final List<RelDataTypeField> fields = rel.getRowType().getFieldList();
     final ImmutableList.Builder<Double> list = ImmutableList.builder();
     for (RelDataTypeField field : fields) {
@@ -160,9 +159,9 @@ public class RelMdSize {
     return list.build();
   }
 
-  public List<Double> averageColumnSizes(Aggregate rel) {
+  public List<Double> averageColumnSizes(Aggregate rel, RelMetadataQuery mq) {
     final List<Double> inputColumnSizes =
-        RelMetadataQuery.getAverageColumnSizesNotNull(rel.getInput());
+        mq.getAverageColumnSizesNotNull(rel.getInput());
     final ImmutableList.Builder<Double> list = ImmutableList.builder();
     for (int key : rel.getGroupSet()) {
       list.add(inputColumnSizes.get(key));
@@ -173,21 +172,21 @@ public class RelMdSize {
     return list.build();
   }
 
-  public List<Double> averageColumnSizes(SemiJoin rel) {
-    return averageJoinColumnSizes(rel, true);
+  public List<Double> averageColumnSizes(SemiJoin rel, RelMetadataQuery mq) {
+    return averageJoinColumnSizes(rel, mq, true);
   }
 
-  public List<Double> averageColumnSizes(Join rel) {
-    return averageJoinColumnSizes(rel, false);
+  public List<Double> averageColumnSizes(Join rel, RelMetadataQuery mq) {
+    return averageJoinColumnSizes(rel, mq, false);
   }
 
-  private List<Double> averageJoinColumnSizes(Join rel, boolean semijoin) {
+  private List<Double> averageJoinColumnSizes(Join rel, RelMetadataQuery mq,
+      boolean semijoin) {
     final RelNode left = rel.getLeft();
     final RelNode right = rel.getRight();
-    final List<Double> lefts =
-        RelMetadataQuery.getAverageColumnSizes(left);
-    final List<Double> rights = semijoin
-        ? null : RelMetadataQuery.getAverageColumnSizes(right);
+    final List<Double> lefts = mq.getAverageColumnSizes(left);
+    final List<Double> rights =
+        semijoin ? null : mq.getAverageColumnSizes(right);
     if (lefts == null && rights == null) {
       return null;
     }
@@ -205,20 +204,19 @@ public class RelMdSize {
     return ImmutableNullableList.copyOf(sizes);
   }
 
-  public List<Double> averageColumnSizes(Intersect rel) {
-    return RelMetadataQuery.getAverageColumnSizes(rel.getInput(0));
+  public List<Double> averageColumnSizes(Intersect rel, RelMetadataQuery mq) {
+    return mq.getAverageColumnSizes(rel.getInput(0));
   }
 
-  public List<Double> averageColumnSizes(Minus rel) {
-    return RelMetadataQuery.getAverageColumnSizes(rel.getInput(0));
+  public List<Double> averageColumnSizes(Minus rel, RelMetadataQuery mq) {
+    return mq.getAverageColumnSizes(rel.getInput(0));
   }
 
-  public List<Double> averageColumnSizes(Union rel) {
+  public List<Double> averageColumnSizes(Union rel, RelMetadataQuery mq) {
     final int fieldCount = rel.getRowType().getFieldCount();
     List<List<Double>> inputColumnSizeList = Lists.newArrayList();
     for (RelNode input : rel.getInputs()) {
-      final List<Double> inputSizes =
-          RelMetadataQuery.getAverageColumnSizes(input);
+      final List<Double> inputSizes = mq.getAverageColumnSizes(input);
       if (inputSizes != null) {
         inputColumnSizeList.add(inputSizes);
       }