calcite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jh...@apache.org
Subject [15/27] calcite git commit: [CALCITE-794] Detect cycles when computing statistics
Date Mon, 11 Jan 2016 04:47:16 GMT
http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/Calc.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Calc.java b/core/src/main/java/org/apache/calcite/rel/core/Calc.java
index f4067eb..5fe5fcb 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Calc.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Calc.java
@@ -25,7 +25,7 @@ import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.SingleRel;
-import org.apache.calcite.rel.logical.LogicalFilter;
+import org.apache.calcite.rel.metadata.RelMdUtil;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rex.RexLocalRef;
 import org.apache.calcite.rex.RexNode;
@@ -131,17 +131,15 @@ public abstract class Calc extends SingleRel {
     return program;
   }
 
-  public double getRows() {
-    return LogicalFilter.estimateFilteredRows(
-        getInput(),
-        program);
+  @Override public double estimateRowCount(RelMetadataQuery mq) {
+    return RelMdUtil.estimateFilteredRows(getInput(), program, mq);
   }
 
-  public RelOptCost computeSelfCost(RelOptPlanner planner) {
-    double dRows = RelMetadataQuery.getRowCount(this);
-    double dCpu =
-        RelMetadataQuery.getRowCount(getInput())
-            * program.getExprCount();
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+      RelMetadataQuery mq) {
+    double dRows = mq.getRowCount(this);
+    double dCpu = mq.getRowCount(getInput())
+        * program.getExprCount();
     double dIo = 0;
     return planner.getCostFactory().makeCost(dRows, dCpu, dIo);
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/Correlate.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Correlate.java b/core/src/main/java/org/apache/calcite/rel/core/Correlate.java
index 982a762..f985099 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Correlate.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Correlate.java
@@ -185,19 +185,20 @@ public abstract class Correlate extends BiRel {
     return ImmutableSet.of(correlationId);
   }
 
-  @Override public RelOptCost computeSelfCost(RelOptPlanner planner) {
-    double rowCount = RelMetadataQuery.getRowCount(this);
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+      RelMetadataQuery mq) {
+    double rowCount = mq.getRowCount(this);
 
-    final double rightRowCount = right.getRows();
-    final double leftRowCount = left.getRows();
+    final double rightRowCount = right.estimateRowCount(mq);
+    final double leftRowCount = left.estimateRowCount(mq);
     if (Double.isInfinite(leftRowCount) || Double.isInfinite(rightRowCount)) {
       return planner.getCostFactory().makeInfiniteCost();
     }
 
-    Double restartCount = RelMetadataQuery.getRowCount(getLeft());
+    Double restartCount = mq.getRowCount(getLeft());
     // RelMetadataQuery.getCumulativeCost(getRight()); does not work for
     // RelSubset, so we ask planner to cost-estimate right relation
-    RelOptCost rightCost = planner.getCost(getRight());
+    RelOptCost rightCost = planner.getCost(getRight(), mq);
     RelOptCost rescanCost =
         rightCost.multiplyBy(Math.max(1.0, restartCount - 1));
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/Exchange.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Exchange.java b/core/src/main/java/org/apache/calcite/rel/core/Exchange.java
index a9acc86..060fa1c 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Exchange.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Exchange.java
@@ -89,10 +89,11 @@ public abstract class Exchange extends SingleRel {
     return distribution;
   }
 
-  @Override public RelOptCost computeSelfCost(RelOptPlanner planner) {
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+      RelMetadataQuery mq) {
     // Higher cost if rows are wider discourages pushing a project through an
     // exchange.
-    double rowCount = RelMetadataQuery.getRowCount(this);
+    double rowCount = mq.getRowCount(this);
     double bytesPerRow = getRowType().getFieldCount() * 4;
     return planner.getCostFactory().makeCost(
         Util.nLogN(rowCount) * bytesPerRow, rowCount, 0);

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/Filter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Filter.java b/core/src/main/java/org/apache/calcite/rel/core/Filter.java
index e5461e8..5387804 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Filter.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Filter.java
@@ -25,9 +25,9 @@ import org.apache.calcite.rel.RelInput;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.SingleRel;
+import org.apache.calcite.rel.metadata.RelMdUtil;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rex.RexChecker;
-import org.apache.calcite.rex.RexLocalRef;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexProgram;
 import org.apache.calcite.rex.RexShuttle;
@@ -123,37 +123,28 @@ public abstract class Filter extends SingleRel {
     return litmus.succeed();
   }
 
-  public RelOptCost computeSelfCost(RelOptPlanner planner) {
-    double dRows = RelMetadataQuery.getRowCount(this);
-    double dCpu = RelMetadataQuery.getRowCount(getInput());
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+      RelMetadataQuery mq) {
+    double dRows = mq.getRowCount(this);
+    double dCpu = mq.getRowCount(getInput());
     double dIo = 0;
     return planner.getCostFactory().makeCost(dRows, dCpu, dIo);
   }
 
-  // override RelNode
-  public double getRows() {
-    return estimateFilteredRows(
-        getInput(),
-        condition);
+  @Override public double estimateRowCount(RelMetadataQuery mq) {
+    return RelMdUtil.estimateFilteredRows(getInput(), condition, mq);
   }
 
+  @Deprecated // to be removed before 2.0
   public static double estimateFilteredRows(RelNode child, RexProgram program) {
-    // 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);
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    return RelMdUtil.estimateFilteredRows(child, program, mq);
   }
 
+  @Deprecated // to be removed before 2.0
   public static double estimateFilteredRows(RelNode child, RexNode condition) {
-    return RelMetadataQuery.getRowCount(child)
-        * RelMetadataQuery.getSelectivity(child, condition);
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    return RelMdUtil.estimateFilteredRows(child, condition, mq);
   }
 
   public RelWriter explainTerms(RelWriter pw) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/Intersect.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Intersect.java b/core/src/main/java/org/apache/calcite/rel/core/Intersect.java
index 6aecbf8..7495a8f 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Intersect.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Intersect.java
@@ -51,12 +51,11 @@ public abstract class Intersect extends SetOp {
     super(input);
   }
 
-  @Override public double getRows() {
+  @Override public double estimateRowCount(RelMetadataQuery mq) {
     // REVIEW jvs 30-May-2005:  I just pulled this out of a hat.
     double dRows = Double.MAX_VALUE;
     for (RelNode input : inputs) {
-      dRows = Math.min(
-          dRows, RelMetadataQuery.getRowCount(input));
+      dRows = Math.min(dRows, mq.getRowCount(input));
     }
     dRows *= 0.25;
     return dRows;

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/Join.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Join.java b/core/src/main/java/org/apache/calcite/rel/core/Join.java
index 3ac32d2..7c20f14 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Join.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Join.java
@@ -179,22 +179,24 @@ public abstract class Join extends BiRel {
     return litmus.succeed();
   }
 
-  @Override public RelOptCost computeSelfCost(RelOptPlanner planner) {
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+      RelMetadataQuery mq) {
     // REVIEW jvs 9-Apr-2006:  Just for now...
-    double rowCount = RelMetadataQuery.getRowCount(this);
+    double rowCount = mq.getRowCount(this);
     return planner.getCostFactory().makeCost(rowCount, 0, 0);
   }
 
-  /** @deprecated Use {@link RelMdUtil#getJoinRowCount(Join, RexNode)}. */
+  /** @deprecated Use {@link RelMdUtil#getJoinRowCount(RelMetadataQuery, Join, RexNode)}. */
   @Deprecated // to be removed before 2.0
   public static double estimateJoinedRows(
       Join joinRel,
       RexNode condition) {
-    return Util.first(RelMdUtil.getJoinRowCount(joinRel, condition), 1D);
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    return Util.first(RelMdUtil.getJoinRowCount(mq, joinRel, condition), 1D);
   }
 
-  @Override public double getRows() {
-    return Util.first(RelMdUtil.getJoinRowCount(this, condition), 1D);
+  @Override public double estimateRowCount(RelMetadataQuery mq) {
+    return Util.first(RelMdUtil.getJoinRowCount(mq, this, condition), 1D);
   }
 
   @Override public Set<CorrelationId> getVariablesSet() {

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/Minus.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Minus.java b/core/src/main/java/org/apache/calcite/rel/core/Minus.java
index 18c6f60..9950d78 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Minus.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Minus.java
@@ -21,6 +21,7 @@ import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelInput;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.metadata.RelMdUtil;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.sql.SqlKind;
 
 import java.util.List;
@@ -48,8 +49,8 @@ public abstract class Minus extends SetOp {
     super(input);
   }
 
-  @Override public double getRows() {
-    return RelMdUtil.getMinusRowCount(this);
+  @Override public double estimateRowCount(RelMetadataQuery mq) {
+    return RelMdUtil.getMinusRowCount(mq, this);
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/Project.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Project.java b/core/src/main/java/org/apache/calcite/rel/core/Project.java
index 86f4147..e92b04d 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Project.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Project.java
@@ -210,8 +210,9 @@ public abstract class Project extends SingleRel {
     return litmus.succeed();
   }
 
-  public RelOptCost computeSelfCost(RelOptPlanner planner) {
-    double dRows = RelMetadataQuery.getRowCount(getInput());
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+      RelMetadataQuery mq) {
+    double dRows = mq.getRowCount(getInput());
     double dCpu = dRows * exps.size();
     double dIo = 0;
     return planner.getCostFactory().makeCost(dRows, dCpu, dIo);

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/SemiJoin.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/SemiJoin.java b/core/src/main/java/org/apache/calcite/rel/core/SemiJoin.java
index 6db45f1..571e8f6 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/SemiJoin.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/SemiJoin.java
@@ -23,6 +23,7 @@ import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
 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.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexNode;
@@ -94,15 +95,16 @@ public class SemiJoin extends EquiJoin {
         joinInfo.leftKeys, joinInfo.rightKeys);
   }
 
-  @Override public RelOptCost computeSelfCost(RelOptPlanner planner) {
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+      RelMetadataQuery mq) {
     // REVIEW jvs 9-Apr-2006:  Just for now...
     return planner.getCostFactory().makeTinyCost();
   }
 
-  @Override public double getRows() {
-    return Util.first(RelMdUtil.getSemiJoinRowCount(left, right, joinType, condition),
+  @Override public double estimateRowCount(RelMetadataQuery mq) {
+    return Util.first(
+        RelMdUtil.getSemiJoinRowCount(mq, left, right, joinType, condition),
         1D);
-
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/Sort.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Sort.java b/core/src/main/java/org/apache/calcite/rel/core/Sort.java
index 022479b..ec42fbf 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Sort.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Sort.java
@@ -129,10 +129,11 @@ public abstract class Sort extends SingleRel {
   public abstract Sort copy(RelTraitSet traitSet, RelNode newInput,
       RelCollation newCollation, RexNode offset, RexNode fetch);
 
-  @Override public RelOptCost computeSelfCost(RelOptPlanner planner) {
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+      RelMetadataQuery mq) {
     // Higher cost if rows are wider discourages pushing a project through a
     // sort.
-    double rowCount = RelMetadataQuery.getRowCount(this);
+    double rowCount = mq.getRowCount(this);
     double bytesPerRow = getRowType().getFieldCount() * 4;
     return planner.getCostFactory().makeCost(
         Util.nLogN(rowCount) * bytesPerRow, rowCount, 0);

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/TableFunctionScan.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/TableFunctionScan.java b/core/src/main/java/org/apache/calcite/rel/core/TableFunctionScan.java
index bffa50f..7482c39 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/TableFunctionScan.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/TableFunctionScan.java
@@ -146,24 +146,24 @@ public abstract class TableFunctionScan extends AbstractRelNode {
   }
 
   @Override public void replaceInput(int ordinalInParent, RelNode p) {
-    final List<RelNode> newInputs = new ArrayList<RelNode>(inputs);
+    final List<RelNode> newInputs = new ArrayList<>(inputs);
     newInputs.set(ordinalInParent, p);
     inputs = ImmutableList.copyOf(newInputs);
     recomputeDigest();
   }
 
-  @Override public double getRows() {
-    // Calculate result as the sum of the input rowcount estimates,
+  @Override public double estimateRowCount(RelMetadataQuery mq) {
+    // Calculate result as the sum of the input row count estimates,
     // assuming there are any, otherwise use the superclass default.  So
     // for a no-input UDX, behave like an AbstractRelNode; for a one-input
     // UDX, behave like a SingleRel; for a multi-input UDX, behave like
     // UNION ALL.  TODO jvs 10-Sep-2007: UDX-supplied costing metadata.
     if (inputs.size() == 0) {
-      return super.getRows();
+      return super.estimateRowCount(mq);
     }
     double nRows = 0.0;
     for (RelNode input : inputs) {
-      Double d = RelMetadataQuery.getRowCount(input);
+      Double d = mq.getRowCount(input);
       if (d != null) {
         nRows += d;
       }

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/TableModify.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/TableModify.java b/core/src/main/java/org/apache/calcite/rel/core/TableModify.java
index fea3a5e..79b6a0d 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/TableModify.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/TableModify.java
@@ -54,7 +54,7 @@ public abstract class TableModify extends SingleRel {
    * Enumeration of supported modification operations.
    */
   public enum Operation {
-    INSERT, UPDATE, DELETE, MERGE;
+    INSERT, UPDATE, DELETE, MERGE
   }
 
   //~ Instance fields --------------------------------------------------------
@@ -190,10 +190,10 @@ public abstract class TableModify extends SingleRel {
         .item("flattened", flattened);
   }
 
-  // implement RelNode
-  public RelOptCost computeSelfCost(RelOptPlanner planner) {
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+      RelMetadataQuery mq) {
     // REVIEW jvs 21-Apr-2006:  Just for now...
-    double rowCount = RelMetadataQuery.getRowCount(this);
+    double rowCount = mq.getRowCount(this);
     return planner.getCostFactory().makeCost(rowCount, 0, 0);
   }
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/TableScan.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/TableScan.java b/core/src/main/java/org/apache/calcite/rel/core/TableScan.java
index 2b958ef..27f842d 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/TableScan.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/TableScan.java
@@ -27,6 +27,7 @@ import org.apache.calcite.rel.RelInput;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.RelWriter;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexBuilder;
@@ -70,7 +71,7 @@ public abstract class TableScan extends AbstractRelNode {
 
   //~ Methods ----------------------------------------------------------------
 
-  @Override public double getRows() {
+  @Override public double estimateRowCount(RelMetadataQuery mq) {
     return table.getRowCount();
   }
 
@@ -82,7 +83,8 @@ public abstract class TableScan extends AbstractRelNode {
     return table.getCollationList();
   }
 
-  @Override public RelOptCost computeSelfCost(RelOptPlanner planner) {
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+      RelMetadataQuery mq) {
     double dRows = table.getRowCount();
     double dCpu = dRows + 1; // ensure non-zero cost
     double dIo = 0;

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/Union.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Union.java b/core/src/main/java/org/apache/calcite/rel/core/Union.java
index 1907199..f7c6a42 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Union.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Union.java
@@ -20,6 +20,7 @@ import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelInput;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.metadata.RelMdUtil;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.sql.SqlKind;
 
@@ -51,27 +52,19 @@ public abstract class Union extends SetOp {
 
   //~ Methods ----------------------------------------------------------------
 
-  // implement RelNode
-  public double getRows() {
-    double dRows = estimateRowCount(this);
+  @Override public double estimateRowCount(RelMetadataQuery mq) {
+    double dRows = RelMdUtil.getUnionAllRowCount(RelMetadataQuery.instance(),
+        this);
     if (!all) {
       dRows *= 0.5;
     }
     return dRows;
   }
 
-  /**
-   * Helper method for computing row count for UNION ALL.
-   *
-   * @param rel node representing UNION ALL
-   * @return estimated row count for rel
-   */
+  @Deprecated // to be removed before 2.0
   public static double estimateRowCount(RelNode rel) {
-    double dRows = 0;
-    for (RelNode input : rel.getInputs()) {
-      dRows += RelMetadataQuery.getRowCount(input);
-    }
-    return dRows;
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    return RelMdUtil.getUnionAllRowCount(mq, (Union) rel);
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/Values.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Values.java b/core/src/main/java/org/apache/calcite/rel/core/Values.java
index 2f4b6ba..bf99fae 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Values.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Values.java
@@ -73,7 +73,7 @@ public abstract class Values extends AbstractRelNode {
 
   //~ Instance fields --------------------------------------------------------
 
-  protected final ImmutableList<ImmutableList<RexLiteral>> tuples;
+  public final ImmutableList<ImmutableList<RexLiteral>> tuples;
 
   //~ Constructors -----------------------------------------------------------
 
@@ -143,14 +143,13 @@ public abstract class Values extends AbstractRelNode {
     return true;
   }
 
-  // implement RelNode
-  protected RelDataType deriveRowType() {
+  @Override protected RelDataType deriveRowType() {
     return rowType;
   }
 
-  // implement RelNode
-  public RelOptCost computeSelfCost(RelOptPlanner planner) {
-    double dRows = RelMetadataQuery.getRowCount(this);
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+      RelMetadataQuery mq) {
+    double dRows = mq.getRowCount(this);
 
     // Assume CPU is negligible since values are precomputed.
     double dCpu = 1;
@@ -159,7 +158,7 @@ public abstract class Values extends AbstractRelNode {
   }
 
   // implement RelNode
-  public double getRows() {
+  public double estimateRowCount(RelMetadataQuery mq) {
     return tuples.size();
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/core/Window.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Window.java b/core/src/main/java/org/apache/calcite/rel/core/Window.java
index 7efb115..09d9ee7 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Window.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Window.java
@@ -170,14 +170,15 @@ public abstract class Window extends SingleRel {
     return constants;
   }
 
-  public RelOptCost computeSelfCost(RelOptPlanner planner) {
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+      RelMetadataQuery mq) {
     // Cost is proportional to the number of rows and the number of
     // components (groups and aggregate functions). There is
     // no I/O cost.
     //
     // TODO #1. Add memory cost.
     // TODO #2. MIN and MAX have higher CPU cost than SUM and COUNT.
-    final double rowsIn = RelMetadataQuery.getRowCount(getInput());
+    final double rowsIn = mq.getRowCount(getInput());
     int count = groups.size();
     for (Group group : groups) {
       count += group.aggCalls.size();

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java
index 63fb3ab..9386f81 100644
--- a/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java
+++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java
@@ -40,8 +40,8 @@ public class RelWriterImpl implements RelWriter {
   private final SqlExplainLevel detailLevel;
   private final boolean withIdPrefix;
   protected final Spacer spacer = new Spacer();
-  private final List<Pair<String, Object>> values =
-      new ArrayList<Pair<String, Object>>();
+  private final List<Pair<String, Object>> values = new ArrayList<>();
+  protected final RelMetadataQuery mq = RelMetadataQuery.instance();
 
   //~ Constructors -----------------------------------------------------------
 
@@ -59,14 +59,11 @@ public class RelWriterImpl implements RelWriter {
 
   //~ Methods ----------------------------------------------------------------
 
-  protected void explain_(
-      RelNode rel,
+  protected void explain_(RelNode rel,
       List<Pair<String, Object>> values) {
     List<RelNode> inputs = rel.getInputs();
 
-    if (!RelMetadataQuery.isVisibleInExplain(
-        rel,
-        detailLevel)) {
+    if (!mq.isVisibleInExplain(rel, detailLevel)) {
       // render children in place of this, at same level
       explainInputs(inputs);
       return;
@@ -101,9 +98,9 @@ public class RelWriterImpl implements RelWriter {
     switch (detailLevel) {
     case ALL_ATTRIBUTES:
       s.append(": rowcount = ")
-          .append(RelMetadataQuery.getRowCount(rel))
+          .append(mq.getRowCount(rel))
           .append(", cumulative cost = ")
-          .append(RelMetadataQuery.getCumulativeCost(rel));
+          .append(mq.getCumulativeCost(rel));
     }
     switch (detailLevel) {
     case NON_COST_ATTRIBUTES:

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/logical/LogicalCalc.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalCalc.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalCalc.java
index 3ca1645..3341ebf 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalCalc.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalCalc.java
@@ -29,6 +29,7 @@ import org.apache.calcite.rel.core.Calc;
 import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.metadata.RelMdCollation;
 import org.apache.calcite.rel.metadata.RelMdDistribution;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.rules.FilterToCalcRule;
 import org.apache.calcite.rel.rules.ProjectToCalcRule;
 import org.apache.calcite.rex.RexNode;
@@ -90,18 +91,19 @@ public final class LogicalCalc extends Calc {
   public static LogicalCalc create(final RelNode input,
       final RexProgram program) {
     final RelOptCluster cluster = input.getCluster();
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
     final RelTraitSet traitSet = cluster.traitSet()
         .replace(Convention.NONE)
         .replaceIfs(RelCollationTraitDef.INSTANCE,
             new Supplier<List<RelCollation>>() {
               public List<RelCollation> get() {
-                return RelMdCollation.calc(input, program);
+                return RelMdCollation.calc(mq, input, program);
               }
             })
         .replaceIf(RelDistributionTraitDef.INSTANCE,
             new Supplier<RelDistribution>() {
               public RelDistribution get() {
-                return RelMdDistribution.calc(input, program);
+                return RelMdDistribution.calc(mq, input, program);
               }
             });
     return new LogicalCalc(cluster, traitSet, input, program);

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
index d25874e..1c25fba 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
@@ -31,6 +31,7 @@ import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.metadata.RelMdCollation;
 import org.apache.calcite.rel.metadata.RelMdDistribution;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rex.RexNode;
 
 import com.google.common.base.Preconditions;
@@ -106,17 +107,18 @@ public final class LogicalFilter extends Filter {
   public static LogicalFilter create(final RelNode input, RexNode condition,
       ImmutableSet<CorrelationId> variablesSet) {
     final RelOptCluster cluster = input.getCluster();
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
     final RelTraitSet traitSet = cluster.traitSetOf(Convention.NONE)
         .replaceIfs(RelCollationTraitDef.INSTANCE,
             new Supplier<List<RelCollation>>() {
               public List<RelCollation> get() {
-                return RelMdCollation.filter(input);
+                return RelMdCollation.filter(mq, input);
               }
             })
         .replaceIf(RelDistributionTraitDef.INSTANCE,
             new Supplier<RelDistribution>() {
               public RelDistribution get() {
-                return RelMdDistribution.filter(input);
+                return RelMdDistribution.filter(mq, input);
               }
             });
     return new LogicalFilter(cluster, traitSet, input, condition, variablesSet);

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java
index cce5e79..61f2fd7 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java
@@ -27,6 +27,7 @@ import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.metadata.RelMdCollation;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexUtil;
@@ -111,13 +112,14 @@ public final class LogicalProject extends Project {
   public static LogicalProject create(final RelNode input,
       final List<? extends RexNode> projects, RelDataType rowType) {
     final RelOptCluster cluster = input.getCluster();
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
     final RelTraitSet traitSet =
         cluster.traitSet().replace(Convention.NONE)
             .replaceIfs(
                 RelCollationTraitDef.INSTANCE,
                 new Supplier<List<RelCollation>>() {
                   public List<RelCollation> get() {
-                    return RelMdCollation.project(input, projects);
+                    return RelMdCollation.project(mq, input, projects);
                   }
                 });
     return new LogicalProject(cluster, traitSet, input, projects, rowType);

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableFunctionScan.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableFunctionScan.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableFunctionScan.java
index 79102db..ec18685 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableFunctionScan.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableFunctionScan.java
@@ -25,6 +25,7 @@ import org.apache.calcite.rel.RelInput;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.TableFunctionScan;
 import org.apache.calcite.rel.metadata.RelColumnMapping;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
 
@@ -111,7 +112,7 @@ public class LogicalTableFunctionScan extends TableFunctionScan {
         columnMappings);
   }
 
-  public RelOptCost computeSelfCost(RelOptPlanner planner) {
+  public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
     // REVIEW jvs 8-Jan-2006:  what is supposed to be here
     // for an abstract rel?
     return planner.getCostFactory().makeHugeCost();

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
index 889f7e6..6d84d74 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
@@ -26,6 +26,7 @@ import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.core.Values;
 import org.apache.calcite.rel.metadata.RelMdCollation;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.sql.type.SqlTypeName;
@@ -81,11 +82,12 @@ public class LogicalValues extends Values {
   public static LogicalValues create(RelOptCluster cluster,
       final RelDataType rowType,
       final ImmutableList<ImmutableList<RexLiteral>> tuples) {
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
     final RelTraitSet traitSet = cluster.traitSetOf(Convention.NONE)
         .replaceIfs(
             RelCollationTraitDef.INSTANCE, new Supplier<List<RelCollation>>() {
               public List<RelCollation> get() {
-                return RelMdCollation.values(rowType, tuples);
+                return RelMdCollation.values(mq, rowType, tuples);
               }
             });
     return new LogicalValues(cluster, traitSet, rowType, tuples);

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java b/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java
index 81d6eed..87fbbf4 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java
@@ -122,7 +122,7 @@ public abstract class BuiltInMetadata {
     /**
      * Estimates the number of rows which will be returned by a relational
      * expression. The default implementation for this query asks the rel itself
-     * via {@link RelNode#getRows}, but metadata providers can override this
+     * via {@link RelNode#estimateRowCount}, but metadata providers can override this
      * with their own cost models.
      *
      * @return estimated row count, or null if no reliable estimate can be
@@ -131,14 +131,18 @@ public abstract class BuiltInMetadata {
     Double getRowCount();
   }
 
-  /** Metadata about the max number of rows returned by a relational expression. */
+  /** Metadata about the maximum number of rows returned by a relational
+   * expression. */
   public interface MaxRowCount extends Metadata {
     /**
      * Estimates the max number of rows which will be returned by a relational
-     * expression. The default implementation for this query returns Double.POSITIVE_INFINITY,
+     * expression.
+     *
+     * <p>The default implementation for this query returns
+     * {@link Double#POSITIVE_INFINITY},
      * but metadata providers can override this with their own cost models.
      *
-     * @return estimated max row count
+     * @return upper bound on the number of rows returned
      */
     Double getMaxRowCount();
   }
@@ -338,7 +342,7 @@ public abstract class BuiltInMetadata {
      * physical operator implementing this relational expression, and all other
      * operators within the same phase, across all splits.
      *
-     * @see org.apache.calcite.rel.metadata.BuiltInMetadata.Parallelism#splitCount()
+     * @see Parallelism#splitCount()
      */
     Double cumulativeMemoryWithinPhase();
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java
index 516de3a..8438a8d 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java
@@ -19,11 +19,12 @@ package org.apache.calcite.rel.metadata;
 import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.rel.RelNode;
 
-import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 
 import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.util.HashMap;
@@ -37,18 +38,12 @@ import java.util.Map;
 public class CachingRelMetadataProvider implements RelMetadataProvider {
   //~ Instance fields --------------------------------------------------------
 
-  private final Map<List, CacheEntry> cache;
+  private final Map<List, CacheEntry> cache = new HashMap<>();
 
   private final RelMetadataProvider underlyingProvider;
 
   private final RelOptPlanner planner;
 
-  private static final Object NULL_SENTINEL = new Object() {
-    @Override public String toString() {
-      return "{null}";
-    }
-  };
-
   //~ Constructors -----------------------------------------------------------
 
   public CachingRelMetadataProvider(
@@ -56,15 +51,14 @@ public class CachingRelMetadataProvider implements RelMetadataProvider {
       RelOptPlanner planner) {
     this.underlyingProvider = underlyingProvider;
     this.planner = planner;
-
-    cache = new HashMap<List, CacheEntry>();
   }
 
   //~ Methods ----------------------------------------------------------------
 
-  public Function<RelNode, Metadata> apply(Class<? extends RelNode> relClass,
-      final Class<? extends Metadata> metadataClass) {
-    final Function<RelNode, Metadata> function =
+  public <M extends Metadata> UnboundMetadata<M>
+  apply(Class<? extends RelNode> relClass,
+      final Class<? extends M> metadataClass) {
+    final UnboundMetadata<M> function =
         underlyingProvider.apply(relClass, metadataClass);
     if (function == null) {
       return null;
@@ -72,11 +66,13 @@ public class CachingRelMetadataProvider implements RelMetadataProvider {
 
     // TODO jvs 30-Mar-2006: Use meta-metadata to decide which metadata
     // query results can stay fresh until the next Ice Age.
-    return new Function<RelNode, Metadata>() {
-      public Metadata apply(RelNode input) {
-        final Metadata metadata = function.apply(input);
-        return (Metadata) Proxy.newProxyInstance(metadataClass.getClassLoader(),
-            new Class[]{metadataClass}, new CachingInvocationHandler(metadata));
+    return new UnboundMetadata<M>() {
+      public M bind(RelNode rel, RelMetadataQuery mq) {
+        final Metadata metadata = function.bind(rel, mq);
+        return metadataClass.cast(
+            Proxy.newProxyInstance(metadataClass.getClassLoader(),
+                new Class[]{metadataClass},
+                new CachingInvocationHandler(metadata)));
       }
     };
   }
@@ -113,7 +109,7 @@ public class CachingRelMetadataProvider implements RelMetadataProvider {
       if (args != null) {
         for (Object arg : args) {
           // Replace null values because ImmutableList does not allow them.
-          builder.add(arg == null ? NULL_SENTINEL : arg);
+          builder.add(NullSentinel.mask(arg));
         }
       }
       List<Object> key = builder.build();
@@ -129,14 +125,19 @@ public class CachingRelMetadataProvider implements RelMetadataProvider {
       }
 
       // Cache miss or stale.
-      Object result = method.invoke(metadata, args);
-      if (result != null) {
-        entry = new CacheEntry();
-        entry.timestamp = timestamp;
-        entry.result = result;
-        cache.put(key, entry);
+      try {
+        Object result = method.invoke(metadata, args);
+        if (result != null) {
+          entry = new CacheEntry();
+          entry.timestamp = timestamp;
+          entry.result = result;
+          cache.put(key, entry);
+        }
+        return result;
+      } catch (InvocationTargetException e) {
+        Throwables.propagateIfPossible(e.getCause());
+        throw e;
       }
-      return result;
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java
index 445cce4..eb8aec5 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java
@@ -18,13 +18,15 @@ package org.apache.calcite.rel.metadata;
 
 import org.apache.calcite.rel.RelNode;
 
-import com.google.common.base.Function;
+import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
 import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -53,11 +55,12 @@ public class ChainedRelMetadataProvider implements RelMetadataProvider {
 
   //~ Methods ----------------------------------------------------------------
 
-  public Function<RelNode, Metadata> apply(Class<? extends RelNode> relClass,
-      final Class<? extends Metadata> metadataClass) {
-    final List<Function<RelNode, Metadata>> functions = Lists.newArrayList();
+  public <M extends Metadata> UnboundMetadata<M>
+  apply(Class<? extends RelNode> relClass,
+      final Class<? extends M> metadataClass) {
+    final List<UnboundMetadata<M>> functions = new ArrayList<>();
     for (RelMetadataProvider provider : providers) {
-      final Function<RelNode, Metadata> function =
+      final UnboundMetadata<M> function =
           provider.apply(relClass, metadataClass);
       if (function == null) {
         continue;
@@ -70,22 +73,21 @@ public class ChainedRelMetadataProvider implements RelMetadataProvider {
     case 1:
       return functions.get(0);
     default:
-      return new Function<RelNode, Metadata>() {
-        public Metadata apply(RelNode input) {
+      return new UnboundMetadata<M>() {
+        public M bind(RelNode rel, RelMetadataQuery mq) {
           final List<Metadata> metadataList = Lists.newArrayList();
-          for (Function<RelNode, Metadata> function : functions) {
-            final Metadata metadata = function.apply(input);
+          for (UnboundMetadata<M> function : functions) {
+            final Metadata metadata = function.bind(rel, mq);
             if (metadata != null) {
               metadataList.add(metadata);
             }
           }
-          return (Metadata) Proxy.newProxyInstance(
-              metadataClass.getClassLoader(),
-              new Class[]{metadataClass},
-              new ChainedInvocationHandler(metadataList));
+          return metadataClass.cast(
+              Proxy.newProxyInstance(metadataClass.getClassLoader(),
+                  new Class[]{metadataClass},
+                  new ChainedInvocationHandler(metadataList)));
         }
       };
-
     }
   }
 
@@ -106,9 +108,17 @@ public class ChainedRelMetadataProvider implements RelMetadataProvider {
     public Object invoke(Object proxy, Method method, Object[] args)
         throws Throwable {
       for (Metadata metadata : metadataList) {
-        final Object o = method.invoke(metadata, args);
-        if (o != null) {
-          return o;
+        try {
+          final Object o = method.invoke(metadata, args);
+          if (o != null) {
+            return o;
+          }
+        } catch (InvocationTargetException e) {
+          if (e.getCause() instanceof CyclicMetadataException) {
+            continue;
+          }
+          Throwables.propagateIfPossible(e.getCause());
+          throw e;
         }
       }
       return null;

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/CyclicMetadataException.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/CyclicMetadataException.java b/core/src/main/java/org/apache/calcite/rel/metadata/CyclicMetadataException.java
new file mode 100644
index 0000000..d4dd249
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/CyclicMetadataException.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+/**
+ * Exception that indicates that a cycle has been detected while
+ * computing metadata.
+ */
+public class CyclicMetadataException extends RuntimeException {
+  /** Singleton instance. Since this exception is thrown for signaling purposes,
+   * rather than on an actual error, re-using a singleton instance saves the
+   * effort of constructing an exception instance. */
+  @SuppressWarnings("ThrowableInstanceNeverThrown")
+  public static final CyclicMetadataException INSTANCE =
+      new CyclicMetadataException();
+
+  /** Creates a CyclicMetadataException. */
+  private CyclicMetadataException() {
+    super();
+  }
+}
+
+// End CyclicMetadataException.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/Metadata.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/Metadata.java b/core/src/main/java/org/apache/calcite/rel/metadata/Metadata.java
index 34e456f..d5d642d 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/Metadata.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/Metadata.java
@@ -26,7 +26,7 @@ import org.apache.calcite.rel.RelNode;
  * kinds of metadata for particular sub-classes of {@link RelNode}.
  *
  * <p>User code (typically in a planner rule or an implementation of
- * {@link RelNode#computeSelfCost(org.apache.calcite.plan.RelOptPlanner)})
+ * {@link RelNode#computeSelfCost(org.apache.calcite.plan.RelOptPlanner, RelMetadataQuery)})
  * acquires a {@code Metadata} instance by calling {@link RelNode#metadata}.
  *
  * <p>A {@code Metadata} instance already knows which particular {@code RelNode}

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactory.java b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactory.java
index 7651ecd..28ca883 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactory.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactory.java
@@ -31,8 +31,17 @@ import org.apache.calcite.rel.RelNode;
 public interface MetadataFactory {
   /** Returns a metadata interface to get a particular kind of metadata
    * from a particular relational expression. Returns null if that kind of
-   * metadata is not available. */
-  <T extends Metadata> T query(RelNode rel, Class<T> clazz);
+   * metadata is not available.
+   *
+   * @param <M> Metadata type
+   *
+   * @param rel Relational expression
+   * @param mq Metadata query
+   * @param metadataClazz Metadata class
+   * @return Metadata bound to {@code rel} and {@code query}
+   */
+  <M extends Metadata> M query(RelNode rel, RelMetadataQuery mq,
+      Class<M> metadataClazz);
 }
 
 // End MetadataFactory.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactoryImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactoryImpl.java b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactoryImpl.java
index e20d6ce..f5b5717 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactoryImpl.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactoryImpl.java
@@ -19,8 +19,6 @@ package org.apache.calcite.rel.metadata;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.util.Pair;
 
-import com.google.common.base.Function;
-import com.google.common.base.Functions;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
@@ -36,24 +34,27 @@ import java.util.concurrent.ExecutionException;
  */
 public class MetadataFactoryImpl implements MetadataFactory {
   @SuppressWarnings("unchecked")
-  public static final Function<RelNode, Metadata> DUMMY =
-      (Function) Functions.<Metadata>constant(null);
+  public static final UnboundMetadata<Metadata> DUMMY =
+      new UnboundMetadata<Metadata>() {
+        public Metadata bind(RelNode rel, RelMetadataQuery mq) {
+          return null;
+        }
+      };
 
   private final LoadingCache<
-      Pair<Class<RelNode>, Class<Metadata>>,
-      Function<RelNode, Metadata>> cache;
+      Pair<Class<RelNode>, Class<Metadata>>, UnboundMetadata<Metadata>> cache;
 
   public MetadataFactoryImpl(RelMetadataProvider provider) {
     this.cache = CacheBuilder.newBuilder().build(loader(provider));
   }
 
   static CacheLoader<Pair<Class<RelNode>, Class<Metadata>>,
-      Function<RelNode, Metadata>> loader(final RelMetadataProvider provider) {
+      UnboundMetadata<Metadata>> loader(final RelMetadataProvider provider) {
     return new CacheLoader<Pair<Class<RelNode>, Class<Metadata>>,
-        Function<RelNode, Metadata>>() {
-      @Override public Function<RelNode, Metadata> load(
+        UnboundMetadata<Metadata>>() {
+      @Override public UnboundMetadata<Metadata> load(
           Pair<Class<RelNode>, Class<Metadata>> key) throws Exception {
-        final Function<RelNode, Metadata> function =
+        final UnboundMetadata<Metadata> function =
             provider.apply(key.left, key.right);
         // Return DUMMY, not null, so the cache knows to not ask again.
         return function != null ? function : DUMMY;
@@ -61,14 +62,14 @@ public class MetadataFactoryImpl implements MetadataFactory {
     };
   }
 
-  public <T extends Metadata> T query(RelNode rel, Class<T> clazz) {
+  public <M extends Metadata> M query(RelNode rel, RelMetadataQuery mq,
+      Class<M> metadataClazz) {
     try {
       //noinspection unchecked
       final Pair<Class<RelNode>, Class<Metadata>> key =
-          (Pair) Pair.of(rel.getClass(), clazz);
-      final Metadata apply = cache.get(key).apply(rel);
-      //noinspection unchecked
-      return (T) apply;
+          (Pair) Pair.of(rel.getClass(), metadataClazz);
+      final Metadata apply = cache.get(key).bind(rel, mq);
+      return metadataClazz.cast(apply);
     } catch (ExecutionException e) {
       if (e.getCause() instanceof RuntimeException) {
         throw (RuntimeException) e.getCause();

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java
index 7db2c6a..beaedfe 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java
@@ -17,23 +17,29 @@
 package org.apache.calcite.rel.metadata;
 
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.runtime.FlatLists;
 import org.apache.calcite.util.BuiltInMethod;
 import org.apache.calcite.util.ImmutableNullableList;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.ReflectiveVisitor;
 import org.apache.calcite.util.Util;
 
-import com.google.common.base.Function;
+import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 
 import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Proxy;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -54,8 +60,8 @@ public class ReflectiveRelMetadataProvider
     implements RelMetadataProvider, ReflectiveVisitor {
 
   //~ Instance fields --------------------------------------------------------
-  private final Map<Class<RelNode>, Function<RelNode, Metadata>> map;
-  private final Class<?> metadataClass0;
+  private final Map<Class<RelNode>, UnboundMetadata> map;
+  private final Class<? extends Metadata> metadataClass0;
 
   //~ Constructors -----------------------------------------------------------
 
@@ -66,8 +72,8 @@ public class ReflectiveRelMetadataProvider
    * @param metadataClass0 Metadata class
    */
   protected ReflectiveRelMetadataProvider(
-      Map<Class<RelNode>, Function<RelNode, Metadata>> map,
-      Class<?> metadataClass0) {
+      Map<Class<RelNode>, UnboundMetadata> map,
+      Class<? extends Metadata> metadataClass0) {
     assert !map.isEmpty() : "are your methods named wrong?";
     this.map = map;
     this.metadataClass0 = metadataClass0;
@@ -77,7 +83,7 @@ public class ReflectiveRelMetadataProvider
    * methods with a preceding argument.
    *
    * <p>For example, {@link BuiltInMetadata.Selectivity} has a method
-   * {@link BuiltInMetadata.Selectivity#getSelectivity(org.apache.calcite.rex.RexNode)}.
+   * {@link BuiltInMetadata.Selectivity#getSelectivity(RexNode)}.
    * A class</p>
    *
    * <blockquote><pre><code>
@@ -106,7 +112,8 @@ public class ReflectiveRelMetadataProvider
       final ImmutableList<Method> methods) {
     assert methods.size() > 0;
     final Method method0 = methods.get(0);
-    final Class<?> metadataClass0 = method0.getDeclaringClass();
+    @SuppressWarnings("unchecked")
+    final Class<Metadata> metadataClass0 = (Class) method0.getDeclaringClass();
     assert Metadata.class.isAssignableFrom(metadataClass0);
     for (Method method : methods) {
       assert method.getDeclaringClass() == metadataClass0;
@@ -128,8 +135,7 @@ public class ReflectiveRelMetadataProvider
       }
     }
 
-    final Map<Class<RelNode>, Function<RelNode, Metadata>> methodsMap =
-        Maps.newHashMap();
+    final Map<Class<RelNode>, UnboundMetadata> methodsMap = new HashMap<>();
     for (Class<RelNode> key : classes) {
       ImmutableNullableList.Builder<Method> builder =
           ImmutableNullableList.builder();
@@ -137,9 +143,10 @@ public class ReflectiveRelMetadataProvider
         builder.add(find(handlerMap, key, method));
       }
       final List<Method> handlerMethods = builder.build();
-      final Function<RelNode, Metadata> function =
-          new Function<RelNode, Metadata>() {
-            public Metadata apply(final RelNode rel) {
+      final UnboundMetadata function =
+          new UnboundMetadata() {
+            public Metadata bind(final RelNode rel,
+                final RelMetadataQuery mq) {
               return (Metadata) Proxy.newProxyInstance(
                   metadataClass0.getClassLoader(),
                   new Class[]{metadataClass0},
@@ -166,20 +173,46 @@ public class ReflectiveRelMetadataProvider
                         throw new AssertionError("not handled: " + method
                             + " for " + rel);
                       }
+                      final Method handlerMethod = handlerMethods.get(i);
+                      if (handlerMethod == null) {
+                        throw new AssertionError("not handled: " + method
+                            + " for " + rel);
+                      }
                       final Object[] args1;
+                      final List key;
                       if (args == null) {
-                        args1 = new Object[]{rel};
+                        args1 = new Object[]{rel, mq};
+                        key = FlatLists.of(rel, method);
                       } else {
-                        args1 = new Object[args.length + 1];
+                        args1 = new Object[args.length + 2];
                         args1[0] = rel;
-                        System.arraycopy(args, 0, args1, 1, args.length);
+                        args1[1] = mq;
+                        System.arraycopy(args, 0, args1, 2, args.length);
+
+                        final Object[] args2 = args1.clone();
+                        args2[1] = method; // replace RelMetadataQuery with method
+                        for (int j = 0; j < args2.length; j++) {
+                          if (args2[j] == null) {
+                            args2[j] = NullSentinel.INSTANCE;
+                          } else if (args2[j] instanceof RexNode) {
+                            // Can't use RexNode.equals - it is not deep
+                            args2[j] = args2[j].toString();
+                          }
+                        }
+                        key = FlatLists.copyOf(args2);
                       }
-                      final Method handlerMethod = handlerMethods.get(i);
-                      if (handlerMethod == null) {
-                        throw new AssertionError("not handled: " + method
-                            + " for " + rel);
+                      if (!mq.set.add(key)) {
+                        throw CyclicMetadataException.INSTANCE;
+                      }
+                      try {
+                        return handlerMethod.invoke(target, args1);
+                      } catch (InvocationTargetException
+                          | UndeclaredThrowableException e) {
+                        Throwables.propagateIfPossible(e.getCause());
+                        throw e;
+                      } finally {
+                        mq.set.remove(key);
                       }
-                      return handlerMethod.invoke(target, args1);
                     }
                   });
             }
@@ -229,19 +262,18 @@ public class ReflectiveRelMetadataProvider
     }
     final Class<?>[] parameterTypes1 = handlerMethod.getParameterTypes();
     final Class<?>[] parameterTypes = method.getParameterTypes();
-    if (parameterTypes1.length != parameterTypes.length + 1
-        || !RelNode.class.isAssignableFrom(parameterTypes1[0])) {
-      return false;
-    }
-    return Util.skip(Arrays.asList(parameterTypes1))
-        .equals(Arrays.asList(parameterTypes));
+    return parameterTypes1.length == parameterTypes.length + 2
+        && RelNode.class.isAssignableFrom(parameterTypes1[0])
+        && RelMetadataQuery.class == parameterTypes1[1]
+        && Arrays.asList(parameterTypes)
+            .equals(Util.skip(Arrays.asList(parameterTypes1), 2));
   }
 
   //~ Methods ----------------------------------------------------------------
 
-  public Function<RelNode, Metadata> apply(
-      Class<? extends RelNode> relClass,
-      Class<? extends Metadata> metadataClass) {
+  public <M extends Metadata> UnboundMetadata<M>
+  apply(Class<? extends RelNode> relClass,
+      Class<? extends M> metadataClass) {
     if (metadataClass == metadataClass0) {
       return apply(relClass);
     } else {
@@ -250,11 +282,11 @@ public class ReflectiveRelMetadataProvider
   }
 
   @SuppressWarnings({ "unchecked", "SuspiciousMethodCalls" })
-  private synchronized Function<RelNode, Metadata> apply(
-      Class<? extends RelNode> relClass) {
-    List<Class<? extends RelNode>> newSources = Lists.newArrayList();
+  public <M extends Metadata> UnboundMetadata<M>
+  apply(Class<? extends RelNode> relClass) {
+    List<Class<? extends RelNode>> newSources = new ArrayList<>();
     for (;;) {
-      final Function<RelNode, Metadata> function = map.get(relClass);
+      UnboundMetadata<M> function = map.get(relClass);
       if (function != null) {
         for (@SuppressWarnings("rawtypes") Class clazz : newSources) {
           map.put(clazz, function);
@@ -265,7 +297,7 @@ public class ReflectiveRelMetadataProvider
       }
       for (Class<?> interfaceClass : relClass.getInterfaces()) {
         if (RelNode.class.isAssignableFrom(interfaceClass)) {
-          final Function<RelNode, Metadata> function2 = map.get(interfaceClass);
+          final UnboundMetadata<M> function2 = map.get(interfaceClass);
           if (function2 != null) {
             for (@SuppressWarnings("rawtypes") Class clazz : newSources) {
               map.put(clazz, function2);

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java
index 37ce6da..92b325d 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java
@@ -47,12 +47,12 @@ import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.LinkedListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Ordering;
-import com.google.common.collect.Sets;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -60,6 +60,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.SortedSet;
+import java.util.TreeSet;
 
 /**
  * RelMdCollation supplies a default implementation of
@@ -77,7 +78,9 @@ public class RelMdCollation {
 
   //~ Methods ----------------------------------------------------------------
 
-  /** Fallback method to deduce collations for any relational expression not
+  /** Catch-all implementation for
+   * {@link BuiltInMetadata.Collation#collations()},
+   * invoked using reflection, for any relational expression not
    * handled by a more specific method.
    *
    * <p>{@link org.apache.calcite.rel.core.Union},
@@ -91,60 +94,72 @@ public class RelMdCollation {
    *
    * @param rel Relational expression
    * @return Relational expression's collations
+   *
+   * @see org.apache.calcite.rel.metadata.RelMetadataQuery#collations(RelNode)
    */
-  public ImmutableList<RelCollation> collations(RelNode rel) {
+  public ImmutableList<RelCollation> collations(RelNode rel,
+      RelMetadataQuery mq) {
     return ImmutableList.of();
   }
 
-  public ImmutableList<RelCollation> collations(Window rel) {
-    return ImmutableList.copyOf(window(rel.getInput(), rel.groups));
+  public ImmutableList<RelCollation> collations(Window rel,
+      RelMetadataQuery mq) {
+    return ImmutableList.copyOf(window(mq, rel.getInput(), rel.groups));
   }
 
-  public ImmutableList<RelCollation> collations(Filter rel) {
-    return RelMetadataQuery.collations(rel.getInput());
+  public ImmutableList<RelCollation> collations(Filter rel,
+      RelMetadataQuery mq) {
+    return mq.collations(rel.getInput());
   }
 
-  public ImmutableList<RelCollation> collations(TableScan scan) {
+  public ImmutableList<RelCollation> collations(TableScan scan,
+      RelMetadataQuery mq) {
     return ImmutableList.copyOf(table(scan.getTable()));
   }
 
-  public ImmutableList<RelCollation> collations(EnumerableMergeJoin join) {
+  public ImmutableList<RelCollation> collations(EnumerableMergeJoin join,
+      RelMetadataQuery mq) {
     // In general a join is not sorted. But a merge join preserves the sort
     // order of the left and right sides.
     return ImmutableList.copyOf(
-        RelMdCollation.mergeJoin(join.getLeft(),
-            join.getRight(),
-            join.getLeftKeys(),
-            join.getRightKeys()));
+        RelMdCollation.mergeJoin(mq, join.getLeft(), join.getRight(),
+            join.getLeftKeys(), join.getRightKeys()));
   }
 
-  public ImmutableList<RelCollation> collations(Sort sort) {
+  public ImmutableList<RelCollation> collations(Sort sort,
+      RelMetadataQuery mq) {
     return ImmutableList.copyOf(
         RelMdCollation.sort(sort.getCollation()));
   }
 
-  public ImmutableList<RelCollation> collations(SortExchange sort) {
+  public ImmutableList<RelCollation> collations(SortExchange sort,
+      RelMetadataQuery mq) {
     return ImmutableList.copyOf(
         RelMdCollation.sort(sort.getCollation()));
   }
 
-  public ImmutableList<RelCollation> collations(Project project) {
+  public ImmutableList<RelCollation> collations(Project project,
+      RelMetadataQuery mq) {
     return ImmutableList.copyOf(
-        project(project.getInput(), project.getProjects()));
+        project(mq, project.getInput(), project.getProjects()));
   }
 
-  public ImmutableList<RelCollation> collations(Values values) {
+  public ImmutableList<RelCollation> collations(Values values,
+      RelMetadataQuery mq) {
     return ImmutableList.copyOf(
-        values(values.getRowType(), values.getTuples()));
+        values(mq, values.getRowType(), values.getTuples()));
   }
 
-  public ImmutableList<RelCollation> collations(HepRelVertex rel) {
-    return RelMetadataQuery.collations(rel.getCurrentRel());
+  public ImmutableList<RelCollation> collations(HepRelVertex rel,
+      RelMetadataQuery mq) {
+    return mq.collations(rel.getCurrentRel());
   }
 
-  public ImmutableList<RelCollation> collations(RelSubset rel) {
+  public ImmutableList<RelCollation> collations(RelSubset rel,
+      RelMetadataQuery mq) {
     return ImmutableList.copyOf(
-        rel.getTraitSet().getTraits(RelCollationTraitDef.INSTANCE));
+        Preconditions.checkNotNull(
+            rel.getTraitSet().getTraits(RelCollationTraitDef.INSTANCE)));
   }
 
   // Helper methods
@@ -163,29 +178,28 @@ public class RelMdCollation {
 
   /** Helper method to determine a
    * {@link org.apache.calcite.rel.core.Filter}'s collation. */
-  public static List<RelCollation> filter(RelNode input) {
-    return RelMetadataQuery.collations(input);
+  public static List<RelCollation> filter(RelMetadataQuery mq, RelNode input) {
+    return mq.collations(input);
   }
 
   /** Helper method to determine a
    * limit's collation. */
-  public static List<RelCollation> limit(RelNode input) {
-    return RelMetadataQuery.collations(input);
+  public static List<RelCollation> limit(RelMetadataQuery mq, RelNode input) {
+    return mq.collations(input);
   }
 
   /** Helper method to determine a
    * {@link org.apache.calcite.rel.core.Calc}'s collation. */
-  public static List<RelCollation> calc(RelNode input,
+  public static List<RelCollation> calc(RelMetadataQuery mq, RelNode input,
       RexProgram program) {
-    return program.getCollations(RelMetadataQuery.collations(input));
+    return program.getCollations(mq.collations(input));
   }
 
   /** Helper method to determine a {@link Project}'s collation. */
-  public static List<RelCollation> project(RelNode input,
-      List<? extends RexNode> projects) {
-    final SortedSet<RelCollation> collations = Sets.newTreeSet();
-    final List<RelCollation> inputCollations =
-        RelMetadataQuery.collations(input);
+  public static List<RelCollation> project(RelMetadataQuery mq,
+      RelNode input, List<? extends RexNode> projects) {
+    final SortedSet<RelCollation> collations = new TreeSet<>();
+    final List<RelCollation> inputCollations = mq.collations(input);
     if (inputCollations == null || inputCollations.isEmpty()) {
       return ImmutableList.of();
     }
@@ -202,7 +216,7 @@ public class RelMdCollation {
         targetsWithMonotonicity.put(project.i, call.getOperator().getMonotonicity(binding));
       }
     }
-    final List<RelFieldCollation> fieldCollations = Lists.newArrayList();
+    final List<RelFieldCollation> fieldCollations = new ArrayList<>();
   loop:
     for (RelCollation ic : inputCollations) {
       if (ic.getFieldCollations().isEmpty()) {
@@ -251,9 +265,9 @@ public class RelMdCollation {
    * from each of its windows. Assuming (quite reasonably) that the
    * implementation does not re-order its input rows, then any collations of its
    * input are preserved. */
-  public static List<RelCollation> window(RelNode input,
+  public static List<RelCollation> window(RelMetadataQuery mq, RelNode input,
       ImmutableList<Window.Group> groups) {
-    return RelMetadataQuery.collations(input);
+    return mq.collations(input);
   }
 
   /** Helper method to determine a
@@ -274,8 +288,9 @@ public class RelMdCollation {
    *
    * <p>So, for an empty Values with 4 columns, we would emit
    * {@code (a, b, c, d), (b, c, d), (c, d), (d)}. */
-  public static List<RelCollation> values(RelDataType rowType,
-      ImmutableList<ImmutableList<RexLiteral>> tuples) {
+  public static List<RelCollation> values(RelMetadataQuery mq,
+      RelDataType rowType, ImmutableList<ImmutableList<RexLiteral>> tuples) {
+    Util.discard(mq); // for future use
     final List<RelCollation> list = Lists.newArrayList();
     final int n = rowType.getFieldCount();
     final List<Pair<RelFieldCollation, Ordering<List<RexLiteral>>>> pairs =
@@ -336,18 +351,17 @@ public class RelMdCollation {
    *
    * <p>If the inputs are sorted on other keys <em>in addition to</em> the join
    * key, the result preserves those collations too. */
-  public static List<RelCollation> mergeJoin(RelNode left, RelNode right,
+  public static List<RelCollation> mergeJoin(RelMetadataQuery mq,
+      RelNode left, RelNode right,
       ImmutableIntList leftKeys, ImmutableIntList rightKeys) {
     final ImmutableList.Builder<RelCollation> builder = ImmutableList.builder();
 
-    final ImmutableList<RelCollation> leftCollations =
-        RelMetadataQuery.collations(left);
+    final ImmutableList<RelCollation> leftCollations = mq.collations(left);
     assert RelCollations.contains(leftCollations, leftKeys)
         : "cannot merge join: left input is not sorted on left keys";
     builder.addAll(leftCollations);
 
-    final ImmutableList<RelCollation> rightCollations =
-        RelMetadataQuery.collations(right);
+    final ImmutableList<RelCollation> rightCollations = mq.collations(right);
     assert RelCollations.contains(rightCollations, rightKeys)
         : "cannot merge join: right input is not sorted on right keys";
     final int leftFieldCount = left.getRowType().getFieldCount();

http://git-wip-us.apache.org/repos/asf/calcite/blob/cabdcf44/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java
index 7f06443..3038260 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java
@@ -53,14 +53,11 @@ public class RelMdColumnOrigins {
 
   //~ Methods ----------------------------------------------------------------
 
-  public Set<RelColumnOrigin> getColumnOrigins(
-      Aggregate rel,
-      int iOutputColumn) {
+  public Set<RelColumnOrigin> getColumnOrigins(Aggregate rel,
+      RelMetadataQuery mq, int iOutputColumn) {
     if (iOutputColumn < rel.getGroupCount()) {
       // Group columns pass through directly.
-      return invokeGetColumnOrigins(
-          rel.getInput(),
-          iOutputColumn);
+      return mq.getColumnOrigins(rel.getInput(), iOutputColumn);
     }
 
     if (rel.indicator) {
@@ -75,11 +72,10 @@ public class RelMdColumnOrigins {
         rel.getAggCallList().get(iOutputColumn
                 - rel.getGroupCount() - rel.getIndicatorCount());
 
-    Set<RelColumnOrigin> set = new HashSet<RelColumnOrigin>();
+    final Set<RelColumnOrigin> set = new HashSet<>();
     for (Integer iInput : call.getArgList()) {
       Set<RelColumnOrigin> inputSet =
-          invokeGetColumnOrigins(
-              rel.getInput(), iInput);
+          mq.getColumnOrigins(rel.getInput(), iInput);
       inputSet = createDerivedColumnOrigins(inputSet);
       if (inputSet != null) {
         set.addAll(inputSet);
@@ -88,25 +84,18 @@ public class RelMdColumnOrigins {
     return set;
   }
 
-  public Set<RelColumnOrigin> getColumnOrigins(
-      Join rel,
+  public Set<RelColumnOrigin> getColumnOrigins(Join rel, RelMetadataQuery mq,
       int iOutputColumn) {
     int nLeftColumns = rel.getLeft().getRowType().getFieldList().size();
     Set<RelColumnOrigin> set;
     boolean derived = false;
     if (iOutputColumn < nLeftColumns) {
-      set =
-          invokeGetColumnOrigins(
-              rel.getLeft(),
-              iOutputColumn);
+      set = mq.getColumnOrigins(rel.getLeft(), iOutputColumn);
       if (rel.getJoinType().generatesNullsOnLeft()) {
         derived = true;
       }
     } else {
-      set =
-          invokeGetColumnOrigins(
-              rel.getRight(),
-              iOutputColumn - nLeftColumns);
+      set = mq.getColumnOrigins(rel.getRight(), iOutputColumn - nLeftColumns);
       if (rel.getJoinType().generatesNullsOnRight()) {
         derived = true;
       }
@@ -119,15 +108,11 @@ public class RelMdColumnOrigins {
     return set;
   }
 
-  public Set<RelColumnOrigin> getColumnOrigins(
-      SetOp rel,
-      int iOutputColumn) {
-    Set<RelColumnOrigin> set = new HashSet<RelColumnOrigin>();
+  public Set<RelColumnOrigin> getColumnOrigins(SetOp rel,
+      RelMetadataQuery mq, int iOutputColumn) {
+    final Set<RelColumnOrigin> set = new HashSet<>();
     for (RelNode input : rel.getInputs()) {
-      Set<RelColumnOrigin> inputSet =
-          invokeGetColumnOrigins(
-              input,
-              iOutputColumn);
+      Set<RelColumnOrigin> inputSet = mq.getColumnOrigins(input, iOutputColumn);
       if (inputSet == null) {
         return null;
       }
@@ -136,30 +121,25 @@ public class RelMdColumnOrigins {
     return set;
   }
 
-  public Set<RelColumnOrigin> getColumnOrigins(
-      Project rel,
-      int iOutputColumn) {
-    final RelNode child = rel.getInput();
+  public Set<RelColumnOrigin> getColumnOrigins(Project rel,
+      final RelMetadataQuery mq, int iOutputColumn) {
+    final RelNode input = rel.getInput();
     RexNode rexNode = rel.getProjects().get(iOutputColumn);
 
     if (rexNode instanceof RexInputRef) {
       // Direct reference:  no derivation added.
       RexInputRef inputRef = (RexInputRef) rexNode;
-      return invokeGetColumnOrigins(
-          child,
-          inputRef.getIndex());
+      return mq.getColumnOrigins(input, inputRef.getIndex());
     }
 
     // Anything else is a derivation, possibly from multiple
     // columns.
-    final Set<RelColumnOrigin> set = new HashSet<RelColumnOrigin>();
+    final Set<RelColumnOrigin> set = new HashSet<>();
     RexVisitor visitor =
         new RexVisitorImpl<Void>(true) {
           public Void visitInputRef(RexInputRef inputRef) {
             Set<RelColumnOrigin> inputSet =
-                invokeGetColumnOrigins(
-                    child,
-                    inputRef.getIndex());
+                mq.getColumnOrigins(input, inputRef.getIndex());
             if (inputSet != null) {
               set.addAll(inputSet);
             }
@@ -171,34 +151,24 @@ public class RelMdColumnOrigins {
     return createDerivedColumnOrigins(set);
   }
 
-  public Set<RelColumnOrigin> getColumnOrigins(
-      Filter rel,
-      int iOutputColumn) {
-    return invokeGetColumnOrigins(
-        rel.getInput(),
-        iOutputColumn);
+  public Set<RelColumnOrigin> getColumnOrigins(Filter rel,
+      RelMetadataQuery mq, int iOutputColumn) {
+    return mq.getColumnOrigins(rel.getInput(), iOutputColumn);
   }
 
-  public Set<RelColumnOrigin> getColumnOrigins(
-      Sort rel,
+  public Set<RelColumnOrigin> getColumnOrigins(Sort rel, RelMetadataQuery mq,
       int iOutputColumn) {
-    return invokeGetColumnOrigins(
-        rel.getInput(),
-        iOutputColumn);
+    return mq.getColumnOrigins(rel.getInput(), iOutputColumn);
   }
 
-  public Set<RelColumnOrigin> getColumnOrigins(
-      Exchange rel,
-      int iOutputColumn) {
-    return invokeGetColumnOrigins(
-        rel.getInput(),
-        iOutputColumn);
+  public Set<RelColumnOrigin> getColumnOrigins(Exchange rel,
+      RelMetadataQuery mq, int iOutputColumn) {
+    return mq.getColumnOrigins(rel.getInput(), iOutputColumn);
   }
 
-  public Set<RelColumnOrigin> getColumnOrigins(
-      TableFunctionScan rel,
-      int iOutputColumn) {
-    Set<RelColumnOrigin> set = new HashSet<RelColumnOrigin>();
+  public Set<RelColumnOrigin> getColumnOrigins(TableFunctionScan rel,
+      RelMetadataQuery mq, int iOutputColumn) {
+    final Set<RelColumnOrigin> set = new HashSet<>();
     Set<RelColumnMapping> mappings = rel.getColumnMappings();
     if (mappings == null) {
       if (rel.getInputs().size() > 0) {
@@ -216,10 +186,9 @@ public class RelMdColumnOrigins {
       if (mapping.iOutputColumn != iOutputColumn) {
         continue;
       }
-      Set<RelColumnOrigin> origins =
-          invokeGetColumnOrigins(
-              rel.getInputs().get(mapping.iInputRel),
-              mapping.iInputColumn);
+      final RelNode input = rel.getInputs().get(mapping.iInputRel);
+      final int column = mapping.iInputColumn;
+      Set<RelColumnOrigin> origins = mq.getColumnOrigins(input, column);
       if (origins == null) {
         return null;
       }
@@ -232,9 +201,8 @@ public class RelMdColumnOrigins {
   }
 
   // Catch-all rule when none of the others apply.
-  public Set<RelColumnOrigin> getColumnOrigins(
-      RelNode rel,
-      int iOutputColumn) {
+  public Set<RelColumnOrigin> getColumnOrigins(RelNode rel,
+      RelMetadataQuery mq, int iOutputColumn) {
     // NOTE jvs 28-Mar-2006: We may get this wrong for a physical table
     // expression which supports projections.  In that case,
     // it's up to the plugin writer to override with the
@@ -245,7 +213,7 @@ public class RelMdColumnOrigins {
       return null;
     }
 
-    Set<RelColumnOrigin> set = new HashSet<RelColumnOrigin>();
+    final Set<RelColumnOrigin> set = new HashSet<>();
 
     RelOptTable table = rel.getTable();
     if (table == null) {
@@ -267,18 +235,12 @@ public class RelMdColumnOrigins {
     return set;
   }
 
-  protected Set<RelColumnOrigin> invokeGetColumnOrigins(
-      RelNode rel,
-      int iOutputColumn) {
-    return RelMetadataQuery.getColumnOrigins(rel, iOutputColumn);
-  }
-
   private Set<RelColumnOrigin> createDerivedColumnOrigins(
       Set<RelColumnOrigin> inputSet) {
     if (inputSet == null) {
       return null;
     }
-    Set<RelColumnOrigin> set = new HashSet<RelColumnOrigin>();
+    final Set<RelColumnOrigin> set = new HashSet<>();
     for (RelColumnOrigin rco : inputSet) {
       RelColumnOrigin derived =
           new RelColumnOrigin(


Mime
View raw message