This is an automated email from the ASF dual-hosted git repository.

jcamacho pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git

commit fbbdf465df46b4e6f9863d7d1dfdcb19a43f2032
Author: James Starr <[email protected]>
AuthorDate: Fri Mar 19 21:42:37 2021 -0700

    [CALCITE-4546] Change metadata dispatch to avoid registration of all 
RelNode subtypes (James Starr)
    
    Close apache/calcite#2538
---
 .../org/apache/calcite/plan/hep/HepRelVertex.java  |  10 +-
 .../calcite/plan/volcano/VolcanoPlanner.java       |  13 -
 .../rel/metadata/DelegatingMetadataRel.java        |  26 ++
 .../rel/metadata/JaninoRelMetadataProvider.java    | 388 +++++++--------------
 .../rel/metadata/RelMdColumnUniqueness.java        |   7 -
 .../calcite/rel/metadata/RelMdDistribution.java    |   5 -
 .../rel/metadata/RelMdExpressionLineage.java       |   6 -
 .../calcite/rel/metadata/RelMdNodeTypes.java       |   6 -
 .../calcite/rel/metadata/RelMdPredicates.java      |   6 -
 .../calcite/rel/metadata/RelMdTableReferences.java |   5 -
 .../rel/metadata/janino/CodeGeneratorUtil.java     |  46 +++
 .../rel/metadata/janino/DispatchGenerator.java     | 199 +++++++++++
 .../calcite/rel/metadata/janino/package-info.java  |  26 ++
 .../org/apache/calcite/test/RelMetadataTest.java   |   4 +-
 14 files changed, 427 insertions(+), 320 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/plan/hep/HepRelVertex.java 
b/core/src/main/java/org/apache/calcite/plan/hep/HepRelVertex.java
index 68e0282..96d5f68 100644
--- a/core/src/main/java/org/apache/calcite/plan/hep/HepRelVertex.java
+++ b/core/src/main/java/org/apache/calcite/plan/hep/HepRelVertex.java
@@ -22,6 +22,7 @@ import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.AbstractRelNode;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelWriter;
+import org.apache.calcite.rel.metadata.DelegatingMetadataRel;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 
@@ -33,7 +34,7 @@ import java.util.List;
  * HepRelVertex wraps a real {@link RelNode} as a vertex in a DAG representing
  * the entire query expression.
  */
-public class HepRelVertex extends AbstractRelNode {
+public class HepRelVertex extends AbstractRelNode implements 
DelegatingMetadataRel {
   //~ Instance fields --------------------------------------------------------
 
   /**
@@ -93,6 +94,13 @@ public class HepRelVertex extends AbstractRelNode {
     return currentRel;
   }
 
+  /**
+   * Returns {@link RelNode} for metadata.
+   */
+  @Override public RelNode getMetadataDelegateRel() {
+    return currentRel;
+  }
+
   @Override public boolean deepEquals(@Nullable Object obj) {
     return this == obj
         || (obj instanceof HepRelVertex
diff --git 
a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java 
b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
index c09ada9..c40a980 100644
--- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
+++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
@@ -44,7 +44,6 @@ import org.apache.calcite.rel.convert.Converter;
 import org.apache.calcite.rel.convert.ConverterRule;
 import org.apache.calcite.rel.externalize.RelWriterImpl;
 import org.apache.calcite.rel.metadata.CyclicMetadataException;
-import org.apache.calcite.rel.metadata.JaninoRelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelMdUtil;
 import org.apache.calcite.rel.metadata.RelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
@@ -271,11 +270,6 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
   }
 
   @Override public void setRoot(RelNode rel) {
-    // We've registered all the rules, and therefore RelNode classes,
-    // we're interested in, and have not yet started calling metadata 
providers.
-    // So now is a good time to tell the metadata layer what to expect.
-    registerMetadataRels();
-
     this.root = registerImpl(rel, null);
     if (this.originalRoot == null) {
       this.originalRoot = rel;
@@ -554,13 +548,6 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
     }
   }
 
-  /** Informs {@link JaninoRelMetadataProvider} about the different kinds of
-   * {@link RelNode} that we will be dealing with. It will reduce the number
-   * of times that we need to re-generate the provider. */
-  private void registerMetadataRels() {
-    JaninoRelMetadataProvider.DEFAULT.register(classOperands.keySet());
-  }
-
   /** Ensures that the subset that is the root relational expression contains
    * converters to all other subsets in its equivalence set.
    *
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/DelegatingMetadataRel.java 
b/core/src/main/java/org/apache/calcite/rel/metadata/DelegatingMetadataRel.java
new file mode 100644
index 0000000..56ecdc9
--- /dev/null
+++ 
b/core/src/main/java/org/apache/calcite/rel/metadata/DelegatingMetadataRel.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel.metadata;
+
+import org.apache.calcite.rel.RelNode;
+
+/**
+ * Interface for {@link RelNode} where the metadata is derived from another 
node.
+ */
+public interface DelegatingMetadataRel {
+  RelNode getMetadataDelegateRel();
+}
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java
 
b/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java
index 9c939b5..04a7af0 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java
@@ -16,50 +16,20 @@
  */
 package org.apache.calcite.rel.metadata;
 
-import org.apache.calcite.adapter.enumerable.EnumerableAggregate;
-import org.apache.calcite.adapter.enumerable.EnumerableFilter;
-import org.apache.calcite.adapter.enumerable.EnumerableHashJoin;
-import org.apache.calcite.adapter.enumerable.EnumerableProject;
-import org.apache.calcite.adapter.enumerable.EnumerableTableScan;
 import org.apache.calcite.config.CalciteSystemProperty;
 import org.apache.calcite.interpreter.JaninoRexCompiler;
 import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.linq4j.tree.Primitive;
-import org.apache.calcite.plan.hep.HepRelVertex;
-import org.apache.calcite.plan.volcano.AbstractConverter;
-import org.apache.calcite.plan.volcano.RelSubset;
-import org.apache.calcite.rel.AbstractRelNode;
 import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.convert.ConverterImpl;
-import org.apache.calcite.rel.logical.LogicalAggregate;
-import org.apache.calcite.rel.logical.LogicalCalc;
-import org.apache.calcite.rel.logical.LogicalCorrelate;
-import org.apache.calcite.rel.logical.LogicalExchange;
-import org.apache.calcite.rel.logical.LogicalFilter;
-import org.apache.calcite.rel.logical.LogicalIntersect;
-import org.apache.calcite.rel.logical.LogicalJoin;
-import org.apache.calcite.rel.logical.LogicalMinus;
-import org.apache.calcite.rel.logical.LogicalProject;
-import org.apache.calcite.rel.logical.LogicalSort;
-import org.apache.calcite.rel.logical.LogicalTableFunctionScan;
-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.logical.LogicalWindow;
-import org.apache.calcite.rel.stream.LogicalChi;
-import org.apache.calcite.rel.stream.LogicalDelta;
+import org.apache.calcite.rel.metadata.janino.DispatchGenerator;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.util.ControlFlowException;
-import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
 
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 import com.google.common.util.concurrent.UncheckedExecutionException;
 
@@ -74,13 +44,9 @@ import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.concurrent.ExecutionException;
 
 /**
@@ -95,8 +61,6 @@ public class JaninoRelMetadataProvider implements 
RelMetadataProvider {
   public static final JaninoRelMetadataProvider DEFAULT =
       JaninoRelMetadataProvider.of(DefaultRelMetadataProvider.INSTANCE);
 
-  private static final Set<Class<? extends RelNode>> ALL_RELS =
-      new CopyOnWriteArraySet<>();
 
   /** Cache of pre-generated handlers by provider and kind of metadata.
    * For the cache to be effective, providers should implement identity
@@ -107,45 +71,7 @@ public class JaninoRelMetadataProvider implements 
RelMetadataProvider {
           CalciteSystemProperty.METADATA_HANDLER_CACHE_MAXIMUM_SIZE.value())
           .build(
               CacheLoader.from(key ->
-                  load3(key.def, key.provider.handlers(key.def),
-                      key.relClasses)));
-
-  // Pre-register the most common relational operators, to reduce the number of
-  // times we re-generate.
-  static {
-    DEFAULT.register(
-        Arrays.asList(RelNode.class,
-            AbstractRelNode.class,
-            RelSubset.class,
-            HepRelVertex.class,
-            ConverterImpl.class,
-            AbstractConverter.class,
-
-            LogicalAggregate.class,
-            LogicalCalc.class,
-            LogicalCorrelate.class,
-            LogicalExchange.class,
-            LogicalFilter.class,
-            LogicalIntersect.class,
-            LogicalJoin.class,
-            LogicalMinus.class,
-            LogicalProject.class,
-            LogicalSort.class,
-            LogicalTableFunctionScan.class,
-            LogicalTableModify.class,
-            LogicalTableScan.class,
-            LogicalUnion.class,
-            LogicalValues.class,
-            LogicalWindow.class,
-            LogicalChi.class,
-            LogicalDelta.class,
-
-            EnumerableAggregate.class,
-            EnumerableFilter.class,
-            EnumerableProject.class,
-            EnumerableHashJoin.class,
-            EnumerableTableScan.class));
-  }
+                  load3(key.def, key.provider.handlers(key.def))));
 
   /** Private constructor; use {@link #of}. */
   private JaninoRelMetadataProvider(RelMetadataProvider provider) {
@@ -194,41 +120,37 @@ public class JaninoRelMetadataProvider implements 
RelMetadataProvider {
   }
 
   private static <M extends Metadata> MetadataHandler<M> load3(
-      MetadataDef<M> def, Multimap<Method, MetadataHandler<M>> map,
-      ImmutableList<Class<? extends RelNode>> relClasses) {
+      MetadataDef<M> def, Multimap<Method, ? extends MetadataHandler<?>> map) {
     final StringBuilder buff = new StringBuilder();
     final String name =
-        "GeneratedMetadataHandler_" + def.metadataClass.getSimpleName();
-    final Set<MetadataHandler> providerSet = new HashSet<>();
-    final List<Pair<String, MetadataHandler>> providerList = new ArrayList<>();
-    //noinspection unchecked
-    final ReflectiveRelMetadataProvider.Space space =
-        new ReflectiveRelMetadataProvider.Space((Multimap) map);
-    for (MetadataHandler provider : space.providerMap.values()) {
-      if (providerSet.add(provider)) {
-        providerList.add(
-            Pair.of("provider" + (providerSet.size() - 1), provider));
+        "GeneratedMetadata_" + simpleNameForHandler(def.handlerClass);
+
+    final Map<MetadataHandler<?>, String> handlerToName = new 
LinkedHashMap<>();
+    for (MetadataHandler<?> provider : map.values()) {
+      if (!handlerToName.containsKey(provider)) {
+        handlerToName.put(provider, "provider" + handlerToName.size());
       }
     }
 
-    buff.append("  private final java.util.List relClasses;\n");
-    for (Pair<String, MetadataHandler> pair : providerList) {
-      buff.append("  public final ").append(pair.right.getClass().getName())
-          .append(' ').append(pair.left).append(";\n");
+    buff.append("  private final org.apache.calcite.rel.metadata.MetadataDef 
def;\n");
+    for (Map.Entry<MetadataHandler<?>, String> handlerAndName : 
handlerToName.entrySet()) {
+      buff.append("  public final 
").append(handlerAndName.getKey().getClass().getName())
+          .append(' ').append(handlerAndName.getValue()).append(";\n");
     }
-    buff.append("  public ").append(name).append("(java.util.List relClasses");
-    for (Pair<String, MetadataHandler> pair : providerList) {
+    buff.append("  public ").append(name).append("(\n")
+        .append("      org.apache.calcite.rel.metadata.MetadataDef def");
+    for (Map.Entry<MetadataHandler<?>, String> handlerAndName : 
handlerToName.entrySet()) {
       buff.append(",\n")
           .append("      ")
-          .append(pair.right.getClass().getName())
+          .append(handlerAndName.getKey().getClass().getName())
           .append(' ')
-          .append(pair.left);
+          .append(handlerAndName.getValue());
     }
     buff.append(") {\n")
-        .append("    this.relClasses = relClasses;\n");
+        .append("    this.def = def;\n");
 
-    for (Pair<String, MetadataHandler> pair : providerList) {
-      buff.append("    this.").append(pair.left).append(" = 
").append(pair.left)
+    for (String handlerName : handlerToName.values()) {
+      buff.append("    this.").append(handlerName).append(" = 
").append(handlerName)
           .append(";\n");
     }
     buff.append("  }\n")
@@ -239,139 +161,14 @@ public class JaninoRelMetadataProvider implements 
RelMetadataProvider {
         .append(def.metadataClass.getName())
         .append(".DEF;\n")
         .append("  }\n");
-    for (Ord<Method> method : Ord.zip(def.methods)) {
-      buff.append("  public ")
-          .append(method.e.getReturnType().getName())
-          .append(" ")
-          .append(method.e.getName())
-          .append("(\n")
-          .append("      ")
-          .append(RelNode.class.getName())
-          .append(" r,\n")
-          .append("      ")
-          .append(RelMetadataQuery.class.getName())
-          .append(" mq");
-      paramList(buff, method.e)
-          .append(") {\n");
-      buff.append("    final java.util.List key = ")
-          .append(
-              (method.e.getParameterTypes().length < 4
-              ? org.apache.calcite.runtime.FlatLists.class
-              : ImmutableList.class).getName())
-          .append(".of(")
-          .append(def.metadataClass.getName());
-      if (method.i == 0) {
-        buff.append(".DEF");
-      } else {
-        buff.append(".DEF.methods.get(")
-            .append(method.i)
-            .append(")");
-      }
-      safeArgList(buff, method.e)
-          .append(");\n")
-          .append("    final Object v = mq.map.get(r, key);\n")
-          .append("    if (v != null) {\n")
-          .append("      if (v == ")
-          .append(NullSentinel.class.getName())
-          .append(".ACTIVE) {\n")
-          .append("        throw new ")
-          .append(CyclicMetadataException.class.getName())
-          .append("();\n")
-          .append("      }\n")
-          .append("      if (v == ")
-          .append(NullSentinel.class.getName())
-          .append(".INSTANCE) {\n")
-          .append("        return null;\n")
-          .append("      }\n")
-          .append("      return (")
-          .append(method.e.getReturnType().getName())
-          .append(") v;\n")
-          .append("    }\n")
-          .append("    mq.map.put(r, key,")
-          .append(NullSentinel.class.getName())
-          .append(".ACTIVE);\n")
-          .append("    try {\n")
-          .append("      final ")
-          .append(method.e.getReturnType().getName())
-          .append(" x = ")
-          .append(method.e.getName())
-          .append("_(r, mq");
-      argList(buff, method.e)
-          .append(");\n")
-          .append("      mq.map.put(r, key, ")
-          .append(NullSentinel.class.getName())
-          .append(".mask(x));\n")
-          .append("      return x;\n")
-          .append("    } catch (")
-          .append(Exception.class.getName())
-          .append(" e) {\n")
-          .append("      mq.map.row(r).clear();\n")
-          .append("      throw e;\n")
-          .append("    }\n")
-          .append("  }\n")
-          .append("\n")
-          .append("  private ")
-          .append(method.e.getReturnType().getName())
-          .append(" ")
-          .append(method.e.getName())
-          .append("_(\n")
-          .append("      ")
-          .append(RelNode.class.getName())
-          .append(" r,\n")
-          .append("      ")
-          .append(RelMetadataQuery.class.getName())
-          .append(" mq");
-      paramList(buff, method.e)
-          .append(") {\n");
-      buff.append("    switch (relClasses.indexOf(r.getClass())) {\n");
-
-      // Build a list of clauses, grouping clauses that have the same action.
-      final Multimap<String, Integer> clauses = LinkedHashMultimap.create();
-      final StringBuilder buf2 = new StringBuilder();
-      for (Ord<Class<? extends RelNode>> relClass : Ord.zip(relClasses)) {
-        if (relClass.e == HepRelVertex.class) {
-          buf2.append("      return ")
-              .append(method.e.getName())
-              .append("(((")
-              .append(relClass.e.getName())
-              .append(") r).getCurrentRel(), mq");
-          argList(buf2, method.e)
-              .append(");\n");
-        } else {
-          final Method handler = space.find(relClass.e, method.e);
-          final String v = findProvider(providerList, 
handler.getDeclaringClass());
-          buf2.append("      return ")
-              .append(v)
-              .append(".")
-              .append(method.e.getName())
-              .append("((")
-              .append(handler.getParameterTypes()[0].getName())
-              .append(") r, mq");
-          argList(buf2, method.e)
-              .append(");\n");
-        }
-        clauses.put(buf2.toString(), relClass.i);
-        buf2.setLength(0);
-      }
-      buf2.append("      throw new ")
-          .append(NoHandler.class.getName())
-          .append("(r.getClass());\n")
-          .append("    }\n")
-          .append("  }\n");
-      clauses.put(buf2.toString(), -1);
-      for (Map.Entry<String, Collection<Integer>> pair : 
clauses.asMap().entrySet()) {
-        if (pair.getValue().contains(relClasses.indexOf(RelNode.class))) {
-          buff.append("    default:\n");
-        } else {
-          for (Integer integer : pair.getValue()) {
-            buff.append("    case ").append(integer).append(":\n");
-          }
-        }
-        buff.append(pair.getKey());
-      }
+    DispatchGenerator dispatchGenerator = new DispatchGenerator(handlerToName);
+    for (Ord<Method> method : Ord.zip(map.keySet())) {
+      generateCachedMethod(buff, method.e, method.i);
+      dispatchGenerator.dispatchMethod(buff, method.e, map.get(method.e));
     }
-    final List<Object> argList = new ArrayList<>(Pair.right(providerList));
-    argList.add(0, ImmutableList.copyOf(relClasses));
+    final List<Object> argList = new ArrayList<>();
+    argList.add(def);
+    argList.addAll(handlerToName.keySet());
     try {
       return compile(name, buff.toString(), def, argList);
     } catch (CompileException | IOException e) {
@@ -380,15 +177,92 @@ public class JaninoRelMetadataProvider implements 
RelMetadataProvider {
     }
   }
 
-  private static String findProvider(
-      List<Pair<String, MetadataHandler>> providerList,
-      Class<?> declaringClass) {
-    for (Pair<String, MetadataHandler> pair : providerList) {
-      if (declaringClass.isInstance(pair.right)) {
-        return pair.left;
-      }
+  private static void generateCachedMethod(StringBuilder buff, Method method, 
int methodIndex) {
+    String delRelClass = DelegatingMetadataRel.class.getName();
+    buff.append("  public ")
+        .append(method.getReturnType().getName())
+        .append(" ")
+        .append(method.getName())
+        .append("(\n")
+        .append("      ")
+        .append(RelNode.class.getName())
+        .append(" r,\n")
+        .append("      ")
+        .append(RelMetadataQuery.class.getName())
+        .append(" mq");
+    paramList(buff, method)
+        .append(") {\n")
+        .append("    while (r instanceof ").append(delRelClass).append(") {\n")
+        .append("      r = ((").append(delRelClass).append(") 
r).getMetadataDelegateRel();\n")
+        .append("    }\n")
+        .append("    final java.util.List key = ")
+        .append(
+            (method.getParameterTypes().length < 4
+                ? org.apache.calcite.runtime.FlatLists.class
+                : ImmutableList.class).getName())
+        .append(".of(");
+    if (methodIndex == 0) {
+      buff.append("def");
+    } else {
+      buff.append("def.methods.get(")
+          .append(methodIndex)
+          .append(")");
+    }
+    safeArgList(buff, method)
+        .append(");\n")
+        .append("    final Object v = mq.map.get(r, key);\n")
+        .append("    if (v != null) {\n")
+        .append("      if (v == ")
+        .append(NullSentinel.class.getName())
+        .append(".ACTIVE) {\n")
+        .append("        throw new ")
+        .append(CyclicMetadataException.class.getName())
+        .append("();\n")
+        .append("      }\n")
+        .append("      if (v == ")
+        .append(NullSentinel.class.getName())
+        .append(".INSTANCE) {\n")
+        .append("        return null;\n")
+        .append("      }\n")
+        .append("      return (")
+        .append(method.getReturnType().getName())
+        .append(") v;\n")
+        .append("    }\n")
+        .append("    mq.map.put(r, key,")
+        .append(NullSentinel.class.getName())
+        .append(".ACTIVE);\n")
+        .append("    try {\n")
+        .append("      final ")
+        .append(method.getReturnType().getName())
+        .append(" x = ")
+        .append(method.getName())
+        .append("_(r, mq");
+    argList(buff, method)
+        .append(");\n")
+        .append("      mq.map.put(r, key, ")
+        .append(NullSentinel.class.getName())
+        .append(".mask(x));\n")
+        .append("      return x;\n")
+        .append("    } catch (")
+        .append(Exception.class.getName())
+        .append(" e) {\n")
+        .append("      mq.map.row(r).clear();\n")
+        .append("      throw e;\n")
+        .append("    }\n")
+        .append("  }\n")
+        .append("\n");
+  }
+
+  private static String simpleNameForHandler(Class<? extends 
MetadataHandler<?>> clazz) {
+    String simpleName = clazz.getSimpleName();
+    //Previously the pattern was to have a nested in class named Handler
+    //So we need to add the parents class to get a unique name
+    if (simpleName.equals("Handler")) {
+      String[] parts = clazz.getName().split("\\.|\\$");
+      return parts[parts.length - 2] + parts[parts.length - 1];
+    } else {
+      return simpleName;
     }
-    throw new AssertionError("not found: " + declaringClass);
   }
 
   /** Returns e.g. ", ignoreNulls". */
@@ -465,8 +339,7 @@ public class JaninoRelMetadataProvider implements 
RelMetadataProvider {
   synchronized <M extends Metadata, H extends MetadataHandler<M>> H create(
       MetadataDef<M> def) {
     try {
-      final Key key = new Key((MetadataDef) def, provider,
-          ImmutableList.copyOf(ALL_RELS));
+      final Key key = new Key((MetadataDef) def, provider);
       //noinspection unchecked
       return (H) HANDLERS.get(key);
     } catch (UncheckedExecutionException | ExecutionException e) {
@@ -476,9 +349,6 @@ public class JaninoRelMetadataProvider implements 
RelMetadataProvider {
 
   synchronized <M extends Metadata, H extends MetadataHandler<M>> H revise(
       Class<? extends RelNode> rClass, MetadataDef<M> def) {
-    if (ALL_RELS.add(rClass)) {
-      HANDLERS.invalidateAll();
-    }
     //noinspection unchecked
     return (H) create(def);
   }
@@ -486,23 +356,8 @@ public class JaninoRelMetadataProvider implements 
RelMetadataProvider {
   /** Registers some classes. Does not flush the providers, but next time we
    * need to generate a provider, it will handle all of these classes. So,
    * calling this method reduces the number of times we need to re-generate. */
+  @Deprecated
   public void register(Iterable<Class<? extends RelNode>> classes) {
-    // Register the classes and their base classes up to RelNode. Don't bother
-    // to remove duplicates; addAll will do that.
-    final List<Class<? extends RelNode>> list = Lists.newArrayList(classes);
-    for (int i = 0; i < list.size(); i++) {
-      final Class<? extends RelNode> c = list.get(i);
-      final Class s = c.getSuperclass();
-      if (s != null && RelNode.class.isAssignableFrom(s)) {
-        //noinspection unchecked
-        list.add(s);
-      }
-    }
-    synchronized (this) {
-      if (ALL_RELS.addAll(list)) {
-        HANDLERS.invalidateAll();
-      }
-    }
   }
 
   /** Exception that indicates there there should be a handler for
@@ -520,27 +375,22 @@ public class JaninoRelMetadataProvider implements 
RelMetadataProvider {
   private static class Key {
     public final MetadataDef def;
     public final RelMetadataProvider provider;
-    public final ImmutableList<Class<? extends RelNode>> relClasses;
 
-    private Key(MetadataDef def, RelMetadataProvider provider,
-        ImmutableList<Class<? extends RelNode>> relClassList) {
+    private Key(MetadataDef def, RelMetadataProvider provider) {
       this.def = def;
       this.provider = provider;
-      this.relClasses = relClassList;
     }
 
     @Override public int hashCode() {
       return (def.hashCode() * 37
-          + provider.hashCode()) * 37
-          + relClasses.hashCode();
+          + provider.hashCode()) * 37;
     }
 
     @Override public boolean equals(@Nullable Object obj) {
       return this == obj
           || obj instanceof Key
           && ((Key) obj).def.equals(def)
-          && ((Key) obj).provider.equals(provider)
-          && ((Key) obj).relClasses.equals(relClasses);
+          && ((Key) obj).provider.equals(provider);
     }
   }
 }
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java
index 3086f38..5a9d12d 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java
@@ -17,7 +17,6 @@
 package org.apache.calcite.rel.metadata;
 
 import org.apache.calcite.plan.RelOptPredicateList;
-import org.apache.calcite.plan.hep.HepRelVertex;
 import org.apache.calcite.plan.volcano.RelSubset;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.SingleRel;
@@ -387,12 +386,6 @@ public class RelMdColumnUniqueness
     return mq.areColumnsUnique(rel.getInput(), columns, ignoreNulls);
   }
 
-  public @Nullable Boolean areColumnsUnique(HepRelVertex rel, RelMetadataQuery 
mq,
-      ImmutableBitSet columns, boolean ignoreNulls) {
-    columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq);
-    return mq.areColumnsUnique(rel.getCurrentRel(), columns, ignoreNulls);
-  }
-
   public @Nullable Boolean areColumnsUnique(RelSubset rel, RelMetadataQuery mq,
       ImmutableBitSet columns, boolean ignoreNulls) {
     columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq);
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java
index b524efb..e06cab5 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java
@@ -17,7 +17,6 @@
 package org.apache.calcite.rel.metadata;
 
 import org.apache.calcite.plan.RelOptTable;
-import org.apache.calcite.plan.hep.HepRelVertex;
 import org.apache.calcite.rel.BiRel;
 import org.apache.calcite.rel.RelDistribution;
 import org.apache.calcite.rel.RelDistributions;
@@ -108,10 +107,6 @@ public class RelMdDistribution
     return exchange(exchange.distribution);
   }
 
-  public RelDistribution distribution(HepRelVertex rel, RelMetadataQuery mq) {
-    return mq.distribution(rel.getCurrentRel());
-  }
-
   // Helper methods
 
   /** Helper method to determine a
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java
 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java
index 43be0ac..6cc92bf 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java
@@ -17,7 +17,6 @@
 package org.apache.calcite.rel.metadata;
 
 import org.apache.calcite.plan.RelOptUtil;
-import org.apache.calcite.plan.hep.HepRelVertex;
 import org.apache.calcite.plan.volcano.RelSubset;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Aggregate;
@@ -103,11 +102,6 @@ public class RelMdExpressionLineage
     return null;
   }
 
-  public @Nullable Set<RexNode> getExpressionLineage(HepRelVertex rel, 
RelMetadataQuery mq,
-      RexNode outputExpression) {
-    return mq.getExpressionLineage(rel.getCurrentRel(), outputExpression);
-  }
-
   public @Nullable Set<RexNode> getExpressionLineage(RelSubset rel,
       RelMetadataQuery mq, RexNode outputExpression) {
     RelNode bestOrOriginal = Util.first(rel.getBest(), rel.getOriginal());
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdNodeTypes.java 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdNodeTypes.java
index 4c934c9..04a71e0 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdNodeTypes.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdNodeTypes.java
@@ -16,7 +16,6 @@
  */
 package org.apache.calcite.rel.metadata;
 
-import org.apache.calcite.plan.hep.HepRelVertex;
 import org.apache.calcite.plan.volcano.RelSubset;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Aggregate;
@@ -71,11 +70,6 @@ public class RelMdNodeTypes
     return getNodeTypes(rel, RelNode.class, mq);
   }
 
-  public @Nullable Multimap<Class<? extends RelNode>, RelNode> 
getNodeTypes(HepRelVertex rel,
-      RelMetadataQuery mq) {
-    return mq.getNodeTypes(rel.getCurrentRel());
-  }
-
   public @Nullable Multimap<Class<? extends RelNode>, RelNode> 
getNodeTypes(RelSubset rel,
       RelMetadataQuery mq) {
     RelNode bestOrOriginal = Util.first(rel.getBest(), rel.getOriginal());
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
index e12d300..603f7a5 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
@@ -22,7 +22,6 @@ import org.apache.calcite.plan.RelOptPredicateList;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.plan.RexImplicationChecker;
 import org.apache.calcite.plan.Strong;
-import org.apache.calcite.plan.hep.HepRelVertex;
 import org.apache.calcite.plan.volcano.RelSubset;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Aggregate;
@@ -148,11 +147,6 @@ public class RelMdPredicates
     return RelOptPredicateList.EMPTY;
   }
 
-  public RelOptPredicateList getPredicates(HepRelVertex rel,
-      RelMetadataQuery mq) {
-    return mq.getPulledUpPredicates(rel.getCurrentRel());
-  }
-
   /**
    * Infers predicates for a table scan.
    */
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdTableReferences.java 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdTableReferences.java
index 3f5718b..0d09815 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdTableReferences.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdTableReferences.java
@@ -16,7 +16,6 @@
  */
 package org.apache.calcite.rel.metadata;
 
-import org.apache.calcite.plan.hep.HepRelVertex;
 import org.apache.calcite.plan.volcano.RelSubset;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Aggregate;
@@ -82,10 +81,6 @@ public class RelMdTableReferences
     return null;
   }
 
-  public @Nullable Set<RelTableRef> getTableReferences(HepRelVertex rel, 
RelMetadataQuery mq) {
-    return mq.getTableReferences(rel.getCurrentRel());
-  }
-
   public @Nullable Set<RelTableRef> getTableReferences(RelSubset rel, 
RelMetadataQuery mq) {
     RelNode bestOrOriginal = Util.first(rel.getBest(), rel.getOriginal());
     if (bestOrOriginal == null) {
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/janino/CodeGeneratorUtil.java
 
b/core/src/main/java/org/apache/calcite/rel/metadata/janino/CodeGeneratorUtil.java
new file mode 100644
index 0000000..0e439e7
--- /dev/null
+++ 
b/core/src/main/java/org/apache/calcite/rel/metadata/janino/CodeGeneratorUtil.java
@@ -0,0 +1,46 @@
+/*
+ * 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.janino;
+
+import org.apache.calcite.linq4j.Ord;
+
+import java.lang.reflect.Method;
+
+/**
+ * Common functions for code generation.
+ */
+public class CodeGeneratorUtil {
+
+  private CodeGeneratorUtil() {
+  }
+
+  /** Returns e.g. ",\n boolean ignoreNulls". */
+  static StringBuilder paramList(StringBuilder buff, Method method) {
+    for (Ord<Class<?>> t : Ord.zip(method.getParameterTypes())) {
+      buff.append(",\n      ").append(t.e.getName()).append(" a").append(t.i);
+    }
+    return buff;
+  }
+
+  /** Returns e.g. ", ignoreNulls". */
+  static StringBuilder argList(StringBuilder buff, Method method) {
+    for (Ord<Class<?>> t : Ord.zip(method.getParameterTypes())) {
+      buff.append(", a").append(t.i);
+    }
+    return buff;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/janino/DispatchGenerator.java
 
b/core/src/main/java/org/apache/calcite/rel/metadata/janino/DispatchGenerator.java
new file mode 100644
index 0000000..5e11cad
--- /dev/null
+++ 
b/core/src/main/java/org/apache/calcite/rel/metadata/janino/DispatchGenerator.java
@@ -0,0 +1,199 @@
+/*
+ * 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.janino;
+
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.metadata.MetadataHandler;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.lang.reflect.Method;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import static org.apache.calcite.linq4j.Nullness.castNonNull;
+import static org.apache.calcite.rel.metadata.janino.CodeGeneratorUtil.argList;
+import static 
org.apache.calcite.rel.metadata.janino.CodeGeneratorUtil.paramList;
+
+/**
+ * Generates the metadata dispatch to handlers.
+ */
+public class DispatchGenerator {
+  private final Map<MetadataHandler<?>, String> metadataHandlerToName;
+
+  public DispatchGenerator(Map<MetadataHandler<?>, String> 
metadataHandlerToName) {
+    this.metadataHandlerToName = metadataHandlerToName;
+  }
+
+  public void dispatchMethod(StringBuilder buff, Method method,
+      Collection<? extends MetadataHandler<?>> metadataHandlers) {
+    Map<MetadataHandler<?>, Set<Class<? extends RelNode>>> handlersToClasses =
+        metadataHandlers.stream()
+            .distinct()
+            .collect(
+                Collectors.toMap(
+                    Function.identity(),
+                    mh -> methodAndInstanceToImplementingClass(method, mh)));
+
+    Set<Class<? extends RelNode>> delegateClassSet = 
handlersToClasses.values().stream()
+        .flatMap(Set::stream)
+        .collect(Collectors.toSet());
+    List<Class<? extends RelNode>> delegateClassList = 
topologicalSort(delegateClassSet);
+    buff
+        .append("  private ")
+        .append(method.getReturnType().getName())
+        .append(" ")
+        .append(method.getName())
+        .append("_(\n")
+        .append("      ")
+        .append(RelNode.class.getName())
+        .append(" r,\n")
+        .append("      ")
+        .append(RelMetadataQuery.class.getName())
+        .append(" mq");
+    paramList(buff, method)
+        .append(") {\n");
+    if (delegateClassList.isEmpty()) {
+      throwUnknown(buff.append("    "), method)
+          .append("  }\n");
+    } else {
+      buff
+          .append(
+              delegateClassList.stream()
+                  .map(clazz ->
+                      ifInstanceThenDispatch(method,
+                          metadataHandlers, handlersToClasses, clazz))
+                  .collect(
+                      Collectors.joining("    } else if ",
+                          "    if ", "    } else {\n")));
+      throwUnknown(buff.append("      "), method)
+          .append("    }\n")
+          .append("  }\n");
+    }
+  }
+
+  private StringBuilder ifInstanceThenDispatch(Method method,
+      Collection<? extends MetadataHandler<?>> metadataHandlers,
+      Map<MetadataHandler<?>, Set<Class<? extends RelNode>>> handlersToClasses,
+      Class<? extends RelNode> clazz) {
+    String handlerName = findProvider(metadataHandlers, handlersToClasses, 
clazz);
+    StringBuilder buff = new StringBuilder()
+        .append("(r instanceof ").append(clazz.getName()).append(") {\n")
+        .append("      return ");
+    dispatchedCall(buff, handlerName, method, clazz);
+
+    return buff;
+  }
+
+  private String findProvider(Collection<? extends MetadataHandler<?>> 
metadataHandlers,
+      Map<MetadataHandler<?>, Set<Class<? extends RelNode>>> handlerToClasses,
+      Class<? extends RelNode> clazz) {
+    for (MetadataHandler<?> mh : metadataHandlers) {
+      if (handlerToClasses.getOrDefault(mh, 
ImmutableSet.of()).contains(clazz)) {
+        return castNonNull(this.metadataHandlerToName.get(mh));
+      }
+    }
+    throw new RuntimeException();
+  }
+
+  private static StringBuilder throwUnknown(StringBuilder buff, Method method) 
{
+    return buff
+        .append("      throw new ")
+        .append(IllegalArgumentException.class.getName())
+        .append("(\"No handler for method [").append(method)
+        .append("] applied to argument of type [\" + r.getClass() + ")
+        .append("\"]; we recommend you create a catch-all (RelNode) handler\"")
+        .append(");\n");
+  }
+
+  private static void dispatchedCall(StringBuilder buff, String handlerName, 
Method method,
+      Class<? extends RelNode> clazz) {
+    buff.append(handlerName).append(".").append(method.getName())
+        .append("((").append(clazz.getName()).append(") r, mq");
+    argList(buff, method);
+    buff.append(");\n");
+  }
+
+  private static Set<Class<? extends RelNode>> 
methodAndInstanceToImplementingClass(
+      Method method, MetadataHandler<?> handler) {
+    Set<Class<? extends RelNode>> set = new HashSet<>();
+    for (Method m : handler.getClass().getMethods()) {
+      Class<? extends RelNode> aClass = toRelClass(method, m);
+      if (aClass != null) {
+        set.add(aClass);
+      }
+    }
+    return set;
+  }
+
+  private static @Nullable Class<? extends RelNode> toRelClass(Method 
superMethod,
+      Method candidate) {
+    if (!superMethod.getName().equals(candidate.getName())) {
+      return null;
+    } else if (superMethod.getParameterCount() + 2 != 
candidate.getParameterCount()) {
+      return null;
+    } else {
+      Class<?>[] cpt = candidate.getParameterTypes();
+      Class<?>[] smpt = superMethod.getParameterTypes();
+      if (!RelNode.class.isAssignableFrom(cpt[0])) {
+        return null;
+      } else if (!RelMetadataQuery.class.equals(cpt[1])) {
+        return null;
+      }
+      for (int i = 0; i < smpt.length; i++) {
+        if (cpt[i + 2] != smpt[i]) {
+          return null;
+        }
+      }
+      return (Class<? extends RelNode>) cpt[0];
+    }
+  }
+
+  private static List<Class<? extends RelNode>> topologicalSort(
+      Collection<Class<? extends RelNode>> list) {
+    List<Class<? extends RelNode>> l = new ArrayList<>();
+    ArrayDeque<Class<? extends RelNode>> s = new ArrayDeque<>(list);
+
+    while (!s.isEmpty()) {
+      Class<? extends RelNode> n = s.remove();
+
+      boolean found = false;
+      for (Class<? extends RelNode> other : s) {
+        if (n.isAssignableFrom(other)) {
+          found = true;
+          break;
+        }
+      }
+      if (found) {
+        s.add(n);
+      } else {
+        l.add(n);
+      }
+    }
+    return l;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/janino/package-info.java 
b/core/src/main/java/org/apache/calcite/rel/metadata/janino/package-info.java
new file mode 100644
index 0000000..2d52741
--- /dev/null
+++ 
b/core/src/main/java/org/apache/calcite/rel/metadata/janino/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/**
+ * Defines metadata interfaces and utilities for relational
+ * expressions.
+ */
+
+/**
+ * Code for generating metadata handlers.
+ */
+package org.apache.calcite.rel.metadata.janino;
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 a497a34..709b49b 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
@@ -1548,7 +1548,7 @@ public class RelMetadataTest extends SqlToRelTestBase {
     } catch (IllegalArgumentException e) {
       final String value = "No handler for method [public abstract 
java.lang.String "
           + "org.apache.calcite.test.RelMetadataTest$ColType.getColType(int)] "
-          + "applied to argument of type [interface 
org.apache.calcite.rel.RelNode]; "
+          + "applied to argument of type [class 
org.apache.calcite.rel.logical.LogicalFilter]; "
           + "we recommend you create a catch-all (RelNode) handler";
       assertThat(e.getMessage(), is(value));
     }
@@ -1582,7 +1582,7 @@ public class RelMetadataTest extends SqlToRelTestBase {
     } catch (IllegalArgumentException e) {
       final String value = "No handler for method [public abstract 
java.lang.String "
           + "org.apache.calcite.test.RelMetadataTest$ColType.getColType(int)] "
-          + "applied to argument of type [interface 
org.apache.calcite.rel.RelNode]; "
+          + "applied to argument of type [class 
org.apache.calcite.rel.logical.LogicalFilter]; "
           + "we recommend you create a catch-all (RelNode) handler";
       assertThat(e.getMessage(), is(value));
     }

Reply via email to