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/02/24 22:38:33 UTC

[2/3] calcite git commit: [CALCITE-604] Tune metadata by generating a dispatcher at runtime

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/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 ea647a6..c9113a1 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
@@ -44,13 +44,18 @@ import org.apache.calcite.util.Util;
  * RelMdRowCount supplies a default implementation of
  * {@link RelMetadataQuery#getRowCount} for the standard logical algebra.
  */
-public class RelMdRowCount {
+public class RelMdRowCount
+    implements MetadataHandler<BuiltInMetadata.RowCount> {
   public static final RelMetadataProvider SOURCE =
       ReflectiveRelMetadataProvider.reflectiveSource(
           BuiltInMethod.ROW_COUNT.method, new RelMdRowCount());
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.RowCount> getDef() {
+    return BuiltInMetadata.RowCount.DEF;
+  }
+
   /** Catch-all implementation for
    * {@link BuiltInMetadata.RowCount#getRowCount()},
    * invoked using reflection.

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/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 b7ffcdd..5aa55bd 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
@@ -38,7 +38,8 @@ import java.util.List;
  * RelMdSelectivity supplies a default implementation of
  * {@link RelMetadataQuery#getSelectivity} for the standard logical algebra.
  */
-public class RelMdSelectivity {
+public class RelMdSelectivity
+    implements MetadataHandler<BuiltInMetadata.Selectivity> {
   public static final RelMetadataProvider SOURCE =
       ReflectiveRelMetadataProvider.reflectiveSource(
           BuiltInMethod.SELECTIVITY.method, new RelMdSelectivity());
@@ -50,6 +51,10 @@ public class RelMdSelectivity {
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.Selectivity> getDef() {
+    return BuiltInMetadata.Selectivity.DEF;
+  }
+
   public Double getSelectivity(Union rel, RelMetadataQuery mq,
       RexNode predicate) {
     if ((rel.getInputs().size() == 0) || (predicate == null)) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/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 8c2502e..0fd808e 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
@@ -56,7 +56,7 @@ import java.util.List;
  * @see RelMetadataQuery#getAverageColumnSizes
  * @see RelMetadataQuery#getAverageColumnSizesNotNull
  */
-public class RelMdSize {
+public class RelMdSize implements MetadataHandler<BuiltInMetadata.Size> {
   /** Source for
    * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Size}. */
   public static final RelMetadataProvider SOURCE =
@@ -73,6 +73,10 @@ public class RelMdSize {
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.Size> getDef() {
+    return BuiltInMetadata.Size.DEF;
+  }
+
   /** Catch-all implementation for
    * {@link BuiltInMetadata.Size#averageRowSize()},
    * invoked using reflection.

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java
index cb1de64..4f5c4d4 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java
@@ -43,7 +43,8 @@ import java.util.Set;
  * RelMdUniqueKeys supplies a default implementation of
  * {@link RelMetadataQuery#getUniqueKeys} for the standard logical algebra.
  */
-public class RelMdUniqueKeys {
+public class RelMdUniqueKeys
+    implements MetadataHandler<BuiltInMetadata.UniqueKeys> {
   public static final RelMetadataProvider SOURCE =
       ReflectiveRelMetadataProvider.reflectiveSource(
           BuiltInMethod.UNIQUE_KEYS.method, new RelMdUniqueKeys());
@@ -54,6 +55,10 @@ public class RelMdUniqueKeys {
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.UniqueKeys> getDef() {
+    return BuiltInMetadata.UniqueKeys.DEF;
+  }
+
   public Set<ImmutableBitSet> getUniqueKeys(Filter rel, RelMetadataQuery mq,
       boolean ignoreNulls) {
     return mq.getUniqueKeys(rel.getInput(), ignoreNulls);

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java
index a2fd272..c1d5f39 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java
@@ -18,6 +18,9 @@ package org.apache.calcite.rel.metadata;
 
 import org.apache.calcite.rel.RelNode;
 
+import java.lang.reflect.Method;
+import java.util.Map;
+
 /**
  * RelMetadataProvider defines an interface for obtaining metadata about
  * relational expressions. This interface is weakly-typed and is not intended to
@@ -26,6 +29,10 @@ import org.apache.calcite.rel.RelNode;
  *
  * <p>For background and motivation, see <a
  * href="http://wiki.eigenbase.org/RelationalExpressionMetadata">wiki</a>.
+ *
+ * <p>If your provider is not a singleton, we recommend that you implement
+ * {@link Object#equals(Object)} and {@link Object#hashCode()} methods. This
+ * makes the cache of {@link JaninoRelMetadataProvider} more effective.
  */
 public interface RelMetadataProvider {
   //~ Methods ----------------------------------------------------------------
@@ -57,6 +64,9 @@ public interface RelMetadataProvider {
   <M extends Metadata> UnboundMetadata<M>
   apply(Class<? extends RelNode> relClass,
       Class<? extends M> metadataClass);
+
+  <M extends Metadata> Map<Method, MetadataHandler<M>>
+  handlers(MetadataDef<M> def);
 }
 
 // End RelMetadataProvider.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
index b6eed5b..abca58e 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
@@ -26,12 +26,17 @@ import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.sql.SqlExplainLevel;
 import org.apache.calcite.util.ImmutableBitSet;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import java.util.Collections;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -69,9 +74,75 @@ import java.util.Set;
  * providers. Then supply that instance to the planner via the appropriate
  * plugin mechanism.
  */
-public abstract class RelMetadataQuery {
-  /** Set of active metadata queries. */
-  public final Set<List> set = new HashSet<>();
+public class RelMetadataQuery {
+  /** Set of active metadata queries, and cache of previous results. */
+  public final Map<List, Object> map = new HashMap<>();
+
+  public final JaninoRelMetadataProvider metadataProvider;
+
+  private static final RelMetadataQuery EMPTY = new RelMetadataQuery(false);
+
+  private BuiltInMetadata.Collation.Handler collationHandler;
+  private BuiltInMetadata.ColumnOrigin.Handler columnOriginHandler;
+  private BuiltInMetadata.ColumnUniqueness.Handler columnUniquenessHandler;
+  private BuiltInMetadata.CumulativeCost.Handler cumulativeCostHandler;
+  private BuiltInMetadata.DistinctRowCount.Handler distinctRowCountHandler;
+  private BuiltInMetadata.Distribution.Handler distributionHandler;
+  private BuiltInMetadata.ExplainVisibility.Handler explainVisibilityHandler;
+  private BuiltInMetadata.MaxRowCount.Handler maxRowCountHandler;
+  private BuiltInMetadata.Memory.Handler memoryHandler;
+  private BuiltInMetadata.NonCumulativeCost.Handler nonCumulativeCostHandler;
+  private BuiltInMetadata.Parallelism.Handler parallelismHandler;
+  private BuiltInMetadata.PercentageOriginalRows.Handler percentageOriginalRowsHandler;
+  private BuiltInMetadata.PopulationSize.Handler populationSizeHandler;
+  private BuiltInMetadata.Predicates.Handler predicatesHandler;
+  private BuiltInMetadata.RowCount.Handler rowCountHandler;
+  private BuiltInMetadata.Selectivity.Handler selectivityHandler;
+  private BuiltInMetadata.Size.Handler sizeHandler;
+  private BuiltInMetadata.UniqueKeys.Handler uniqueKeysHandler;
+
+  public static final ThreadLocal<JaninoRelMetadataProvider> THREAD_PROVIDERS =
+      new ThreadLocal<JaninoRelMetadataProvider>() {
+        protected JaninoRelMetadataProvider initialValue() {
+          return JaninoRelMetadataProvider.DEFAULT;
+        }
+      };
+
+  private RelMetadataQuery(JaninoRelMetadataProvider metadataProvider,
+      RelMetadataQuery prototype) {
+    this.metadataProvider = Preconditions.checkNotNull(metadataProvider);
+    this.collationHandler = prototype.collationHandler;
+    this.columnOriginHandler = prototype.columnOriginHandler;
+    this.columnUniquenessHandler = prototype.columnUniquenessHandler;
+    this.cumulativeCostHandler = prototype.cumulativeCostHandler;
+    this.distinctRowCountHandler = prototype.distinctRowCountHandler;
+    this.distributionHandler = prototype.distributionHandler;
+    this.explainVisibilityHandler = prototype.explainVisibilityHandler;
+    this.maxRowCountHandler = prototype.maxRowCountHandler;
+    this.memoryHandler = prototype.memoryHandler;
+    this.nonCumulativeCostHandler = prototype.nonCumulativeCostHandler;
+    this.parallelismHandler = prototype.parallelismHandler;
+    this.percentageOriginalRowsHandler = prototype.percentageOriginalRowsHandler;
+    this.populationSizeHandler = prototype.populationSizeHandler;
+    this.predicatesHandler = prototype.predicatesHandler;
+    this.rowCountHandler = prototype.rowCountHandler;
+    this.selectivityHandler = prototype.selectivityHandler;
+    this.sizeHandler = prototype.sizeHandler;
+    this.uniqueKeysHandler = prototype.uniqueKeysHandler;
+  }
+
+  private static <H> H initialHandler(Class<H> handlerClass) {
+    return handlerClass.cast(
+        Proxy.newProxyInstance(RelMetadataQuery.class.getClassLoader(),
+            new Class[] {handlerClass},
+            new InvocationHandler() {
+              public Object invoke(Object proxy, Method method, Object[] args)
+                  throws Throwable {
+                final RelNode r = (RelNode) args[0];
+                throw new JaninoRelMetadataProvider.NoHandler(r.getClass());
+              }
+            }));
+  }
 
   //~ Methods ----------------------------------------------------------------
 
@@ -80,8 +151,32 @@ public abstract class RelMetadataQuery {
    * occur while computing metadata.
    */
   public static RelMetadataQuery instance() {
-    return new RelMetadataQuery() {
-    };
+    return new RelMetadataQuery(THREAD_PROVIDERS.get(), EMPTY);
+  }
+
+  /** Creates and initializes the instance that will serve as a prototype for
+   * all other instances. */
+  private RelMetadataQuery(boolean dummy) {
+    this.metadataProvider = null;
+    this.collationHandler = initialHandler(BuiltInMetadata.Collation.Handler.class);
+    this.columnOriginHandler = initialHandler(BuiltInMetadata.ColumnOrigin.Handler.class);
+    this.columnUniquenessHandler = initialHandler(BuiltInMetadata.ColumnUniqueness.Handler.class);
+    this.cumulativeCostHandler = initialHandler(BuiltInMetadata.CumulativeCost.Handler.class);
+    this.distinctRowCountHandler = initialHandler(BuiltInMetadata.DistinctRowCount.Handler.class);
+    this.distributionHandler = initialHandler(BuiltInMetadata.Distribution.Handler.class);
+    this.explainVisibilityHandler = initialHandler(BuiltInMetadata.ExplainVisibility.Handler.class);
+    this.maxRowCountHandler = initialHandler(BuiltInMetadata.MaxRowCount.Handler.class);
+    this.memoryHandler = initialHandler(BuiltInMetadata.Memory.Handler.class);
+    this.nonCumulativeCostHandler = initialHandler(BuiltInMetadata.NonCumulativeCost.Handler.class);
+    this.parallelismHandler = initialHandler(BuiltInMetadata.Parallelism.Handler.class);
+    this.percentageOriginalRowsHandler =
+        initialHandler(BuiltInMetadata.PercentageOriginalRows.Handler.class);
+    this.populationSizeHandler = initialHandler(BuiltInMetadata.PopulationSize.Handler.class);
+    this.predicatesHandler = initialHandler(BuiltInMetadata.Predicates.Handler.class);
+    this.rowCountHandler = initialHandler(BuiltInMetadata.RowCount.Handler.class);
+    this.selectivityHandler = initialHandler(BuiltInMetadata.Selectivity.Handler.class);
+    this.sizeHandler = initialHandler(BuiltInMetadata.Size.Handler.class);
+    this.uniqueKeysHandler = initialHandler(BuiltInMetadata.UniqueKeys.Handler.class);
   }
 
   /**
@@ -94,10 +189,15 @@ public abstract class RelMetadataQuery {
    * determined
    */
   public Double getRowCount(RelNode rel) {
-    final BuiltInMetadata.RowCount metadata =
-        rel.metadata(BuiltInMetadata.RowCount.class, this);
-    Double result = metadata.getRowCount();
-    return validateResult(result);
+    for (;;) {
+      try {
+        Double result = rowCountHandler.getRowCount(rel, this);
+        return validateResult(result);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        rowCountHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.RowCount.DEF);
+      }
+    }
   }
 
   /**
@@ -109,9 +209,14 @@ public abstract class RelMetadataQuery {
    * @return max row count
    */
   public Double getMaxRowCount(RelNode rel) {
-    final BuiltInMetadata.MaxRowCount metadata =
-        rel.metadata(BuiltInMetadata.MaxRowCount.class, this);
-    return metadata.getMaxRowCount();
+    for (;;) {
+      try {
+        return maxRowCountHandler.getMaxRowCount(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        maxRowCountHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.MaxRowCount.DEF);
+      }
+    }
   }
 
   /**
@@ -123,9 +228,14 @@ public abstract class RelMetadataQuery {
    * @return estimated cost, or null if no reliable estimate can be determined
    */
   public RelOptCost getCumulativeCost(RelNode rel) {
-    final BuiltInMetadata.CumulativeCost metadata =
-        rel.metadata(BuiltInMetadata.CumulativeCost.class, this);
-    return metadata.getCumulativeCost();
+    for (;;) {
+      try {
+        return cumulativeCostHandler.getCumulativeCost(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        cumulativeCostHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.CumulativeCost.DEF);
+      }
+    }
   }
 
   /**
@@ -137,9 +247,14 @@ public abstract class RelMetadataQuery {
    * @return estimated cost, or null if no reliable estimate can be determined
    */
   public RelOptCost getNonCumulativeCost(RelNode rel) {
-    final BuiltInMetadata.NonCumulativeCost metadata =
-        rel.metadata(BuiltInMetadata.NonCumulativeCost.class, this);
-    return metadata.getNonCumulativeCost();
+    for (;;) {
+      try {
+        return nonCumulativeCostHandler.getNonCumulativeCost(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        nonCumulativeCostHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.NonCumulativeCost.DEF);
+      }
+    }
   }
 
   /**
@@ -152,11 +267,16 @@ public abstract class RelMetadataQuery {
    * reliable estimate can be determined
    */
   public Double getPercentageOriginalRows(RelNode rel) {
-    final BuiltInMetadata.PercentageOriginalRows metadata =
-        rel.metadata(BuiltInMetadata.PercentageOriginalRows.class, this);
-    Double result = metadata.getPercentageOriginalRows();
-    assert isPercentage(result, true);
-    return result;
+    for (;;) {
+      try {
+        Double result =
+            percentageOriginalRowsHandler.getPercentageOriginalRows(rel, this);
+        return validatePercentage(result);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        percentageOriginalRowsHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.PercentageOriginalRows.DEF);
+      }
+    }
   }
 
   /**
@@ -171,9 +291,14 @@ public abstract class RelMetadataQuery {
    * all)
    */
   public Set<RelColumnOrigin> getColumnOrigins(RelNode rel, int column) {
-    final BuiltInMetadata.ColumnOrigin metadata =
-        rel.metadata(BuiltInMetadata.ColumnOrigin.class, this);
-    return metadata.getColumnOrigins(column);
+    for (;;) {
+      try {
+        return columnOriginHandler.getColumnOrigins(rel, this, column);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        columnOriginHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.ColumnOrigin.DEF);
+      }
+    }
   }
 
   /**
@@ -229,11 +354,15 @@ public abstract class RelMetadataQuery {
    * reliable estimate can be determined
    */
   public Double getSelectivity(RelNode rel, RexNode predicate) {
-    final BuiltInMetadata.Selectivity metadata =
-        rel.metadata(BuiltInMetadata.Selectivity.class, this);
-    Double result = metadata.getSelectivity(predicate);
-    assert isPercentage(result, true);
-    return result;
+    for (;;) {
+      try {
+        Double result = selectivityHandler.getSelectivity(rel, this, predicate);
+        return validatePercentage(result);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        selectivityHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Selectivity.DEF);
+      }
+    }
   }
 
   /**
@@ -246,9 +375,7 @@ public abstract class RelMetadataQuery {
    * (whereas empty set indicates definitely no keys at all)
    */
   public Set<ImmutableBitSet> getUniqueKeys(RelNode rel) {
-    final BuiltInMetadata.UniqueKeys metadata =
-        rel.metadata(BuiltInMetadata.UniqueKeys.class, this);
-    return metadata.getUniqueKeys(false);
+    return getUniqueKeys(rel, false);
   }
 
   /**
@@ -265,9 +392,14 @@ public abstract class RelMetadataQuery {
    */
   public Set<ImmutableBitSet> getUniqueKeys(RelNode rel,
       boolean ignoreNulls) {
-    final BuiltInMetadata.UniqueKeys metadata =
-        rel.metadata(BuiltInMetadata.UniqueKeys.class, this);
-    return metadata.getUniqueKeys(ignoreNulls);
+    for (;;) {
+      try {
+        return uniqueKeysHandler.getUniqueKeys(rel, this, ignoreNulls);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        uniqueKeysHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.UniqueKeys.DEF);
+      }
+    }
   }
 
   /**
@@ -282,11 +414,9 @@ public abstract class RelMetadataQuery {
    * null if not enough information is available to make that determination
    */
   public Boolean areRowsUnique(RelNode rel) {
-    final BuiltInMetadata.ColumnUniqueness metadata =
-        rel.metadata(BuiltInMetadata.ColumnUniqueness.class, this);
     final ImmutableBitSet columns =
         ImmutableBitSet.range(rel.getRowType().getFieldCount());
-    return metadata.areColumnsUnique(columns, false);
+    return areColumnsUnique(rel, columns, false);
   }
 
   /**
@@ -302,9 +432,7 @@ public abstract class RelMetadataQuery {
    * null if not enough information is available to make that determination
    */
   public Boolean areColumnsUnique(RelNode rel, ImmutableBitSet columns) {
-    final BuiltInMetadata.ColumnUniqueness metadata =
-        rel.metadata(BuiltInMetadata.ColumnUniqueness.class, this);
-    return metadata.areColumnsUnique(columns, false);
+    return areColumnsUnique(rel, columns, false);
   }
 
   /**
@@ -322,9 +450,15 @@ public abstract class RelMetadataQuery {
    */
   public Boolean areColumnsUnique(RelNode rel, ImmutableBitSet columns,
       boolean ignoreNulls) {
-    final BuiltInMetadata.ColumnUniqueness metadata =
-        rel.metadata(BuiltInMetadata.ColumnUniqueness.class, this);
-    return metadata.areColumnsUnique(columns, ignoreNulls);
+    for (;;) {
+      try {
+        return columnUniquenessHandler.areColumnsUnique(rel, this, columns,
+            ignoreNulls);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        columnUniquenessHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.ColumnUniqueness.DEF);
+      }
+    }
   }
 
   /**
@@ -337,9 +471,14 @@ public abstract class RelMetadataQuery {
    * null if not enough information is available to make that determination
    */
   public ImmutableList<RelCollation> collations(RelNode rel) {
-    final BuiltInMetadata.Collation metadata =
-        rel.metadata(BuiltInMetadata.Collation.class, this);
-    return metadata.collations();
+    for (;;) {
+      try {
+        return collationHandler.collations(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        collationHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Collation.DEF);
+      }
+    }
   }
 
   /**
@@ -352,9 +491,14 @@ public abstract class RelMetadataQuery {
    * null if not enough information is available to make that determination
    */
   public RelDistribution distribution(RelNode rel) {
-    final BuiltInMetadata.Distribution metadata =
-        rel.metadata(BuiltInMetadata.Distribution.class, this);
-    return metadata.distribution();
+    for (;;) {
+      try {
+        return distributionHandler.distribution(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        distributionHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Distribution.DEF);
+      }
+    }
   }
 
   /**
@@ -371,10 +515,16 @@ public abstract class RelMetadataQuery {
    */
   public Double getPopulationSize(RelNode rel,
       ImmutableBitSet groupKey) {
-    final BuiltInMetadata.PopulationSize metadata =
-        rel.metadata(BuiltInMetadata.PopulationSize.class, this);
-    Double result = metadata.getPopulationSize(groupKey);
-    return validateResult(result);
+    for (;;) {
+      try {
+        Double result =
+            populationSizeHandler.getPopulationSize(rel, this, groupKey);
+        return validateResult(result);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        populationSizeHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.PopulationSize.DEF);
+      }
+    }
   }
 
   /**
@@ -386,9 +536,14 @@ public abstract class RelMetadataQuery {
    * @return average size of a row, in bytes, or null if not known
      */
   public Double getAverageRowSize(RelNode rel) {
-    final BuiltInMetadata.Size metadata =
-        rel.metadata(BuiltInMetadata.Size.class, this);
-    return metadata.averageRowSize();
+    for (;;) {
+      try {
+        return sizeHandler.averageRowSize(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        sizeHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Size.DEF);
+      }
+    }
   }
 
   /**
@@ -402,17 +557,20 @@ public abstract class RelMetadataQuery {
    * metadata is not available
    */
   public List<Double> getAverageColumnSizes(RelNode rel) {
-    final BuiltInMetadata.Size metadata =
-        rel.metadata(BuiltInMetadata.Size.class, this);
-    return metadata.averageColumnSizes();
+    for (;;) {
+      try {
+        return sizeHandler.averageColumnSizes(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        sizeHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Size.DEF);
+      }
+    }
   }
 
   /** As {@link #getAverageColumnSizes(org.apache.calcite.rel.RelNode)} but
    * never returns a null list, only ever a list of nulls. */
   public List<Double> getAverageColumnSizesNotNull(RelNode rel) {
-    final BuiltInMetadata.Size metadata =
-        rel.metadata(BuiltInMetadata.Size.class, this);
-    final List<Double> averageColumnSizes = metadata.averageColumnSizes();
+    final List<Double> averageColumnSizes = getAverageColumnSizes(rel);
     return averageColumnSizes == null
         ? Collections.<Double>nCopies(rel.getRowType().getFieldCount(), null)
         : averageColumnSizes;
@@ -429,9 +587,14 @@ public abstract class RelMetadataQuery {
    * known
    */
   public Boolean isPhaseTransition(RelNode rel) {
-    final BuiltInMetadata.Parallelism metadata =
-        rel.metadata(BuiltInMetadata.Parallelism.class, this);
-    return metadata.isPhaseTransition();
+    for (;;) {
+      try {
+        return parallelismHandler.isPhaseTransition(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        parallelismHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Parallelism.DEF);
+      }
+    }
   }
 
   /**
@@ -443,9 +606,14 @@ public abstract class RelMetadataQuery {
    * @return the number of distinct splits of the data, or null if not known
    */
   public Integer splitCount(RelNode rel) {
-    final BuiltInMetadata.Parallelism metadata =
-        rel.metadata(BuiltInMetadata.Parallelism.class, this);
-    return metadata.splitCount();
+    for (;;) {
+      try {
+        return parallelismHandler.splitCount(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        parallelismHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Parallelism.DEF);
+      }
+    }
   }
 
   /**
@@ -459,9 +627,14 @@ public abstract class RelMetadataQuery {
    * or null if not known
    */
   public Double memory(RelNode rel) {
-    final BuiltInMetadata.Memory metadata =
-        rel.metadata(BuiltInMetadata.Memory.class, this);
-    return metadata.memory();
+    for (;;) {
+      try {
+        return memoryHandler.memory(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        memoryHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Memory.DEF);
+      }
+    }
   }
 
   /**
@@ -475,9 +648,14 @@ public abstract class RelMetadataQuery {
    * operators within the same phase, across all splits, or null if not known
    */
   public Double cumulativeMemoryWithinPhase(RelNode rel) {
-    final BuiltInMetadata.Memory metadata =
-        rel.metadata(BuiltInMetadata.Memory.class, this);
-    return metadata.cumulativeMemoryWithinPhase();
+    for (;;) {
+      try {
+        return memoryHandler.cumulativeMemoryWithinPhase(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        memoryHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Memory.DEF);
+      }
+    }
   }
 
   /**
@@ -491,9 +669,14 @@ public abstract class RelMetadataQuery {
    * operators within the same phase, within each split, or null if not known
    */
   public Double cumulativeMemoryWithinPhaseSplit(RelNode rel) {
-    final BuiltInMetadata.Memory metadata =
-        rel.metadata(BuiltInMetadata.Memory.class, this);
-    return metadata.cumulativeMemoryWithinPhaseSplit();
+    for (;;) {
+      try {
+        return memoryHandler.cumulativeMemoryWithinPhaseSplit(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        memoryHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Memory.DEF);
+      }
+    }
   }
 
   /**
@@ -511,10 +694,17 @@ public abstract class RelMetadataQuery {
       RelNode rel,
       ImmutableBitSet groupKey,
       RexNode predicate) {
-    final BuiltInMetadata.DistinctRowCount metadata =
-        rel.metadata(BuiltInMetadata.DistinctRowCount.class, this);
-    Double result = metadata.getDistinctRowCount(groupKey, predicate);
-    return validateResult(result);
+    for (;;) {
+      try {
+        Double result =
+            distinctRowCountHandler.getDistinctRowCount(rel, this, groupKey,
+                predicate);
+        return validateResult(result);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        distinctRowCountHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.DistinctRowCount.DEF);
+      }
+    }
   }
 
   /**
@@ -526,9 +716,14 @@ public abstract class RelMetadataQuery {
    * @return Predicates that can be pulled above this RelNode
    */
   public RelOptPredicateList getPulledUpPredicates(RelNode rel) {
-    final BuiltInMetadata.Predicates metadata =
-        rel.metadata(BuiltInMetadata.Predicates.class, this);
-    return metadata.getPredicates();
+    for (;;) {
+      try {
+        return predicatesHandler.getPredicates(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        predicatesHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Predicates.DEF);
+      }
+    }
   }
 
   /**
@@ -543,10 +738,21 @@ public abstract class RelMetadataQuery {
    */
   public boolean isVisibleInExplain(RelNode rel,
       SqlExplainLevel explainLevel) {
-    final BuiltInMetadata.ExplainVisibility metadata =
-        rel.metadata(BuiltInMetadata.ExplainVisibility.class, this);
-    Boolean b = metadata.isVisibleInExplain(explainLevel);
-    return b == null || b;
+    for (;;) {
+      try {
+        Boolean b = explainVisibilityHandler.isVisibleInExplain(rel, this,
+            explainLevel);
+        return b == null || b;
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        explainVisibilityHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.ExplainVisibility.DEF);
+      }
+    }
+  }
+
+  private static Double validatePercentage(Double result) {
+    assert isPercentage(result, true);
+    return result;
   }
 
   /**
@@ -609,6 +815,7 @@ public abstract class RelMetadataQuery {
     }
     return result;
   }
+
 }
 
 // End RelMetadataQuery.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 1fe4177..b271945 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -60,7 +60,9 @@ import org.apache.calcite.rel.logical.LogicalTableModify;
 import org.apache.calcite.rel.logical.LogicalTableScan;
 import org.apache.calcite.rel.logical.LogicalUnion;
 import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.metadata.JaninoRelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelColumnMapping;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.stream.Delta;
 import org.apache.calcite.rel.stream.LogicalDelta;
 import org.apache.calcite.rel.type.RelDataType;
@@ -575,6 +577,8 @@ public class SqlToRelConverter {
       query = validator.validate(query);
     }
 
+    RelMetadataQuery.THREAD_PROVIDERS.set(
+        JaninoRelMetadataProvider.of(cluster.getMetadataProvider()));
     RelNode result = convertQueryRecursive(query, top, null).rel;
     if (top) {
       if (isStream(query)) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/tools/Programs.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/Programs.java b/core/src/main/java/org/apache/calcite/tools/Programs.java
index a52387d..5f2f098 100644
--- a/core/src/main/java/org/apache/calcite/tools/Programs.java
+++ b/core/src/main/java/org/apache/calcite/tools/Programs.java
@@ -102,11 +102,11 @@ public class Programs {
 
   /** Program that converts filters and projects to {@link Calc}s. */
   public static final Program CALC_PROGRAM =
-      calc(new DefaultRelMetadataProvider());
+      calc(DefaultRelMetadataProvider.INSTANCE);
 
   /** Program that expands sub-queries. */
   public static final Program SUB_QUERY_PROGRAM =
-      subquery(new DefaultRelMetadataProvider());
+      subquery(DefaultRelMetadataProvider.INSTANCE);
 
   public static final ImmutableSet<RelOptRule> RULE_SET =
       ImmutableSet.of(
@@ -233,7 +233,7 @@ public class Programs {
               .addRuleInstance(JoinToMultiJoinRule.INSTANCE)
               .build();
           final Program program1 =
-              of(hep, false, new DefaultRelMetadataProvider());
+              of(hep, false, DefaultRelMetadataProvider.INSTANCE);
 
           // Create a program that contains a rule to expand a MultiJoin
           // into heuristically ordered joins.
@@ -279,7 +279,7 @@ public class Programs {
 
   /** Returns the standard program used by Prepare. */
   public static Program standard() {
-    return standard(new DefaultRelMetadataProvider());
+    return standard(DefaultRelMetadataProvider.INSTANCE);
   }
 
   /** Returns the standard program with user metadata provider. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
index 56c458e..ab7e02d 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
@@ -52,6 +52,8 @@ import org.apache.calcite.rel.metadata.CachingRelMetadataProvider;
 import org.apache.calcite.rel.metadata.ChainedRelMetadataProvider;
 import org.apache.calcite.rel.metadata.DefaultRelMetadataProvider;
 import org.apache.calcite.rel.metadata.Metadata;
+import org.apache.calcite.rel.metadata.MetadataDef;
+import org.apache.calcite.rel.metadata.MetadataHandler;
 import org.apache.calcite.rel.metadata.ReflectiveRelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelColumnOrigin;
 import org.apache.calcite.rel.metadata.RelMdCollation;
@@ -137,8 +139,7 @@ public class RelMetadataTest extends SqlToRelTestBase {
 
   private RelNode convertSql(String sql) {
     final RelRoot root = tester.convertSqlToRel(sql);
-    DefaultRelMetadataProvider provider = new DefaultRelMetadataProvider();
-    root.rel.getCluster().setMetadataProvider(provider);
+    root.rel.getCluster().setMetadataProvider(DefaultRelMetadataProvider.INSTANCE);
     return root.rel;
   }
 
@@ -1387,7 +1388,7 @@ public class RelMetadataTest extends SqlToRelTestBase {
 
   /** A provider for {@link org.apache.calcite.test.RelMetadataTest.ColType} via
    * reflection. */
-  public static class ColTypeImpl {
+  public static class ColTypeImpl implements MetadataHandler {
     static final ThreadLocal<List<String>> THREAD_LIST = new ThreadLocal<>();
     static final Method METHOD;
     static {
@@ -1402,6 +1403,10 @@ public class RelMetadataTest extends SqlToRelTestBase {
         ReflectiveRelMetadataProvider.reflectiveSource(
             METHOD, new ColTypeImpl());
 
+    public MetadataDef getDef() {
+      throw new UnsupportedOperationException();
+    }
+
     /** Implementation of {@link ColType#getColType(int)} for
      * {@link org.apache.calcite.rel.logical.LogicalAggregate}, called via
      * reflection. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index bfdd32b..0e0e5a0 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -1630,9 +1630,7 @@ public class RelOptRulesTest extends RelOptTestBase {
     assertTrue(relInitial != null);
 
     List<RelMetadataProvider> list = Lists.newArrayList();
-    DefaultRelMetadataProvider defaultProvider =
-        new DefaultRelMetadataProvider();
-    list.add(defaultProvider);
+    list.add(DefaultRelMetadataProvider.INSTANCE);
     planner.registerMetadataProviders(list);
     RelMetadataProvider plannerChain = ChainedRelMetadataProvider.of(list);
     relInitial.getCluster().setMetadataProvider(

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/test/java/org/apache/calcite/test/RelOptTestBase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptTestBase.java b/core/src/test/java/org/apache/calcite/test/RelOptTestBase.java
index 7f191a3..c12aa30 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptTestBase.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptTestBase.java
@@ -132,9 +132,7 @@ abstract class RelOptTestBase extends SqlToRelTestBase {
     assertTrue(relInitial != null);
 
     List<RelMetadataProvider> list = Lists.newArrayList();
-    DefaultRelMetadataProvider defaultProvider =
-        new DefaultRelMetadataProvider();
-    list.add(defaultProvider);
+    list.add(DefaultRelMetadataProvider.INSTANCE);
     planner.registerMetadataProviders(list);
     RelMetadataProvider plannerChain =
         ChainedRelMetadataProvider.of(list);