[CALCITE-604] Tune metadata by generating a dispatcher at runtime Currently we dispatch to metadata providers using reflection. With this change, we generate a dispatcher class and compile using Janino.
Cache results within a metadata call. This should yield a performance improvement when, say, a Join requires many kinds of metadata (rowCount, averageRowSize, selectivity) and they all rely on the rowCount of the input, and that rowCount is expensive to compute. Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/d14040c5 Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/d14040c5 Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/d14040c5 Branch: refs/heads/master Commit: d14040c52f8b0bc351443cff1584e8b587378698 Parents: 8cba7fc Author: Julian Hyde <[email protected]> Authored: Mon Jan 18 16:22:06 2016 -0800 Committer: Julian Hyde <[email protected]> Committed: Wed Feb 24 13:37:49 2016 -0800 ---------------------------------------------------------------------- .../org/apache/calcite/plan/RelOptCluster.java | 2 +- .../calcite/plan/RelOptMaterialization.java | 4 +- .../plan/hep/HepRelMetadataProvider.java | 20 + .../calcite/plan/volcano/VolcanoPlanner.java | 13 + .../volcano/VolcanoRelMetadataProvider.java | 20 + .../calcite/rel/metadata/BuiltInMetadata.java | 163 ++++++ .../metadata/CachingRelMetadataProvider.java | 5 + .../metadata/ChainedRelMetadataProvider.java | 23 + .../metadata/DefaultRelMetadataProvider.java | 8 +- .../rel/metadata/JaninoRelMetadataProvider.java | 522 +++++++++++++++++++ .../calcite/rel/metadata/MetadataDef.java | 67 +++ .../calcite/rel/metadata/MetadataHandler.java | 28 + .../calcite/rel/metadata/NullSentinel.java | 14 +- .../metadata/ReflectiveRelMetadataProvider.java | 185 ++++--- .../calcite/rel/metadata/RelMdCollation.java | 7 +- .../rel/metadata/RelMdColumnOrigins.java | 7 +- .../rel/metadata/RelMdColumnUniqueness.java | 7 +- .../rel/metadata/RelMdDistinctRowCount.java | 7 +- .../calcite/rel/metadata/RelMdDistribution.java | 7 +- .../rel/metadata/RelMdExplainVisibility.java | 7 +- .../calcite/rel/metadata/RelMdMaxRowCount.java | 7 +- .../calcite/rel/metadata/RelMdMemory.java | 6 +- .../calcite/rel/metadata/RelMdParallelism.java | 7 +- .../metadata/RelMdPercentageOriginalRows.java | 7 +- .../rel/metadata/RelMdPopulationSize.java | 7 +- .../calcite/rel/metadata/RelMdPredicates.java | 7 +- .../calcite/rel/metadata/RelMdRowCount.java | 7 +- .../calcite/rel/metadata/RelMdSelectivity.java | 7 +- .../apache/calcite/rel/metadata/RelMdSize.java | 6 +- .../calcite/rel/metadata/RelMdUniqueKeys.java | 7 +- .../rel/metadata/RelMetadataProvider.java | 10 + .../calcite/rel/metadata/RelMetadataQuery.java | 391 ++++++++++---- .../calcite/sql2rel/SqlToRelConverter.java | 4 + .../java/org/apache/calcite/tools/Programs.java | 8 +- .../apache/calcite/test/RelMetadataTest.java | 11 +- .../apache/calcite/test/RelOptRulesTest.java | 4 +- .../org/apache/calcite/test/RelOptTestBase.java | 4 +- 37 files changed, 1418 insertions(+), 198 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java b/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java index 241f4e0..f88e232 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java @@ -81,7 +81,7 @@ public class RelOptCluster { // set up a default rel metadata provider, // giving the planner first crack at everything - setMetadataProvider(new DefaultRelMetadataProvider()); + setMetadataProvider(DefaultRelMetadataProvider.INSTANCE); this.emptyTraitSet = planner.emptyTraitSet(); assert emptyTraitSet.size() == planner.getRelTraitDefs().size(); } http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java index 4108cff..1da831d 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java @@ -204,7 +204,7 @@ public class RelOptMaterialization { AggregateProjectMergeRule.INSTANCE, AggregateFilterTransposeRule.INSTANCE), false, - new DefaultRelMetadataProvider()); + DefaultRelMetadataProvider.INSTANCE); return program.run(null, rel2, null); } @@ -273,7 +273,7 @@ public class RelOptMaterialization { FilterJoinRule.FilterIntoJoinRule.FILTER_ON_JOIN, ProjectMergeRule.INSTANCE), false, - new DefaultRelMetadataProvider()); + DefaultRelMetadataProvider.INSTANCE); if (CalcitePrepareImpl.DEBUG) { System.out.println( RelOptUtil.dumpPlan( http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/plan/hep/HepRelMetadataProvider.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/plan/hep/HepRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/plan/hep/HepRelMetadataProvider.java index 3f1170a..317fe94 100644 --- a/core/src/main/java/org/apache/calcite/plan/hep/HepRelMetadataProvider.java +++ b/core/src/main/java/org/apache/calcite/plan/hep/HepRelMetadataProvider.java @@ -18,10 +18,17 @@ package org.apache.calcite.plan.hep; import org.apache.calcite.rel.RelNode; 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.RelMetadataProvider; import org.apache.calcite.rel.metadata.RelMetadataQuery; import org.apache.calcite.rel.metadata.UnboundMetadata; +import com.google.common.collect.ImmutableMap; + +import java.lang.reflect.Method; +import java.util.Map; + /** * HepRelMetadataProvider implements the {@link RelMetadataProvider} interface * by combining metadata from the rels inside of a {@link HepRelVertex}. @@ -29,6 +36,14 @@ import org.apache.calcite.rel.metadata.UnboundMetadata; class HepRelMetadataProvider implements RelMetadataProvider { //~ Methods ---------------------------------------------------------------- + @Override public boolean equals(Object obj) { + return obj instanceof HepRelMetadataProvider; + } + + @Override public int hashCode() { + return 107; + } + public <M extends Metadata> UnboundMetadata<M> apply(Class<? extends RelNode> relClass, final Class<? extends M> metadataClass) { @@ -46,6 +61,11 @@ class HepRelMetadataProvider implements RelMetadataProvider { } }; } + + public <M extends Metadata> Map<Method, MetadataHandler<M>> + handlers(MetadataDef<M> def) { + return ImmutableMap.of(); + } } // End HepRelMetadataProvider.java http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java ---------------------------------------------------------------------- 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 2565ca7..8cb91b0 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 @@ -48,6 +48,7 @@ import org.apache.calcite.rel.RelVisitor; import org.apache.calcite.rel.convert.Converter; import org.apache.calcite.rel.convert.ConverterRule; import org.apache.calcite.rel.core.TableScan; +import org.apache.calcite.rel.metadata.JaninoRelMetadataProvider; import org.apache.calcite.rel.metadata.RelMetadataProvider; import org.apache.calcite.rel.metadata.RelMetadataQuery; import org.apache.calcite.rel.rules.AggregateJoinTransposeRule; @@ -322,6 +323,11 @@ public class VolcanoPlanner extends AbstractRelOptPlanner { } public void setRoot(RelNode rel) { + // We're 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; @@ -842,6 +848,13 @@ public class VolcanoPlanner extends AbstractRelOptPlanner { return cheapest; } + /** 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. * http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRelMetadataProvider.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRelMetadataProvider.java index 201670f..c32d7d3 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRelMetadataProvider.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRelMetadataProvider.java @@ -18,10 +18,17 @@ package org.apache.calcite.plan.volcano; import org.apache.calcite.rel.RelNode; 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.RelMetadataProvider; import org.apache.calcite.rel.metadata.RelMetadataQuery; import org.apache.calcite.rel.metadata.UnboundMetadata; +import com.google.common.collect.ImmutableMap; + +import java.lang.reflect.Method; +import java.util.Map; + /** * VolcanoRelMetadataProvider implements the {@link RelMetadataProvider} * interface by combining metadata from the rels making up an equivalence class. @@ -29,6 +36,14 @@ import org.apache.calcite.rel.metadata.UnboundMetadata; public class VolcanoRelMetadataProvider implements RelMetadataProvider { //~ Methods ---------------------------------------------------------------- + @Override public boolean equals(Object obj) { + return obj instanceof VolcanoRelMetadataProvider; + } + + @Override public int hashCode() { + return 103; + } + public <M extends Metadata> UnboundMetadata<M> apply(Class<? extends RelNode> relClass, final Class<? extends M> metadataClass) { @@ -99,6 +114,11 @@ public class VolcanoRelMetadataProvider implements RelMetadataProvider { } }; } + + public <M extends Metadata> Map<Method, MetadataHandler<M>> + handlers(MetadataDef<M> def) { + return ImmutableMap.of(); + } } // End VolcanoRelMetadataProvider.java http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java b/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java index 87fbbf4..0ff0515 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java @@ -23,6 +23,7 @@ import org.apache.calcite.rel.RelDistribution; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rex.RexNode; import org.apache.calcite.sql.SqlExplainLevel; +import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.ImmutableBitSet; import com.google.common.collect.ImmutableList; @@ -37,6 +38,9 @@ public abstract class BuiltInMetadata { /** Metadata about the selectivity of a predicate. */ public interface Selectivity extends Metadata { + MetadataDef<Selectivity> DEF = MetadataDef.of(Selectivity.class, + Selectivity.Handler.class, BuiltInMethod.SELECTIVITY.method); + /** * Estimates the percentage of an expression's output rows which satisfy a * given predicate. Returns null to indicate that no reliable estimate can @@ -48,10 +52,18 @@ public abstract class BuiltInMetadata { * reliable estimate can be determined */ Double getSelectivity(RexNode predicate); + + /** Handler API. */ + interface Handler extends MetadataHandler<Selectivity> { + Double getSelectivity(RelNode r, RelMetadataQuery mq, RexNode predicate); + } } /** Metadata about which combinations of columns are unique identifiers. */ public interface UniqueKeys extends Metadata { + MetadataDef<UniqueKeys> DEF = MetadataDef.of(UniqueKeys.class, + UniqueKeys.Handler.class, BuiltInMethod.UNIQUE_KEYS.method); + /** * Determines the set of unique minimal keys for this expression. A key is * represented as an {@link org.apache.calcite.util.ImmutableBitSet}, where @@ -66,10 +78,19 @@ public abstract class BuiltInMetadata { * (whereas empty set indicates definitely no keys at all) */ Set<ImmutableBitSet> getUniqueKeys(boolean ignoreNulls); + + /** Handler API. */ + interface Handler extends MetadataHandler<UniqueKeys> { + Set<ImmutableBitSet> getUniqueKeys(RelNode r, RelMetadataQuery mq, + boolean ignoreNulls); + } } /** Metadata about whether a set of columns uniquely identifies a row. */ public interface ColumnUniqueness extends Metadata { + MetadataDef<ColumnUniqueness> DEF = MetadataDef.of(ColumnUniqueness.class, + ColumnUniqueness.Handler.class, BuiltInMethod.COLUMN_UNIQUENESS.method); + /** * Determines whether a specified set of columns from a specified relational * expression are unique. @@ -93,12 +114,26 @@ public abstract class BuiltInMetadata { * null if not enough information is available to make that determination */ Boolean areColumnsUnique(ImmutableBitSet columns, boolean ignoreNulls); + + /** Handler API. */ + interface Handler extends MetadataHandler<ColumnUniqueness> { + Boolean areColumnsUnique(RelNode r, RelMetadataQuery mq, + ImmutableBitSet columns, boolean ignoreNulls); + } } /** Metadata about which columns are sorted. */ public interface Collation extends Metadata { + MetadataDef<Collation> DEF = MetadataDef.of(Collation.class, + Collation.Handler.class, BuiltInMethod.COLLATIONS.method); + /** Determines which columns are sorted. */ ImmutableList<RelCollation> collations(); + + /** Handler API. */ + interface Handler extends MetadataHandler<Collation> { + ImmutableList<RelCollation> collations(RelNode r, RelMetadataQuery mq); + } } /** Metadata about how a relational expression is distributed. @@ -113,12 +148,23 @@ public abstract class BuiltInMetadata { * among nodes, but it may be partitioned among threads running on the same * node. */ public interface Distribution extends Metadata { + MetadataDef<Distribution> DEF = MetadataDef.of(Distribution.class, + Distribution.Handler.class, BuiltInMethod.DISTRIBUTION.method); + /** Determines how the rows are distributed. */ RelDistribution distribution(); + + /** Handler API. */ + interface Handler extends MetadataHandler<Distribution> { + RelDistribution distribution(RelNode r, RelMetadataQuery mq); + } } /** Metadata about the number of rows returned by a relational expression. */ public interface RowCount extends Metadata { + MetadataDef<RowCount> DEF = MetadataDef.of(RowCount.class, + RowCount.Handler.class, BuiltInMethod.ROW_COUNT.method); + /** * Estimates the number of rows which will be returned by a relational * expression. The default implementation for this query asks the rel itself @@ -129,11 +175,19 @@ public abstract class BuiltInMetadata { * determined */ Double getRowCount(); + + /** Handler API. */ + interface Handler extends MetadataHandler<RowCount> { + Double getRowCount(RelNode r, RelMetadataQuery mq); + } } /** Metadata about the maximum number of rows returned by a relational * expression. */ public interface MaxRowCount extends Metadata { + MetadataDef<MaxRowCount> DEF = MetadataDef.of(MaxRowCount.class, + MaxRowCount.Handler.class, BuiltInMethod.MAX_ROW_COUNT.method); + /** * Estimates the max number of rows which will be returned by a relational * expression. @@ -145,11 +199,19 @@ public abstract class BuiltInMetadata { * @return upper bound on the number of rows returned */ Double getMaxRowCount(); + + /** Handler API. */ + interface Handler extends MetadataHandler<MaxRowCount> { + Double getMaxRowCount(RelNode r, RelMetadataQuery mq); + } } /** Metadata about the number of distinct rows returned by a set of columns * in a relational expression. */ public interface DistinctRowCount extends Metadata { + MetadataDef<DistinctRowCount> DEF = MetadataDef.of(DistinctRowCount.class, + DistinctRowCount.Handler.class, BuiltInMethod.DISTINCT_ROW_COUNT.method); + /** * Estimates the number of rows which would be produced by a GROUP BY on the * set of columns indicated by groupKey, where the input to the GROUP BY has @@ -163,11 +225,22 @@ public abstract class BuiltInMetadata { * if no reliable estimate can be determined */ Double getDistinctRowCount(ImmutableBitSet groupKey, RexNode predicate); + + /** Handler API. */ + interface Handler extends MetadataHandler<DistinctRowCount> { + Double getDistinctRowCount(RelNode r, RelMetadataQuery mq, + ImmutableBitSet groupKey, RexNode predicate); + } } /** Metadata about the proportion of original rows that remain in a relational * expression. */ public interface PercentageOriginalRows extends Metadata { + MetadataDef<PercentageOriginalRows> DEF = + MetadataDef.of(PercentageOriginalRows.class, + PercentageOriginalRows.Handler.class, + BuiltInMethod.PERCENTAGE_ORIGINAL_ROWS.method); + /** * Estimates the percentage of the number of rows actually produced by a * relational expression out of the number of rows it would produce if all @@ -177,11 +250,19 @@ public abstract class BuiltInMetadata { * reliable estimate can be determined */ Double getPercentageOriginalRows(); + + /** Handler API. */ + interface Handler extends MetadataHandler<PercentageOriginalRows> { + Double getPercentageOriginalRows(RelNode r, RelMetadataQuery mq); + } } /** Metadata about the number of distinct values in the original source of a * column or set of columns. */ public interface PopulationSize extends Metadata { + MetadataDef<PopulationSize> DEF = MetadataDef.of(PopulationSize.class, + PopulationSize.Handler.class, BuiltInMethod.POPULATION_SIZE.method); + /** * Estimates the distinct row count in the original source for the given * {@code groupKey}, ignoring any filtering being applied by the expression. @@ -194,10 +275,20 @@ public abstract class BuiltInMetadata { * estimate can be determined */ Double getPopulationSize(ImmutableBitSet groupKey); + + /** Handler API. */ + interface Handler extends MetadataHandler<PopulationSize> { + Double getPopulationSize(RelNode r, RelMetadataQuery mq, + ImmutableBitSet groupKey); + } } /** Metadata about the size of rows and columns. */ public interface Size extends Metadata { + MetadataDef<Size> DEF = MetadataDef.of(Size.class, Size.Handler.class, + BuiltInMethod.AVERAGE_ROW_SIZE.method, + BuiltInMethod.AVERAGE_COLUMN_SIZES.method); + /** * Determines the average size (in bytes) of a row from this relational * expression. @@ -222,10 +313,19 @@ public abstract class BuiltInMetadata { * the metadata is not available */ List<Double> averageColumnSizes(); + + /** Handler API. */ + interface Handler extends MetadataHandler<Size> { + Double averageRowSize(RelNode r, RelMetadataQuery mq); + List<Double> averageColumnSizes(RelNode r, RelMetadataQuery mq); + } } /** Metadata about the origins of columns. */ public interface ColumnOrigin extends Metadata { + MetadataDef<ColumnOrigin> DEF = MetadataDef.of(ColumnOrigin.class, + ColumnOrigin.Handler.class, BuiltInMethod.COLUMN_ORIGIN.method); + /** * For a given output column of an expression, determines all columns of * underlying tables which contribute to result values. An output column may @@ -239,11 +339,20 @@ public abstract class BuiltInMetadata { * all) */ Set<RelColumnOrigin> getColumnOrigins(int outputColumn); + + /** Handler API. */ + interface Handler extends MetadataHandler<ColumnOrigin> { + Set<RelColumnOrigin> getColumnOrigins(RelNode r, RelMetadataQuery mq, + int outputColumn); + } } /** Metadata about the cost of evaluating a relational expression, including * all of its inputs. */ public interface CumulativeCost extends Metadata { + MetadataDef<CumulativeCost> DEF = MetadataDef.of(CumulativeCost.class, + CumulativeCost.Handler.class, BuiltInMethod.CUMULATIVE_COST.method); + /** * Estimates the cost of executing a relational expression, including the * cost of its inputs. The default implementation for this query adds @@ -255,11 +364,20 @@ public abstract class BuiltInMetadata { * determined */ RelOptCost getCumulativeCost(); + + /** Handler API. */ + interface Handler extends MetadataHandler<CumulativeCost> { + RelOptCost getCumulativeCost(RelNode r, RelMetadataQuery mq); + } } /** Metadata about the cost of evaluating a relational expression, not * including its inputs. */ public interface NonCumulativeCost extends Metadata { + MetadataDef<NonCumulativeCost> DEF = MetadataDef.of(NonCumulativeCost.class, + NonCumulativeCost.Handler.class, + BuiltInMethod.NON_CUMULATIVE_COST.method); + /** * Estimates the cost of executing a relational expression, not counting the * cost of its inputs. (However, the non-cumulative cost is still usually @@ -271,10 +389,19 @@ public abstract class BuiltInMetadata { * determined */ RelOptCost getNonCumulativeCost(); + + /** Handler API. */ + interface Handler extends MetadataHandler<NonCumulativeCost> { + RelOptCost getNonCumulativeCost(RelNode r, RelMetadataQuery mq); + } } /** Metadata about whether a relational expression should appear in a plan. */ public interface ExplainVisibility extends Metadata { + MetadataDef<ExplainVisibility> DEF = MetadataDef.of(ExplainVisibility.class, + ExplainVisibility.Handler.class, + BuiltInMethod.EXPLAIN_VISIBILITY.method); + /** * Determines whether a relational expression should be visible in EXPLAIN * PLAN output at a particular level of detail. @@ -283,11 +410,20 @@ public abstract class BuiltInMetadata { * @return true for visible, false for invisible */ Boolean isVisibleInExplain(SqlExplainLevel explainLevel); + + /** Handler API. */ + interface Handler extends MetadataHandler<ExplainVisibility> { + Boolean isVisibleInExplain(RelNode r, RelMetadataQuery mq, + SqlExplainLevel explainLevel); + } } /** Metadata about the predicates that hold in the rows emitted from a * relational expression. */ public interface Predicates extends Metadata { + MetadataDef<Predicates> DEF = MetadataDef.of(Predicates.class, + Predicates.Handler.class, BuiltInMethod.PREDICATES.method); + /** * Derives the predicates that hold on rows emitted from a relational * expression. @@ -295,12 +431,21 @@ public abstract class BuiltInMetadata { * @return Predicate list */ RelOptPredicateList getPredicates(); + + /** Handler API. */ + interface Handler extends MetadataHandler<Predicates> { + RelOptPredicateList getPredicates(RelNode r, RelMetadataQuery mq); + } } /** Metadata about the degree of parallelism of a relational expression, and * how its operators are assigned to processes with independent resource * pools. */ public interface Parallelism extends Metadata { + MetadataDef<Parallelism> DEF = MetadataDef.of(Parallelism.class, + Parallelism.Handler.class, BuiltInMethod.IS_PHASE_TRANSITION.method, + BuiltInMethod.SPLIT_COUNT.method); + /** Returns whether each physical operator implementing this relational * expression belongs to a different process than its inputs. * @@ -321,10 +466,21 @@ public abstract class BuiltInMetadata { * each operator instance. */ Integer splitCount(); + + /** Handler API. */ + interface Handler extends MetadataHandler<Parallelism> { + Boolean isPhaseTransition(RelNode r, RelMetadataQuery mq); + Integer splitCount(RelNode r, RelMetadataQuery mq); + } } /** Metadata about the memory use of an operator. */ public interface Memory extends Metadata { + MetadataDef<Memory> DEF = MetadataDef.of(Memory.class, + Memory.Handler.class, BuiltInMethod.MEMORY.method, + BuiltInMethod.CUMULATIVE_MEMORY_WITHIN_PHASE.method, + BuiltInMethod.CUMULATIVE_MEMORY_WITHIN_PHASE_SPLIT.method); + /** Returns the expected amount of memory, in bytes, required by a physical * operator implementing this relational expression, across all splits. * @@ -356,6 +512,13 @@ public abstract class BuiltInMetadata { * = cumulativeMemoryWithinPhase / Parallelism.splitCount</blockquote> */ Double cumulativeMemoryWithinPhaseSplit(); + + /** Handler API. */ + interface Handler extends MetadataHandler<Memory> { + Double memory(RelNode r, RelMetadataQuery mq); + Double cumulativeMemoryWithinPhase(RelNode r, RelMetadataQuery mq); + Double cumulativeMemoryWithinPhaseSplit(RelNode r, RelMetadataQuery mq); + } } /** The built-in forms of metadata. */ http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java index 8438a8d..202bf4c 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java @@ -77,6 +77,11 @@ public class CachingRelMetadataProvider implements RelMetadataProvider { }; } + public <M extends Metadata> Map<Method, MetadataHandler<M>> + handlers(MetadataDef<M> def) { + return underlyingProvider.handlers(def); + } + //~ Inner Classes ---------------------------------------------------------- /** An entry in the cache. Consists of the cached object and the timestamp http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java index eb8aec5..698bc9c 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java @@ -20,6 +20,7 @@ import org.apache.calcite.rel.RelNode; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import java.lang.reflect.InvocationHandler; @@ -28,6 +29,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * Implementation of the {@link RelMetadataProvider} @@ -51,10 +53,21 @@ public class ChainedRelMetadataProvider implements RelMetadataProvider { protected ChainedRelMetadataProvider( ImmutableList<RelMetadataProvider> providers) { this.providers = providers; + assert !providers.contains(this); } //~ Methods ---------------------------------------------------------------- + @Override public boolean equals(Object obj) { + return obj == this + || obj instanceof ChainedRelMetadataProvider + && providers.equals(((ChainedRelMetadataProvider) obj).providers); + } + + @Override public int hashCode() { + return providers.hashCode(); + } + public <M extends Metadata> UnboundMetadata<M> apply(Class<? extends RelNode> relClass, final Class<? extends M> metadataClass) { @@ -91,6 +104,16 @@ public class ChainedRelMetadataProvider implements RelMetadataProvider { } } + public <M extends Metadata> Map<Method, MetadataHandler<M>> + handlers(MetadataDef<M> def) { + final ImmutableMap.Builder<Method, MetadataHandler<M>> builder = + ImmutableMap.builder(); + for (RelMetadataProvider provider : providers.reverse()) { + builder.putAll(provider.handlers(def)); + } + return builder.build(); + } + /** Creates a chain. */ public static RelMetadataProvider of(List<RelMetadataProvider> list) { return new ChainedRelMetadataProvider(ImmutableList.copyOf(list)); http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java index e40f1d4..2abd5b5 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java @@ -25,14 +25,20 @@ import com.google.common.collect.ImmutableList; * the methods declared in {@link RelMetadataQuery}. */ public class DefaultRelMetadataProvider extends ChainedRelMetadataProvider { + public static final DefaultRelMetadataProvider INSTANCE = + new DefaultRelMetadataProvider(); + //~ Constructors ----------------------------------------------------------- /** * Creates a new default provider. This provider defines "catch-all" * handlers for generic RelNodes, so it should always be given lowest * priority when chaining. + * + * <p>Use this constructor only from a sub-class. Otherwise use the singleton + * instance, {@link #INSTANCE}. */ - public DefaultRelMetadataProvider() { + protected DefaultRelMetadataProvider() { super( ImmutableList.of( RelMdPercentageOriginalRows.SOURCE, http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..c861106 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java @@ -0,0 +1,522 @@ +/* + * 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.adapter.enumerable.EnumerableAggregate; +import org.apache.calcite.adapter.enumerable.EnumerableFilter; +import org.apache.calcite.adapter.enumerable.EnumerableJoin; +import org.apache.calcite.adapter.enumerable.EnumerableProject; +import org.apache.calcite.adapter.enumerable.EnumerableTableScan; +import org.apache.calcite.interpreter.JaninoRexCompiler; +import org.apache.calcite.linq4j.Ord; +import org.apache.calcite.linq4j.tree.ClassDeclaration; +import org.apache.calcite.linq4j.tree.MemberDeclaration; +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.prepare.CalcitePrepareImpl; +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.rex.RexNode; +import org.apache.calcite.util.ControlFlowException; +import org.apache.calcite.util.Pair; + +import com.google.common.base.Throwables; +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 org.codehaus.commons.compiler.CompileException; +import org.codehaus.commons.compiler.CompilerFactoryFactory; +import org.codehaus.commons.compiler.IClassBodyEvaluator; +import org.codehaus.commons.compiler.ICompilerFactory; + +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.ExecutionException; +import javax.annotation.Nonnull; + +/** + * Implementation of the {@link RelMetadataProvider} interface that generates + * a class that dispatches to the underlying providers. + */ +public class JaninoRelMetadataProvider implements RelMetadataProvider { + private final RelMetadataProvider provider; + + // Constants and static fields + + 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 + * correctly. */ + private static final LoadingCache<Key, MetadataHandler> HANDLERS = + CacheBuilder.newBuilder().build( + new CacheLoader<Key, MetadataHandler>() { + public MetadataHandler load(@Nonnull Key key) { + //noinspection unchecked + return 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, + EnumerableJoin.class, + EnumerableTableScan.class)); + } + + /** Private constructor; use {@link #of}. */ + private JaninoRelMetadataProvider(RelMetadataProvider provider) { + this.provider = provider; + } + + /** Creates a JaninoRelMetadataProvider. + * + * @param provider Underlying provider + */ + public static JaninoRelMetadataProvider of(RelMetadataProvider provider) { + if (provider instanceof JaninoRelMetadataProvider) { + return (JaninoRelMetadataProvider) provider; + } + return new JaninoRelMetadataProvider(provider); + } + + @Override public boolean equals(Object obj) { + return obj == this + || obj instanceof JaninoRelMetadataProvider + && ((JaninoRelMetadataProvider) obj).provider.equals(provider); + } + + @Override public int hashCode() { + return 109 + provider.hashCode(); + } + + public <M extends Metadata> UnboundMetadata<M> apply( + Class<? extends RelNode> relClass, Class<? extends M> metadataClass) { + throw new UnsupportedOperationException(); + } + + public <M extends Metadata> Map<Method, MetadataHandler<M>> + handlers(MetadataDef<M> def) { + return provider.handlers(def); + } + + private static <M extends Metadata> + MetadataHandler<M> load3(MetadataDef<M> def, + Map<Method, MetadataHandler<M>> map, + ImmutableList<Class<? extends RelNode>> relClasses) { + 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((Map) map); + for (MetadataHandler provider : space.providerMap.values()) { + if (providerSet.add(provider)) { + providerList.add(Pair.of("provider" + (providerSet.size() - 1), + provider)); + } + } + + 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(" public ").append(name).append("(java.util.List relClasses"); + for (Pair<String, MetadataHandler> pair : providerList) { + buff.append(",\n") + .append(" ") + .append(pair.right.getClass().getName()) + .append(' ') + .append(pair.left); + } + buff.append(") {\n") + .append(" this.relClasses = relClasses;\n"); + + for (Pair<String, MetadataHandler> pair : providerList) { + buff.append(" this.").append(pair.left).append(" = ").append(pair.left) + .append(";\n"); + } + buff.append(" }\n") + .append(" public org.apache.calcite.rel.metadata.MetadataDef getDef() {\n") + .append(" return ") + .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(" org.apache.calcite.rel.RelNode r,\n") + .append(" org.apache.calcite.rel.metadata.RelMetadataQuery 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(")"); + } + buff.append(", r"); + safeArgList(buff, method.e) + .append(");\n") + .append(" final Object v = mq.map.get(key);\n") + .append(" if (v != null) {\n") + .append(" if (v == ") + .append(NullSentinel.class.getName()) + .append(".ACTIVE) {\n") + .append(" throw ") + .append(CyclicMetadataException.class.getName()) + .append(".INSTANCE;\n") + .append(" }\n") + .append(" return (") + .append(method.e.getReturnType().getName()) + .append(") v;\n") + .append(" }\n") + .append(" mq.map.put(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(key, x);\n") + .append(" return x;\n") + .append(" } catch (") + .append(NoHandler.class.getName()) + .append(" e) {\n") + .append(" mq.map.remove(key);\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(" org.apache.calcite.rel.RelNode r,\n") + .append(" org.apache.calcite.rel.metadata.RelMetadataQuery 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()); + } + } + ClassDeclaration decl = new ClassDeclaration(0, name, Object.class, + ImmutableList.<Type>of(), ImmutableList.<MemberDeclaration>of()); + final List<Object> argList = new ArrayList<Object>(Pair.right(providerList)); + argList.add(0, ImmutableList.copyOf(relClasses)); + try { + return compile(decl, buff.toString(), def, argList); + } catch (CompileException | IOException e) { + System.out.println(buff); + throw Throwables.propagate(e); + } + } + + 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; + } + } + throw new AssertionError("not found: " + declaringClass); + } + + /** Returns e.g. ", ignoreNulls". */ + private static StringBuilder argList(StringBuilder buff, Method method) { + for (Ord<Class<?>> t : Ord.zip(method.getParameterTypes())) { + buff.append(", a").append(t.i); + } + return buff; + } + + /** Returns e.g. ", ignoreNulls". */ + private static StringBuilder safeArgList(StringBuilder buff, Method method) { + for (Ord<Class<?>> t : Ord.zip(method.getParameterTypes())) { + if (Primitive.is(t.e)) { + buff.append(", a").append(t.i); + } else if (RexNode.class.isAssignableFrom(t.e)) { + // For RexNode, convert to string, because equals does not look deep. + // a1 == null ? "" : a1.toString() + buff.append(", a").append(t.i).append(" == null ? \"\" : a") + .append(t.i).append(".toString()"); + } else { + buff.append(", ") .append(NullSentinel.class.getName()) + .append(".mask(a").append(t.i).append(")"); + } + } + return buff; + } + + /** Returns e.g. ",\n boolean ignoreNulls". */ + private 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; + } + + static <M extends Metadata> MetadataHandler<M> + compile(ClassDeclaration expr, String s, MetadataDef<M> def, + List<Object> argList) throws CompileException, IOException { + final ICompilerFactory compilerFactory; + try { + compilerFactory = CompilerFactoryFactory.getDefaultCompilerFactory(); + } catch (Exception e) { + throw new IllegalStateException( + "Unable to instantiate java compiler", e); + } + final IClassBodyEvaluator cbe = compilerFactory.newClassBodyEvaluator(); + cbe.setClassName(expr.name); + cbe.setImplementedInterfaces(new Class[]{def.handlerClass}); + cbe.setParentClassLoader(JaninoRexCompiler.class.getClassLoader()); + if (CalcitePrepareImpl.DEBUG) { + // Add line numbers to the generated janino class + cbe.setDebuggingInformation(true, true, true); + System.out.println(s); + } + cbe.cook(new StringReader(s)); + final Constructor constructor = cbe.getClazz().getDeclaredConstructors()[0]; + final Object o; + try { + o = constructor.newInstance(argList.toArray()); + } catch (InstantiationException + | IllegalAccessException + | InvocationTargetException e) { + throw Throwables.propagate(e); + } + return def.handlerClass.cast(o); + } + + 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)); + //noinspection unchecked + return (H) HANDLERS.get(key); + } catch (ExecutionException e) { + throw Throwables.propagate(e); + } + } + + 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); + } + + /** 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. */ + 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); + } + } + if (ALL_RELS.addAll(list)) { + HANDLERS.invalidateAll(); + } + } + + /** Exception that indicates there there should be a handler for + * this class but there is not. The action is probably to + * re-generate the handler class. */ + public static class NoHandler extends ControlFlowException { + public final Class<? extends RelNode> relClass; + + public NoHandler(Class<? extends RelNode> relClass) { + this.relClass = relClass; + } + } + + /** Key for the cache. */ + 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) { + this.def = def; + this.provider = provider; + this.relClasses = relClassList; + } + + @Override public int hashCode() { + return (def.hashCode() * 37 + + provider.hashCode()) * 37 + + relClasses.hashCode(); + } + + @Override public boolean equals(Object obj) { + return this == obj + || obj instanceof Key + && ((Key) obj).def.equals(def) + && ((Key) obj).provider.equals(provider) + && ((Key) obj).relClasses.equals(relClasses); + } + } +} + +// End JaninoRelMetadataProvider.java http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/MetadataDef.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/MetadataDef.java b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataDef.java new file mode 100644 index 0000000..e7924e7 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataDef.java @@ -0,0 +1,67 @@ +/* + * 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; +import org.apache.calcite.util.Pair; + +import com.google.common.collect.ImmutableList; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; + +/** + * Definition of metadata. + * + * @param <M> Kind of metadata + */ +public class MetadataDef<M extends Metadata> { + public final Class<M> metadataClass; + public final Class<? extends MetadataHandler<M>> handlerClass; + public final ImmutableList<Method> methods; + + private MetadataDef(Class<M> metadataClass, + Class<? extends MetadataHandler<M>> handlerClass, Method... methods) { + this.metadataClass = metadataClass; + this.handlerClass = handlerClass; + this.methods = ImmutableList.copyOf(methods); + final Method[] handlerMethods = handlerClass.getDeclaredMethods(); + + // Handler must have the same methods as Metadata, each method having + // additional "subclass-of-RelNode, RelMetadataQuery" parameters. + assert handlerMethods.length == methods.length; + for (Pair<Method, Method> pair : Pair.zip(methods, handlerMethods)) { + final List<Class<?>> leftTypes = + Arrays.asList(pair.left.getParameterTypes()); + final List<Class<?>> rightTypes = + Arrays.asList(pair.right.getParameterTypes()); + assert leftTypes.size() + 2 == rightTypes.size(); + assert RelNode.class.isAssignableFrom(rightTypes.get(0)); + assert RelMetadataQuery.class == rightTypes.get(1); + assert leftTypes.equals(rightTypes.subList(2, rightTypes.size())); + } + } + + /** Creates a {@link org.apache.calcite.rel.metadata.MetadataDef}. */ + public static <M extends Metadata> MetadataDef<M> of(Class<M> metadataClass, + Class<? extends MetadataHandler<M>> handlerClass, Method... methods) { + return new MetadataDef<>(metadataClass, handlerClass, methods); + } +} + +// End MetadataDef.java http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/MetadataHandler.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/MetadataHandler.java b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataHandler.java new file mode 100644 index 0000000..95f8d62 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataHandler.java @@ -0,0 +1,28 @@ +/* + * 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; + +/** + * Marker interface for a handler of metadata. + * + * @param <M> Kind of metadata + */ +public interface MetadataHandler<M extends Metadata> { + MetadataDef<M> getDef(); +} + +// End MetadataHandler.java http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/NullSentinel.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/NullSentinel.java b/core/src/main/java/org/apache/calcite/rel/metadata/NullSentinel.java index 3f5dcf6..4215211 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/NullSentinel.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/NullSentinel.java @@ -17,18 +17,22 @@ package org.apache.calcite.rel.metadata; /** Placeholder for null values. */ -// package-private -enum NullSentinel { - INSTANCE; +public enum NullSentinel { + /** Placeholder for a null value. */ + INSTANCE, - static Comparable mask(Comparable value) { + /** Placeholder that means that a request for metadata is already active, + * therefore this request forms a cycle. */ + ACTIVE; + + public static Comparable mask(Comparable value) { if (value == null) { return INSTANCE; } return value; } - static Object mask(Object value) { + public static Object mask(Object value) { if (value == null) { return INSTANCE; } http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java index 8cdb55a..5f6d680 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java @@ -27,9 +27,7 @@ import org.apache.calcite.util.Util; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import com.google.common.collect.ImmutableMap; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; @@ -39,6 +37,8 @@ import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -63,6 +63,7 @@ public class ReflectiveRelMetadataProvider //~ Instance fields -------------------------------------------------------- private final ConcurrentMap<Class<RelNode>, UnboundMetadata> map; private final Class<? extends Metadata> metadataClass0; + private final ImmutableMap<Method, MetadataHandler> handlerMap; //~ Constructors ----------------------------------------------------------- @@ -71,13 +72,16 @@ public class ReflectiveRelMetadataProvider * * @param map Map * @param metadataClass0 Metadata class + * @param handlerMap Methods handled and the objects to call them on */ protected ReflectiveRelMetadataProvider( ConcurrentMap<Class<RelNode>, UnboundMetadata> map, - Class<? extends Metadata> metadataClass0) { + Class<? extends Metadata> metadataClass0, + Map<Method, MetadataHandler> handlerMap) { assert !map.isEmpty() : "are your methods named wrong?"; this.map = map; this.metadataClass0 = metadataClass0; + this.handlerMap = ImmutableMap.copyOf(handlerMap); } /** Returns an implementation of {@link RelMetadataProvider} that scans for @@ -98,54 +102,32 @@ public class ReflectiveRelMetadataProvider * or {@link org.apache.calcite.rel.core.Filter}.</p> */ public static RelMetadataProvider reflectiveSource(Method method, - Object target) { + MetadataHandler target) { return reflectiveSource(target, ImmutableList.of(method)); } /** Returns a reflective metadata provider that implements several * methods. */ - public static RelMetadataProvider reflectiveSource(Object target, + public static RelMetadataProvider reflectiveSource(MetadataHandler target, Method... methods) { return reflectiveSource(target, ImmutableList.copyOf(methods)); } - private static RelMetadataProvider reflectiveSource(final Object target, + private static RelMetadataProvider + reflectiveSource(final MetadataHandler target, final ImmutableList<Method> methods) { - assert methods.size() > 0; - final Method method0 = methods.get(0); - @SuppressWarnings("unchecked") - final Class<Metadata> metadataClass0 = (Class) method0.getDeclaringClass(); - assert Metadata.class.isAssignableFrom(metadataClass0); - for (Method method : methods) { - assert method.getDeclaringClass() == metadataClass0; - } - - // Find the distinct set of RelNode classes handled by this provider, - // ordered base-class first. - final Set<Class<RelNode>> classes = Sets.newHashSet(); - final Map<Pair<Class<RelNode>, Method>, Method> handlerMap = - Maps.newHashMap(); - for (final Method handlerMethod : target.getClass().getMethods()) { - for (Method method : methods) { - if (couldImplement(handlerMethod, method)) { - @SuppressWarnings("unchecked") final Class<RelNode> relNodeClass = - (Class<RelNode>) handlerMethod.getParameterTypes()[0]; - classes.add(relNodeClass); - handlerMap.put(Pair.of(relNodeClass, method), handlerMethod); - } - } - } + final Space2 space = Space2.create(target, methods); - // This needs to be a councurrent map since RelMetadataProvider are cached in static + // This needs to be a concurrent map since RelMetadataProvider are cached in static // fields, thus the map is subject to concurrent modifications later. // See map.put in org.apache.calcite.rel.metadata.ReflectiveRelMetadataProvider.apply( // java.lang.Class<? extends org.apache.calcite.rel.RelNode>) final ConcurrentMap<Class<RelNode>, UnboundMetadata> methodsMap = new ConcurrentHashMap<>(); - for (Class<RelNode> key : classes) { + for (Class<RelNode> key : space.classes) { ImmutableNullableList.Builder<Method> builder = ImmutableNullableList.builder(); for (final Method method : methods) { - builder.add(find(handlerMap, key, method)); + builder.add(space.find(key, method)); } final List<Method> handlerMethods = builder.build(); final UnboundMetadata function = @@ -153,8 +135,8 @@ public class ReflectiveRelMetadataProvider public Metadata bind(final RelNode rel, final RelMetadataQuery mq) { return (Metadata) Proxy.newProxyInstance( - metadataClass0.getClassLoader(), - new Class[]{metadataClass0}, + space.metadataClass0.getClassLoader(), + new Class[]{space.metadataClass0}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { @@ -170,7 +152,7 @@ public class ReflectiveRelMetadataProvider } if (method.equals( BuiltInMethod.OBJECT_TO_STRING.method)) { - return metadataClass0.getSimpleName() + "(" + rel + return space.metadataClass0.getSimpleName() + "(" + rel + ")"; } int i = methods.indexOf(method); @@ -206,7 +188,7 @@ public class ReflectiveRelMetadataProvider } key = FlatLists.copyOf(args2); } - if (!mq.set.add(key)) { + if (mq.map.put(key, NullSentinel.INSTANCE) != null) { throw CyclicMetadataException.INSTANCE; } try { @@ -216,7 +198,7 @@ public class ReflectiveRelMetadataProvider Throwables.propagateIfPossible(e.getCause()); throw e; } finally { - mq.set.remove(key); + mq.map.remove(key); } } }); @@ -224,39 +206,21 @@ public class ReflectiveRelMetadataProvider }; methodsMap.put(key, function); } - return new ReflectiveRelMetadataProvider(methodsMap, metadataClass0); + return new ReflectiveRelMetadataProvider(methodsMap, space.metadataClass0, + space.providerMap); } - /** Finds an implementation of a method for {@code relNodeClass} or its - * nearest base class. Assumes that base classes have already been added to - * {@code map}. */ - @SuppressWarnings({ "unchecked", "SuspiciousMethodCalls" }) - private static Method find(Map<Pair<Class<RelNode>, Method>, - Method> handlerMap, Class<RelNode> relNodeClass, Method method) { - List<Class<RelNode>> newSources = Lists.newArrayList(); - Method implementingMethod; - while (relNodeClass != null) { - implementingMethod = handlerMap.get(Pair.of(relNodeClass, method)); - if (implementingMethod != null) { - return implementingMethod; - } else { - newSources.add(relNodeClass); - } - for (Class<?> clazz : relNodeClass.getInterfaces()) { - if (RelNode.class.isAssignableFrom(clazz)) { - implementingMethod = handlerMap.get(Pair.of(clazz, method)); - if (implementingMethod != null) { - return implementingMethod; - } - } - } - if (RelNode.class.isAssignableFrom(relNodeClass.getSuperclass())) { - relNodeClass = (Class<RelNode>) relNodeClass.getSuperclass(); - } else { - relNodeClass = null; + public <M extends Metadata> Map<Method, MetadataHandler<M>> + handlers(MetadataDef<M> def) { + final ImmutableMap.Builder<Method, MetadataHandler<M>> builder = + ImmutableMap.builder(); + for (Map.Entry<Method, MetadataHandler> entry : handlerMap.entrySet()) { + if (def.methods.contains(entry.getKey())) { + //noinspection unchecked + builder.put(entry.getKey(), entry.getValue()); } } - return null; + return builder.build(); } private static boolean couldImplement(Method handlerMethod, Method method) { @@ -318,6 +282,91 @@ public class ReflectiveRelMetadataProvider } } } + + /** Workspace for computing which methods can act as handlers for + * given metadata methods. */ + static class Space { + final Set<Class<RelNode>> classes = new HashSet<>(); + final Map<Pair<Class<RelNode>, Method>, Method> handlerMap = new HashMap<>(); + final ImmutableMap<Method, MetadataHandler> providerMap; + + Space(Map<Method, MetadataHandler> providerMap) { + this.providerMap = ImmutableMap.copyOf(providerMap); + + // Find the distinct set of RelNode classes handled by this provider, + // ordered base-class first. + for (Map.Entry<Method, MetadataHandler> entry : providerMap.entrySet()) { + final Method method = entry.getKey(); + final MetadataHandler provider = entry.getValue(); + for (final Method handlerMethod : provider.getClass().getMethods()) { + if (couldImplement(handlerMethod, method)) { + @SuppressWarnings("unchecked") final Class<RelNode> relNodeClass = + (Class<RelNode>) handlerMethod.getParameterTypes()[0]; + classes.add(relNodeClass); + handlerMap.put(Pair.of(relNodeClass, method), handlerMethod); + } + } + } + } + + /** Finds an implementation of a method for {@code relNodeClass} or its + * nearest base class. Assumes that base classes have already been added to + * {@code map}. */ + @SuppressWarnings({ "unchecked", "SuspiciousMethodCalls" }) + Method find(Class<? extends RelNode> relNodeClass, Method method) { + Method implementingMethod; + while (relNodeClass != null) { + implementingMethod = handlerMap.get(Pair.of(relNodeClass, method)); + if (implementingMethod != null) { + return implementingMethod; + } + for (Class<?> clazz : relNodeClass.getInterfaces()) { + if (RelNode.class.isAssignableFrom(clazz)) { + implementingMethod = handlerMap.get(Pair.of(clazz, method)); + if (implementingMethod != null) { + return implementingMethod; + } + } + } + if (RelNode.class.isAssignableFrom(relNodeClass.getSuperclass())) { + relNodeClass = (Class<RelNode>) relNodeClass.getSuperclass(); + } else { + relNodeClass = null; + } + } + return null; + } + } + + /** Extended work space. */ + static class Space2 extends Space { + private Class<Metadata> metadataClass0; + + public Space2(Class<Metadata> metadataClass0, + ImmutableMap<Method, MetadataHandler> providerMap) { + super(providerMap); + this.metadataClass0 = metadataClass0; + } + + public static Space2 create(MetadataHandler target, + ImmutableList<Method> methods) { + assert methods.size() > 0; + final Method method0 = methods.get(0); + //noinspection unchecked + Class<Metadata> metadataClass0 = (Class) method0.getDeclaringClass(); + assert Metadata.class.isAssignableFrom(metadataClass0); + for (Method method : methods) { + assert method.getDeclaringClass() == metadataClass0; + } + + final ImmutableMap.Builder<Method, MetadataHandler> providerBuilder = + ImmutableMap.builder(); + for (final Method method : methods) { + providerBuilder.put(method, target); + } + return new Space2(metadataClass0, providerBuilder.build()); + } + } } // End ReflectiveRelMetadataProvider.java http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java index 92b325d..2867657 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java @@ -67,7 +67,8 @@ import java.util.TreeSet; * {@link org.apache.calcite.rel.metadata.RelMetadataQuery#collations} * for the standard logical algebra. */ -public class RelMdCollation { +public class RelMdCollation + implements MetadataHandler<BuiltInMetadata.Collation> { public static final RelMetadataProvider SOURCE = ReflectiveRelMetadataProvider.reflectiveSource( BuiltInMethod.COLLATIONS.method, new RelMdCollation()); @@ -78,6 +79,10 @@ public class RelMdCollation { //~ Methods ---------------------------------------------------------------- + public MetadataDef<BuiltInMetadata.Collation> getDef() { + return BuiltInMetadata.Collation.DEF; + } + /** Catch-all implementation for * {@link BuiltInMetadata.Collation#collations()}, * invoked using reflection, for any relational expression not http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java index 3038260..9944b17 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java @@ -42,7 +42,8 @@ import java.util.Set; * RelMdColumnOrigins supplies a default implementation of * {@link RelMetadataQuery#getColumnOrigins} for the standard logical algebra. */ -public class RelMdColumnOrigins { +public class RelMdColumnOrigins + implements MetadataHandler<BuiltInMetadata.ColumnOrigin> { public static final RelMetadataProvider SOURCE = ReflectiveRelMetadataProvider.reflectiveSource( BuiltInMethod.COLUMN_ORIGIN.method, new RelMdColumnOrigins()); @@ -53,6 +54,10 @@ public class RelMdColumnOrigins { //~ Methods ---------------------------------------------------------------- + public MetadataDef<BuiltInMetadata.ColumnOrigin> getDef() { + return BuiltInMetadata.ColumnOrigin.DEF; + } + public Set<RelColumnOrigin> getColumnOrigins(Aggregate rel, RelMetadataQuery mq, int iOutputColumn) { if (iOutputColumn < rel.getGroupCount()) { http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java index c872075..60fefb5 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 @@ -55,7 +55,8 @@ import java.util.Set; * RelMdColumnUniqueness supplies a default implementation of * {@link RelMetadataQuery#areColumnsUnique} for the standard logical algebra. */ -public class RelMdColumnUniqueness { +public class RelMdColumnUniqueness + implements MetadataHandler<BuiltInMetadata.ColumnUniqueness> { public static final RelMetadataProvider SOURCE = ReflectiveRelMetadataProvider.reflectiveSource( BuiltInMethod.COLUMN_UNIQUENESS.method, new RelMdColumnUniqueness()); @@ -66,6 +67,10 @@ public class RelMdColumnUniqueness { //~ Methods ---------------------------------------------------------------- + public MetadataDef<BuiltInMetadata.ColumnUniqueness> getDef() { + return BuiltInMetadata.ColumnUniqueness.DEF; + } + public Boolean areColumnsUnique(TableScan rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) { return rel.getTable().isKey(columns); http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java index eef245e..bf3fc3a 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java @@ -45,7 +45,8 @@ import java.util.List; * {@link RelMetadataQuery#getDistinctRowCount} for the standard logical * algebra. */ -public class RelMdDistinctRowCount { +public class RelMdDistinctRowCount + implements MetadataHandler<BuiltInMetadata.DistinctRowCount> { public static final RelMetadataProvider SOURCE = ReflectiveRelMetadataProvider.reflectiveSource( BuiltInMethod.DISTINCT_ROW_COUNT.method, new RelMdDistinctRowCount()); @@ -56,6 +57,10 @@ public class RelMdDistinctRowCount { //~ Methods ---------------------------------------------------------------- + public MetadataDef<BuiltInMetadata.DistinctRowCount> getDef() { + return BuiltInMetadata.DistinctRowCount.DEF; + } + /** Catch-all implementation for * {@link BuiltInMetadata.DistinctRowCount#getDistinctRowCount(ImmutableBitSet, RexNode)}, * invoked using reflection. http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java index 80f4e37..b594e4d 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 @@ -46,7 +46,8 @@ import java.util.List; * {@link RelMetadataQuery#distribution} * for the standard logical algebra. */ -public class RelMdDistribution { +public class RelMdDistribution + implements MetadataHandler<BuiltInMetadata.Distribution> { public static final RelMetadataProvider SOURCE = ReflectiveRelMetadataProvider.reflectiveSource( BuiltInMethod.DISTRIBUTION.method, new RelMdDistribution()); @@ -57,6 +58,10 @@ public class RelMdDistribution { //~ Methods ---------------------------------------------------------------- + public MetadataDef<BuiltInMetadata.Distribution> getDef() { + return BuiltInMetadata.Distribution.DEF; + } + /** Fallback method to deduce distribution for any relational expression not * handled by a more specific method. * http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java index bf51f2f..473b30a 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java @@ -24,7 +24,8 @@ import org.apache.calcite.util.BuiltInMethod; * RelMdExplainVisibility supplies a default implementation of * {@link RelMetadataQuery#isVisibleInExplain} for the standard logical algebra. */ -public class RelMdExplainVisibility { +public class RelMdExplainVisibility + implements MetadataHandler<BuiltInMetadata.ExplainVisibility> { public static final RelMetadataProvider SOURCE = ReflectiveRelMetadataProvider.reflectiveSource( BuiltInMethod.EXPLAIN_VISIBILITY.method, @@ -36,6 +37,10 @@ public class RelMdExplainVisibility { //~ Methods ---------------------------------------------------------------- + public MetadataDef<BuiltInMetadata.ExplainVisibility> getDef() { + return BuiltInMetadata.ExplainVisibility.DEF; + } + /** Catch-all implementation for * {@link BuiltInMetadata.ExplainVisibility#isVisibleInExplain(SqlExplainLevel)}, * invoked using reflection. http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java index ab9739f..dd811a7 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java @@ -38,13 +38,18 @@ import org.apache.calcite.util.Util; * RelMdMaxRowCount supplies a default implementation of * {@link RelMetadataQuery#getMaxRowCount} for the standard logical algebra. */ -public class RelMdMaxRowCount { +public class RelMdMaxRowCount + implements MetadataHandler<BuiltInMetadata.MaxRowCount> { public static final RelMetadataProvider SOURCE = ReflectiveRelMetadataProvider.reflectiveSource( BuiltInMethod.MAX_ROW_COUNT.method, new RelMdMaxRowCount()); //~ Methods ---------------------------------------------------------------- + public MetadataDef<BuiltInMetadata.MaxRowCount> getDef() { + return BuiltInMetadata.MaxRowCount.DEF; + } + public Double getMaxRowCount(Union rel, RelMetadataQuery mq) { double rowCount = 0.0; for (RelNode input : rel.getInputs()) { http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java index c760b0e..359af35 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java @@ -27,7 +27,7 @@ import org.apache.calcite.util.BuiltInMethod; * @see RelMetadataQuery#isPhaseTransition * @see RelMetadataQuery#splitCount */ -public class RelMdMemory { +public class RelMdMemory implements MetadataHandler<BuiltInMetadata.Memory> { /** Source for * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Memory}. */ public static final RelMetadataProvider SOURCE = @@ -42,6 +42,10 @@ public class RelMdMemory { //~ Methods ---------------------------------------------------------------- + public MetadataDef<BuiltInMetadata.Memory> getDef() { + return BuiltInMetadata.Memory.DEF; + } + /** Catch-all implementation for * {@link BuiltInMetadata.Memory#memory()}, * invoked using reflection. http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdParallelism.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdParallelism.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdParallelism.java index dcd69f1..4d144aa 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdParallelism.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdParallelism.java @@ -30,7 +30,8 @@ import org.apache.calcite.util.BuiltInMethod; * @see org.apache.calcite.rel.metadata.RelMetadataQuery#isPhaseTransition * @see org.apache.calcite.rel.metadata.RelMetadataQuery#splitCount */ -public class RelMdParallelism { +public class RelMdParallelism + implements MetadataHandler<BuiltInMetadata.Parallelism> { /** Source for * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Parallelism}. */ public static final RelMetadataProvider SOURCE = @@ -44,6 +45,10 @@ public class RelMdParallelism { //~ Methods ---------------------------------------------------------------- + public MetadataDef<BuiltInMetadata.Parallelism> getDef() { + return BuiltInMetadata.Parallelism.DEF; + } + /** Catch-all implementation for * {@link BuiltInMetadata.Parallelism#isPhaseTransition()}, * invoked using reflection. http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java index bde07a9..e50c8d0 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java @@ -33,7 +33,8 @@ import java.util.List; * {@link RelMetadataQuery#getPercentageOriginalRows} for the standard logical * algebra. */ -public class RelMdPercentageOriginalRows { +public class RelMdPercentageOriginalRows + implements MetadataHandler<BuiltInMetadata.PercentageOriginalRows> { private static final RelMdPercentageOriginalRows INSTANCE = new RelMdPercentageOriginalRows(); @@ -53,6 +54,10 @@ public class RelMdPercentageOriginalRows { private RelMdPercentageOriginalRows() {} + public MetadataDef<BuiltInMetadata.PercentageOriginalRows> getDef() { + return BuiltInMetadata.PercentageOriginalRows.DEF; + } + public Double getPercentageOriginalRows(Aggregate rel, RelMetadataQuery mq) { // REVIEW jvs 28-Mar-2006: The assumption here seems to be that // aggregation does not apply any filtering, so it does not modify the http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java index 180883f..67b3fe9 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java @@ -36,7 +36,8 @@ import java.util.List; * RelMdPopulationSize supplies a default implementation of * {@link RelMetadataQuery#getPopulationSize} for the standard logical algebra. */ -public class RelMdPopulationSize { +public class RelMdPopulationSize + implements MetadataHandler<BuiltInMetadata.PopulationSize> { public static final RelMetadataProvider SOURCE = ReflectiveRelMetadataProvider.reflectiveSource( BuiltInMethod.POPULATION_SIZE.method, new RelMdPopulationSize()); @@ -47,6 +48,10 @@ public class RelMdPopulationSize { //~ Methods ---------------------------------------------------------------- + public MetadataDef<BuiltInMetadata.PopulationSize> getDef() { + return BuiltInMetadata.PopulationSize.DEF; + } + public Double getPopulationSize(Filter rel, RelMetadataQuery mq, ImmutableBitSet groupKey) { return mq.getPopulationSize(rel.getInput(), groupKey); http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java index 4b1a894..7300624 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 @@ -115,12 +115,17 @@ import java.util.SortedMap; * </ul> * </ol> */ -public class RelMdPredicates { +public class RelMdPredicates + implements MetadataHandler<BuiltInMetadata.Predicates> { public static final RelMetadataProvider SOURCE = ReflectiveRelMetadataProvider .reflectiveSource(BuiltInMethod.PREDICATES.method, new RelMdPredicates()); private static final List<RexNode> EMPTY_LIST = ImmutableList.of(); + public MetadataDef<BuiltInMetadata.Predicates> getDef() { + return BuiltInMetadata.Predicates.DEF; + } + /** Catch-all implementation for * {@link BuiltInMetadata.Predicates#getPredicates()}, * invoked using reflection.
