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)); }
