This is an automated email from the ASF dual-hosted git repository. danny0405 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/master by this push: new a551d4b Revert "[CALCITE-4056] Remove Digest from RelNode and RexCall" a551d4b is described below commit a551d4bfa3b97c42e74fe4e30ca66ba493719c96 Author: yuzhao.cyz <yuzhao....@gmail.com> AuthorDate: Thu Jun 18 14:39:38 2020 +0800 Revert "[CALCITE-4056] Remove Digest from RelNode and RexCall" This reverts commit b00c1fd6 --- .../adapter/enumerable/EnumerableAggregate.java | 8 - .../adapter/enumerable/EnumerableFilter.java | 8 - .../adapter/enumerable/EnumerableHashJoin.java | 8 - .../adapter/enumerable/EnumerableMergeJoin.java | 8 - .../enumerable/EnumerableNestedLoopJoin.java | 8 - .../adapter/enumerable/EnumerableProject.java | 8 - .../enumerable/EnumerableSortedAggregate.java | 8 - .../main/java/org/apache/calcite/plan/Digest.java | 256 +++++++++++++++++++++ .../java/org/apache/calcite/plan/RelDigest.java | 52 ----- .../java/org/apache/calcite/plan/RelOptNode.java | 15 +- .../java/org/apache/calcite/plan/RelOptUtil.java | 1 - .../org/apache/calcite/plan/hep/HepPlanner.java | 12 +- .../org/apache/calcite/plan/hep/HepRelVertex.java | 15 +- .../org/apache/calcite/plan/volcano/RelSubset.java | 36 +-- .../calcite/plan/volcano/VolcanoPlanner.java | 55 ++--- .../org/apache/calcite/rel/AbstractRelNode.java | 135 ++--------- .../main/java/org/apache/calcite/rel/RelNode.java | 7 +- .../java/org/apache/calcite/rel/SingleRel.java | 1 - .../org/apache/calcite/rel/core/Aggregate.java | 21 -- .../java/org/apache/calcite/rel/core/Filter.java | 19 -- .../java/org/apache/calcite/rel/core/Join.java | 22 -- .../java/org/apache/calcite/rel/core/Project.java | 21 -- .../java/org/apache/calcite/rel/core/Values.java | 18 -- .../java/org/apache/calcite/rel/core/Window.java | 5 +- .../calcite/rel/logical/LogicalAggregate.java | 8 - .../apache/calcite/rel/logical/LogicalFilter.java | 12 - .../apache/calcite/rel/logical/LogicalJoin.java | 13 -- .../apache/calcite/rel/logical/LogicalProject.java | 8 - .../rel/metadata/JaninoRelMetadataProvider.java | 7 +- .../org/apache/calcite/rel/rules/MultiJoin.java | 1 - .../main/java/org/apache/calcite/rex/RexCall.java | 18 +- .../org/apache/calcite/rex/RexDynamicParam.java | 6 +- .../java/org/apache/calcite/rex/RexLiteral.java | 8 +- .../main/java/org/apache/calcite/rex/RexNode.java | 5 - .../main/java/org/apache/calcite/rex/RexOver.java | 6 +- .../java/org/apache/calcite/rex/RexSubQuery.java | 8 +- .../org/apache/calcite/test/HepPlannerTest.java | 2 +- 37 files changed, 354 insertions(+), 495 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java index 9967e2a..a0dee72 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java @@ -105,14 +105,6 @@ public class EnumerableAggregate extends Aggregate implements EnumerableRel { } } - @Override public boolean digestEquals(Object obj) { - return digestEquals0(obj); - } - - @Override public int digestHash() { - return digestHash0(); - } - public Result implement(EnumerableRelImplementor implementor, Prefer pref) { final JavaTypeFactory typeFactory = implementor.getTypeFactory(); final BlockBuilder builder = new BlockBuilder(); diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java index 8d26c7e..5af195c 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java @@ -71,14 +71,6 @@ public class EnumerableFilter return new EnumerableFilter(getCluster(), traitSet, input, condition); } - @Override public boolean digestEquals(Object obj) { - return digestEquals0(obj); - } - - @Override public int digestHash() { - return digestHash0(); - } - public Result implement(EnumerableRelImplementor implementor, Prefer pref) { // EnumerableCalc is always better throw new UnsupportedOperationException(); diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableHashJoin.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableHashJoin.java index 41d5d50..918919e 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableHashJoin.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableHashJoin.java @@ -166,14 +166,6 @@ public class EnumerableHashJoin extends Join implements EnumerableRel { } } - @Override public boolean digestEquals(Object obj) { - return digestEquals0(obj); - } - - @Override public int digestHash() { - return digestHash0(); - } - @Override public Result implement(EnumerableRelImplementor implementor, Prefer pref) { switch (joinType) { case SEMI: diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java index d9e04c4..f9f4d1a 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java @@ -130,14 +130,6 @@ public class EnumerableMergeJoin extends Join implements EnumerableRel { CorrelationId.setOf(variablesStopped), joinType); } - @Override public boolean digestEquals(Object obj) { - return digestEquals0(obj); - } - - @Override public int digestHash() { - return digestHash0(); - } - @Override public Pair<RelTraitSet, List<RelTraitSet>> passThroughTraits( final RelTraitSet required) { // Required collation keys can be subset or superset of merge join keys. diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableNestedLoopJoin.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableNestedLoopJoin.java index 0bd8aef..1561d8f 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableNestedLoopJoin.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableNestedLoopJoin.java @@ -121,14 +121,6 @@ public class EnumerableNestedLoopJoin extends Join implements EnumerableRel { return cost; } - @Override public boolean digestEquals(Object obj) { - return digestEquals0(obj); - } - - @Override public int digestHash() { - return digestHash0(); - } - @Override public Pair<RelTraitSet, List<RelTraitSet>> passThroughTraits( final RelTraitSet required) { // EnumerableNestedLoopJoin traits passdown shall only pass through collation to diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java index 5bc8a8b..7c62e2f 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java @@ -84,14 +84,6 @@ public class EnumerableProject extends Project implements EnumerableRel { projects, rowType); } - @Override public boolean digestEquals(Object obj) { - return digestEquals0(obj); - } - - @Override public int digestHash() { - return digestHash0(); - } - public Result implement(EnumerableRelImplementor implementor, Prefer pref) { // EnumerableCalcRel is always better throw new UnsupportedOperationException(); diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortedAggregate.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortedAggregate.java index f341921..00ca78a 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortedAggregate.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortedAggregate.java @@ -56,14 +56,6 @@ public class EnumerableSortedAggregate extends Aggregate implements EnumerableRe groupSet, groupSets, aggCalls); } - @Override public boolean digestEquals(Object obj) { - return digestEquals0(obj); - } - - @Override public int digestHash() { - return digestHash0(); - } - @Override public Pair<RelTraitSet, List<RelTraitSet>> passThroughTraits( final RelTraitSet required) { if (!isSimple(this)) { diff --git a/core/src/main/java/org/apache/calcite/plan/Digest.java b/core/src/main/java/org/apache/calcite/plan/Digest.java new file mode 100644 index 0000000..494cb14 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/plan/Digest.java @@ -0,0 +1,256 @@ +/* + * 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.plan; + +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.hint.Hintable; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.util.Pair; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import javax.annotation.Nonnull; + +/** + * A short description of relational expression's type, inputs, and + * other properties. The digest uniquely identifies the node; another node + * is equivalent if and only if it has the same value. + * + * <p>Row type is part of the digest for the rare occasion that similar + * expressions have different types, e.g. variants of + * {@code Project(child=rel#1, a=null)} where a is a null INTEGER or a + * null VARCHAR(10). Row type is represented as fieldTypes only, so {@code RelNode} + * that differ with field names only are treated equal. + * For instance, {@code Project(input=rel#1,empid=$0)} and {@code Project(input=rel#1,deptno=$0)} + * are equal. + * + * <p>Computed by {@code org.apache.calcite.rel.AbstractRelNode#computeDigest}, + * assigned by {@link org.apache.calcite.rel.AbstractRelNode#onRegister}, + * returned by {@link org.apache.calcite.rel.AbstractRelNode#getDigest()}. + */ +public class Digest implements Comparable<Digest> { + + //~ Instance fields -------------------------------------------------------- + + private final int hashCode; + private final List<Pair<String, Object>> items; + private final RelNode rel; + + // Used for debugging, computed lazily. + private String digest = null; + + //~ Constructors ----------------------------------------------------------- + + /** + * Creates a digest with given rel and properties. + * + * @param rel The rel + * @param items The properties, e.g. the inputs, the type, the traits and so on + */ + private Digest(RelNode rel, List<Pair<String, Object>> items) { + this.rel = rel; + this.items = normalizeContents(items); + this.hashCode = computeIdentity(rel, this.items); + } + + /** + * Creates a digest with given rel, the digest is computed as simple, + * see {@link #simpleRelDigest(RelNode)}. + */ + private Digest(RelNode rel) { + this(rel, simpleRelDigest(rel)); + } + + /** Creates a digest with given rel and string format digest. */ + private Digest(RelNode rel, String digest) { + this.rel = rel; + this.items = Collections.emptyList(); + this.digest = digest; + this.hashCode = this.digest.hashCode(); + } + + /** Returns the identity of this digest which is used to speedup hashCode and equals. */ + private static int computeIdentity(RelNode rel, List<Pair<String, Object>> contents) { + return Objects.hash(collect(rel, contents, false)); + } + + /** + * Collects the items used for {@link #hashCode} and {@link #equals}. + * + * <p>Generally, the items used for hashCode and equals should be the same. The exception + * is the row type of the relational expression: the row type is needed because during + * planning, new equivalent rels may be produced with changed fields nullability + * (i.e. most of them comes from the rex simplify or constant reduction). + * This expects to be rare case, so the hashcode is computed without row type + * but when it conflicts, we compare with the row type involved(sans field names). + * + * @param rel The rel to compute digest + * @param contents The rel properties should be considered in digest + * @param withType Whether to involve the row type + */ + private static Object[] collect( + RelNode rel, + List<Pair<String, Object>> contents, + boolean withType) { + List<Object> hashCodeItems = new ArrayList<>(); + // The type name. + hashCodeItems.add(rel.getRelTypeName()); + // The traits. + hashCodeItems.addAll(rel.getTraitSet()); + // The hints. + if (rel instanceof Hintable) { + hashCodeItems.addAll(((Hintable) rel).getHints()); + } + if (withType) { + // The row type sans field names. + RelDataType relType = rel.getRowType(); + if (relType.isStruct()) { + hashCodeItems.addAll(Pair.right(relType.getFieldList())); + } else { + // Make a decision here because + // some downstream projects have custom rel type which has no explicit fields. + hashCodeItems.add(relType); + } + } + // The rel node contents(e.g. the inputs or exprs). + hashCodeItems.addAll(contents); + return hashCodeItems.toArray(); + } + + /** Normalizes the rel node properties, currently, just to replace the + * {@link RelNode} with a simple string format digest. **/ + private static List<Pair<String, Object>> normalizeContents( + List<Pair<String, Object>> items) { + List<Pair<String, Object>> normalized = new ArrayList<>(); + for (Pair<String, Object> item : items) { + if (item.right instanceof RelNode) { + RelNode input = (RelNode) item.right; + normalized.add(Pair.of(item.left, simpleRelDigest(input))); + } else { + normalized.add(item); + } + } + return normalized; + } + + /** + * Returns a simple string format digest. + * + * <p>Currently, returns composition of class name and id. + * + * @param rel The rel + */ + private static String simpleRelDigest(RelNode rel) { + return rel.getRelTypeName() + '#' + rel.getId(); + } + + @Override public String toString() { + if (null != digest) { + return digest; + } + StringBuilder sb = new StringBuilder(); + sb.append(rel.getRelTypeName()); + + for (RelTrait trait : rel.getTraitSet()) { + sb.append('.'); + sb.append(trait.toString()); + } + + sb.append('('); + int j = 0; + for (Pair<String, Object> item : items) { + if (j++ > 0) { + sb.append(','); + } + sb.append(item.left); + sb.append('='); + sb.append(item.right); + } + sb.append(')'); + digest = sb.toString(); + return digest; + } + + @Override public int compareTo(@Nonnull Digest other) { + return this.equals(other) ? 0 : this.rel.getId() - other.rel.getId(); + } + + @Override public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Digest that = (Digest) o; + return hashCode == that.hashCode && deepEquals(that); + } + + /** + * The method is used to resolve hash conflict, in current 6000+ tests, there are about 8 + * tests with conflict, so we do not cache the hash code items in order to + * reduce mem consumption. + */ + private boolean deepEquals(Digest other) { + Object[] thisItems = collect(this.rel, this.items, true); + Object[] thatItems = collect(other.rel, other.items, true); + if (thisItems.length != thatItems.length) { + return false; + } + for (int i = 0; i < thisItems.length; i++) { + if (!Objects.equals(thisItems[i], thatItems[i])) { + return false; + } + } + return true; + } + + @Override public int hashCode() { + return hashCode; + } + + /** + * Creates a digest with given rel and properties. + */ + public static Digest create(RelNode rel, List<Pair<String, Object>> contents) { + return new Digest(rel, contents); + } + + /** + * Creates a digest with given rel. + */ + public static Digest create(RelNode rel) { + return new Digest(rel); + } + + /** + * Creates a digest with given rel and string format digest + */ + public static Digest create(RelNode rel, String digest) { + return new Digest(rel, digest); + } + + /** + * Instantiates a digest with solid string format digest, this digest should only + * be used as a initial. + */ + public static Digest initial(RelNode rel) { + return new Digest(rel, simpleRelDigest(rel)); + } +} diff --git a/core/src/main/java/org/apache/calcite/plan/RelDigest.java b/core/src/main/java/org/apache/calcite/plan/RelDigest.java deleted file mode 100644 index d2716b1..0000000 --- a/core/src/main/java/org/apache/calcite/plan/RelDigest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.plan; - -import org.apache.calcite.rel.AbstractRelNode; -import org.apache.calcite.rel.RelNode; -import org.apache.calcite.rel.RelWriter; - -import org.apiguardian.api.API; - -/** - * The digest is the exact representation of the corresponding {@code RelNode}, - * at anytime, anywhere. The only difference is that digest is compared using - * {@code #equals} and {@code #hashCode}, which are prohibited for RelNode, - * for legacy reasons. - * - * <p>It is highly recommended to override {@link AbstractRelNode#digestHash} - * and {@link AbstractRelNode#digestEquals(Object)}, instead of relying on - * {@link AbstractRelNode#explainTerms(RelWriter)}, which is used as the - * default source for equivalent comparison, for backward compatibility.</p> - * - * <p>INTERNAL USE ONLY.</p> - * - * @see AbstractRelNode#digestHash() - * @see AbstractRelNode#digestEquals(Object) - */ -@API(since = "1.24", status = API.Status.INTERNAL) -public interface RelDigest { - /** - * Reset state, possibly cache of hash code. - */ - void clear(); - - /** - * Returns the relnode that this digest is associated with. - */ - RelNode getRel(); -} diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptNode.java b/core/src/main/java/org/apache/calcite/plan/RelOptNode.java index 208a87c..8484f54 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptNode.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptNode.java @@ -18,8 +18,6 @@ package org.apache.calcite.plan; import org.apache.calcite.rel.type.RelDataType; -import org.apiguardian.api.API; - import java.util.List; /** @@ -47,20 +45,9 @@ public interface RelOptNode { * <p>If you want a descriptive string which contains the identity, call * {@link Object#toString()}, which always returns "rel#{id}:{digest}". * - * @return Digest string of this {@code RelNode} - */ - default String getDigest() { - return getRelDigest().toString(); - } - - /** - * Digest of the {@code RelNode}, for planner internal use only. - * * @return Digest of this {@code RelNode} - * @see #getDigest() */ - @API(since = "1.24", status = API.Status.INTERNAL) - RelDigest getRelDigest(); + Digest getDigest(); /** * Retrieves this RelNode's traits. Note that although the RelTraitSet diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java index c3a8e4e..1083047 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java @@ -2068,7 +2068,6 @@ public abstract class RelOptUtil { } - @Deprecated // to be removed before 1.25 public static StringBuilder appendRelDescription( StringBuilder sb, RelNode rel) { sb.append("rel#").append(rel.getId()) diff --git a/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java b/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java index 97cf205..36bd186 100644 --- a/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java +++ b/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java @@ -21,7 +21,7 @@ import org.apache.calcite.linq4j.function.Functions; import org.apache.calcite.plan.AbstractRelOptPlanner; import org.apache.calcite.plan.CommonRelSubExprRule; import org.apache.calcite.plan.Context; -import org.apache.calcite.plan.RelDigest; +import org.apache.calcite.plan.Digest; import org.apache.calcite.plan.RelOptCost; import org.apache.calcite.plan.RelOptCostFactory; import org.apache.calcite.plan.RelOptCostImpl; @@ -85,7 +85,7 @@ public class HepPlanner extends AbstractRelOptPlanner { * {@link RelDataType} is represented with its field types as {@code List<RelDataType>}. * This enables to treat as equal projects that differ in expression names only. */ - private final Map<RelDigest, HepRelVertex> mapDigestToVertex = + private final Map<Digest, HepRelVertex> mapDigestToVertex = new HashMap<>(); private int nTransformations; @@ -811,7 +811,7 @@ public class HepPlanner extends AbstractRelOptPlanner { // try to find equivalent rel only if DAG is allowed if (!noDag) { // Now, check if an equivalent vertex already exists in graph. - HepRelVertex equivVertex = mapDigestToVertex.get(rel.getRelDigest()); + HepRelVertex equivVertex = mapDigestToVertex.get(rel.getDigest()); if (equivVertex != null) { // Use existing vertex. return equivVertex; @@ -900,7 +900,7 @@ public class HepPlanner extends AbstractRelOptPlanner { // reachable from here. notifyDiscard(vertex.getCurrentRel()); } - RelDigest oldKey = vertex.getCurrentRel().getRelDigest(); + Digest oldKey = vertex.getCurrentRel().getDigest(); if (mapDigestToVertex.get(oldKey) == vertex) { mapDigestToVertex.remove(oldKey); } @@ -911,7 +911,7 @@ public class HepPlanner extends AbstractRelOptPlanner { // otherwise the digest will be removed wrongly in the mapDigestToVertex // when collectGC // so it must update the digest that map to vertex - mapDigestToVertex.put(rel.getRelDigest(), vertex); + mapDigestToVertex.put(rel.getDigest(), vertex); if (rel != vertex.getCurrentRel()) { vertex.replaceRel(rel); } @@ -977,7 +977,7 @@ public class HepPlanner extends AbstractRelOptPlanner { graphSizeLastGC = graph.vertexSet().size(); // Clean up digest map too. - Iterator<Map.Entry<RelDigest, HepRelVertex>> digestIter = + Iterator<Map.Entry<Digest, HepRelVertex>> digestIter = mapDigestToVertex.entrySet().iterator(); while (digestIter.hasNext()) { HepRelVertex vertex = digestIter.next().getValue(); 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 5d52e42..92394b1 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 @@ -16,6 +16,7 @@ */ package org.apache.calcite.plan.hep; +import org.apache.calcite.plan.Digest; import org.apache.calcite.plan.RelOptCost; import org.apache.calcite.plan.RelOptPlanner; import org.apache.calcite.plan.RelTraitSet; @@ -75,18 +76,8 @@ public class HepRelVertex extends AbstractRelNode { return currentRel.getRowType(); } - @Override public String toString() { - return "rel#" + id + ':' + "HepRelVertex(" + currentRel + ')'; - } - - @Override public boolean digestEquals(Object obj) { - return this == obj - || (obj instanceof HepRelVertex - && this.currentRel == ((HepRelVertex) obj).currentRel); - } - - @Override public int digestHash() { - return this.currentRel.getId(); + @Override protected Digest computeDigest() { + return Digest.create(this, getRelTypeName() + '#' + getCurrentRel().getId()); } /** diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/RelSubset.java b/core/src/main/java/org/apache/calcite/plan/volcano/RelSubset.java index 32486ca..e821496 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/RelSubset.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/RelSubset.java @@ -17,7 +17,7 @@ package org.apache.calcite.plan.volcano; import org.apache.calcite.linq4j.Linq4j; -import org.apache.calcite.plan.RelDigest; +import org.apache.calcite.plan.Digest; import org.apache.calcite.plan.RelOptCluster; import org.apache.calcite.plan.RelOptCost; import org.apache.calcite.plan.RelOptListener; @@ -129,9 +129,9 @@ public class RelSubset extends AbstractRelNode { RelTraitSet traits) { super(cluster, traits); this.set = set; - this.digest = new RelDigest0(); assert traits.allSimple(); computeBestCost(cluster.getPlanner()); + recomputeDigest(); } //~ Methods ---------------------------------------------------------------- @@ -225,6 +225,16 @@ public class RelSubset extends AbstractRelNode { pw.done(input); } + @Override protected Digest computeDigest() { + StringBuilder digest = new StringBuilder(getRelTypeName()); + digest.append('#'); + digest.append(set.id); + for (RelTrait trait : getTraitSet()) { + digest.append('.').append(trait); + } + return Digest.create(this, digest.toString()); + } + @Override protected RelDataType deriveRowType() { return set.rel.getRowType(); } @@ -537,28 +547,6 @@ public class RelSubset extends AbstractRelNode { } } - private class RelDigest0 implements RelDigest { - - @Override public RelNode getRel() { - return RelSubset.this; - } - - @Override public void clear() { - } - - @Override public boolean equals(final Object o) { - return this == o; - } - - @Override public int hashCode() { - return id; - } - - @Override public String toString() { - return "RelSubset#" + set.id + '.' + getTraitSet(); - } - } - /** * Visitor which walks over a tree of {@link RelSet}s, replacing each node * with the cheapest implementation of the expression. 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 7227293..4842932 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 @@ -22,7 +22,7 @@ import org.apache.calcite.plan.AbstractRelOptPlanner; import org.apache.calcite.plan.Context; import org.apache.calcite.plan.Convention; import org.apache.calcite.plan.ConventionTraitDef; -import org.apache.calcite.plan.RelDigest; +import org.apache.calcite.plan.Digest; import org.apache.calcite.plan.RelOptCost; import org.apache.calcite.plan.RelOptCostFactory; import org.apache.calcite.plan.RelOptLattice; @@ -103,7 +103,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner { * Canonical map from {@link String digest} to the unique * {@link RelNode relational expression} with that digest. */ - private final Map<RelDigest, RelNode> mapDigestToRel = + private final Map<Digest, RelNode> mapDigestToRel = new HashMap<>(); /** @@ -889,12 +889,11 @@ public class VolcanoPlanner extends AbstractRelOptPlanner { * @param rel Relational expression */ void rename(RelNode rel) { - String oldDigest = null; - if (LOGGER.isTraceEnabled()) { - oldDigest = rel.getDigest(); - } + final Digest oldDigest = rel.getDigest(); if (fixUpInputs(rel)) { - final RelDigest newDigest = rel.recomputeDigest(); + final RelNode removed = mapDigestToRel.remove(oldDigest); + assert removed == rel; + final Digest newDigest = rel.recomputeDigest(); LOGGER.trace("Rename #{} from '{}' to '{}'", rel.getId(), oldDigest, newDigest); final RelNode equivRel = mapDigestToRel.put(newDigest, rel); if (equivRel != null) { @@ -960,7 +959,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner { // Is there an equivalent relational expression? (This might have // just occurred because the relational expression's child was just // found to be equivalent to another set.) - RelNode equivRel = mapDigestToRel.get(rel.getRelDigest()); + RelNode equivRel = mapDigestToRel.get(rel.getDigest()); if (equivRel != null && equivRel != rel) { assert equivRel.getClass() == rel.getClass(); assert equivRel.getTraitSet().equals(rel.getTraitSet()); @@ -1023,33 +1022,25 @@ public class VolcanoPlanner extends AbstractRelOptPlanner { private boolean fixUpInputs(RelNode rel) { List<RelNode> inputs = rel.getInputs(); - List<RelNode> newInputs = new ArrayList<>(inputs.size()); + int i = -1; int changeCount = 0; for (RelNode input : inputs) { - assert input instanceof RelSubset; - final RelSubset subset = (RelSubset) input; - RelSubset newSubset = canonize(subset); - newInputs.add(newSubset); - if (newSubset != subset) { - if (subset.set != newSubset.set) { - subset.set.parents.remove(rel); - newSubset.set.parents.add(rel); + ++i; + if (input instanceof RelSubset) { + final RelSubset subset = (RelSubset) input; + RelSubset newSubset = canonize(subset); + if (newSubset != subset) { + rel.replaceInput(i, newSubset); + if (subset.set != newSubset.set) { + subset.set.parents.remove(rel); + newSubset.set.parents.add(rel); + } + changeCount++; } - changeCount++; } } - - if (changeCount > 0) { - RelMdUtil.clearCache(rel); - RelNode removed = mapDigestToRel.remove(rel.getRelDigest()); - assert removed == rel; - for (int i = 0; i < inputs.size(); i++) { - rel.replaceInput(i, newInputs.get(i)); - } - rel.recomputeDigest(); - return true; - } - return false; + RelMdUtil.clearCache(rel); + return changeCount > 0; } private RelSet merge(RelSet set, RelSet set2) { @@ -1177,7 +1168,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner { // If it is equivalent to an existing expression, return the set that // the equivalent expression belongs to. - RelDigest digest = rel.getRelDigest(); + Digest digest = rel.getDigest(); RelNode equivExp = mapDigestToRel.get(digest); if (equivExp == null) { // do nothing @@ -1207,7 +1198,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner { && (set.equivalentSet == null)) { LOGGER.trace( "Register #{} {} (and merge sets, because it is a conversion)", - rel.getId(), rel.getRelDigest()); + rel.getId(), rel.getDigest()); merge(set, childSet); // During the mergers, the child set may have changed, and since diff --git a/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java b/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java index 1ce29f1..c5ec873 100644 --- a/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java +++ b/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java @@ -18,7 +18,7 @@ package org.apache.calcite.rel; import org.apache.calcite.plan.Convention; import org.apache.calcite.plan.ConventionTraitDef; -import org.apache.calcite.plan.RelDigest; +import org.apache.calcite.plan.Digest; import org.apache.calcite.plan.RelOptCluster; import org.apache.calcite.plan.RelOptCost; import org.apache.calcite.plan.RelOptPlanner; @@ -27,7 +27,6 @@ import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.plan.RelTraitSet; import org.apache.calcite.rel.core.CorrelationId; -import org.apache.calcite.rel.hint.Hintable; import org.apache.calcite.rel.metadata.Metadata; import org.apache.calcite.rel.metadata.MetadataFactory; import org.apache.calcite.rel.metadata.RelMetadataQuery; @@ -44,13 +43,11 @@ import org.apache.calcite.util.trace.CalciteTrace; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import org.apiguardian.api.API; import org.slf4j.Logger; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; @@ -75,8 +72,7 @@ public abstract class AbstractRelNode implements RelNode { /** * The digest that uniquely identifies the node. */ - @API(since = "1.24", status = API.Status.INTERNAL) - protected RelDigest digest; + protected Digest digest; private final RelOptCluster cluster; @@ -101,7 +97,8 @@ public abstract class AbstractRelNode implements RelNode { this.cluster = cluster; this.traitSet = traitSet; this.id = NEXT_ID.getAndIncrement(); - this.digest = new RelDigest0(); + this.digest = Digest.initial(this); + LOGGER.trace("new {}", digest); } //~ Methods ---------------------------------------------------------------- @@ -337,8 +334,9 @@ public abstract class AbstractRelNode implements RelNode { return r; } - public RelDigest recomputeDigest() { - digest.clear(); + public Digest recomputeDigest() { + digest = computeDigest(); + assert digest != null : "computeDigest() should be non-null"; return digest; } @@ -350,7 +348,9 @@ public abstract class AbstractRelNode implements RelNode { /** Description, consists of id plus digest */ public String toString() { - return "rel#" + id + ':' + getDigest(); + StringBuilder sb = new StringBuilder(); + RelOptUtil.appendRelDescription(sb, this); + return sb.toString(); } /** Description, consists of id plus digest */ @@ -359,11 +359,7 @@ public abstract class AbstractRelNode implements RelNode { return this.toString(); } - public final String getDigest() { - return digest.toString(); - } - - public final RelDigest getRelDigest() { + public final Digest getDigest() { return digest; } @@ -372,6 +368,17 @@ public abstract class AbstractRelNode implements RelNode { } /** + * Computes the digest. Does not modify this object. + * + * @return Digest + */ + protected Digest computeDigest() { + RelDigestWriter rdw = new RelDigestWriter(); + explain(rdw); + return rdw.digest; + } + + /** * {@inheritDoc} * * <p>This method (and {@link #hashCode} is intentionally final. We do not want @@ -393,73 +400,6 @@ public abstract class AbstractRelNode implements RelNode { return super.hashCode(); } - public boolean digestEquals(Object obj) { - if (this == obj) { - return true; - } - if (this.getClass() != obj.getClass()) { - return false; - } - AbstractRelNode that = (AbstractRelNode) obj; - return this.getTraitSet() == that.getTraitSet() - && this.getDigestItems().equals(that.getDigestItems()) - && Pair.right(getRowType().getFieldList()).equals( - Pair.right(that.getRowType().getFieldList())) - && (!(that instanceof Hintable) - || ((Hintable) this).getHints().equals( - ((Hintable) that).getHints())); - } - - public int digestHash() { - return Objects.hash(getTraitSet(), getDigestItems(), - this instanceof Hintable ? ((Hintable) this).getHints() : null); - } - - private List<Pair<String, Object>> getDigestItems() { - RelDigestWriter rdw = new RelDigestWriter(); - explainTerms(rdw); - return rdw.values; - } - - private class RelDigest0 implements RelDigest { - /** - * Cache of hash code. - */ - private int hash = 0; - - @Override public RelNode getRel() { - return AbstractRelNode.this; - } - - @Override public void clear() { - hash = 0; - } - - @Override public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final RelDigest0 relDigest = (RelDigest0) o; - return digestEquals(relDigest.getRel()); - } - - @Override public int hashCode() { - if (hash == 0) { - hash = digestHash(); - } - return hash; - } - - @Override public String toString() { - RelDigestWriter rdw = new RelDigestWriter(); - explain(rdw); - return rdw.digest; - } - } - /** * A writer object used exclusively for computing the digest of a RelNode. * @@ -472,7 +412,7 @@ public abstract class AbstractRelNode implements RelNode { private final List<Pair<String, Object>> values = new ArrayList<>(); - String digest = null; + Digest digest = null; @Override public void explain(final RelNode rel, final List<Pair<String, Object>> valueList) { throw new IllegalStateException("Should not be called for computing digest"); @@ -483,39 +423,12 @@ public abstract class AbstractRelNode implements RelNode { } @Override public RelWriter item(String term, Object value) { - if (value != null && value.getClass().isArray()) { - // We can't call hashCode and equals on Array, so - // convert it to String to keep the same behaviour. - value = "" + value; - } values.add(Pair.of(term, value)); return this; } @Override public RelWriter done(RelNode node) { - StringBuilder sb = new StringBuilder(); - sb.append(node.getRelTypeName()); - sb.append('.'); - sb.append(node.getTraitSet()); - sb.append('('); - int j = 0; - for (Pair<String, Object> value : values) { - if (j++ > 0) { - sb.append(','); - } - sb.append(value.left); - sb.append('='); - if (value.right instanceof RelNode) { - RelNode input = (RelNode) value.right; - sb.append(input.getRelTypeName()); - sb.append('#'); - sb.append(input.getId()); - } else { - sb.append(value.right); - } - } - sb.append(')'); - digest = sb.toString(); + digest = Digest.create(node, values); return this; } } diff --git a/core/src/main/java/org/apache/calcite/rel/RelNode.java b/core/src/main/java/org/apache/calcite/rel/RelNode.java index 3d5a08e..18ff76f 100644 --- a/core/src/main/java/org/apache/calcite/rel/RelNode.java +++ b/core/src/main/java/org/apache/calcite/rel/RelNode.java @@ -17,7 +17,7 @@ package org.apache.calcite.rel; import org.apache.calcite.plan.Convention; -import org.apache.calcite.plan.RelDigest; +import org.apache.calcite.plan.Digest; import org.apache.calcite.plan.RelOptCost; import org.apache.calcite.plan.RelOptNode; import org.apache.calcite.plan.RelOptPlanner; @@ -33,8 +33,6 @@ import org.apache.calcite.rex.RexShuttle; import org.apache.calcite.util.ImmutableBitSet; import org.apache.calcite.util.Litmus; -import org.apiguardian.api.API; - import java.util.List; import java.util.Set; @@ -311,8 +309,7 @@ public interface RelNode extends RelOptNode, Cloneable { * * @return Digest of this relational expression */ - @API(since = "1.24", status = API.Status.INTERNAL) - RelDigest recomputeDigest(); + Digest recomputeDigest(); /** * Replaces the <code>ordinalInParent</code><sup>th</sup> input. You must diff --git a/core/src/main/java/org/apache/calcite/rel/SingleRel.java b/core/src/main/java/org/apache/calcite/rel/SingleRel.java index ef06c96..a650543 100644 --- a/core/src/main/java/org/apache/calcite/rel/SingleRel.java +++ b/core/src/main/java/org/apache/calcite/rel/SingleRel.java @@ -82,7 +82,6 @@ public abstract class SingleRel extends AbstractRelNode { RelNode rel) { assert ordinalInParent == 0; this.input = rel; - recomputeDigest(); } protected RelDataType deriveRowType() { diff --git a/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java b/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java index ec97781..82de9fc 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java @@ -332,27 +332,6 @@ public abstract class Aggregate extends SingleRel implements Hintable { return pw; } - protected boolean digestEquals0(Object obj) { - if (this == obj) { - return true; - } - if (this.getClass() != obj.getClass()) { - return false; - } - Aggregate o = (Aggregate) obj; - return getTraitSet() == o.getTraitSet() - && getInput().equals(o.getInput()) - && groupSet.equals(o.groupSet) - && groupSets.equals(o.groupSets) - && aggCalls.equals(o.aggCalls) - && hints.equals(o.hints) - && getRowType().equals(o.getRowType()); - } - - protected int digestHash0() { - return Objects.hash(traitSet, input, groupSet, groupSets, aggCalls, hints); - } - @Override public double estimateRowCount(RelMetadataQuery mq) { // Assume that each sort column has 50% of the value count. // Therefore one sort column has .5 * rowCount, diff --git a/core/src/main/java/org/apache/calcite/rel/core/Filter.java b/core/src/main/java/org/apache/calcite/rel/core/Filter.java index d56daf7..dd13485 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Filter.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Filter.java @@ -37,7 +37,6 @@ import org.apache.calcite.util.Litmus; import com.google.common.collect.ImmutableList; import java.util.List; -import java.util.Objects; /** * Relational expression that iterates over its input @@ -100,24 +99,6 @@ public abstract class Filter extends SingleRel { return ImmutableList.of(condition); } - protected boolean digestEquals0(Object obj) { - if (this == obj) { - return true; - } - if (this.getClass() != obj.getClass()) { - return false; - } - Filter o = (Filter) obj; - return getTraitSet() == o.getTraitSet() - && getInput().equals(o.getInput()) - && condition.equals(o.condition) - && getRowType().equals(o.getRowType()); - } - - protected int digestHash0() { - return Objects.hash(traitSet, input, condition); - } - public RelNode accept(RexShuttle shuttle) { RexNode condition = shuttle.apply(this.condition); if (this.condition == condition) { diff --git a/core/src/main/java/org/apache/calcite/rel/core/Join.java b/core/src/main/java/org/apache/calcite/rel/core/Join.java index 9f14f97..5d615a8 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Join.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Join.java @@ -239,28 +239,6 @@ public abstract class Join extends BiRel implements Hintable { getSystemFieldList()); } - protected boolean digestEquals0(Object obj) { - if (this == obj) { - return true; - } - if (this.getClass() != obj.getClass()) { - return false; - } - Join o = (Join) obj; - return getTraitSet() == o.getTraitSet() - && getInputs().equals(o.getInputs()) - && condition.equals(o.condition) - && joinType == o.joinType - && hints.equals(o.hints) - && variablesSet.equals(o.variablesSet) - && getRowType().equals(o.getRowType()); - } - - protected int digestHash0() { - return Objects.hash(traitSet, left, right, - condition, joinType, hints, variablesSet); - } - /** * Returns whether this LogicalJoin has already spawned a * {@code SemiJoin} via diff --git a/core/src/main/java/org/apache/calcite/rel/core/Project.java b/core/src/main/java/org/apache/calcite/rel/core/Project.java index d9abce7..e3c1412 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Project.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Project.java @@ -48,7 +48,6 @@ import com.google.common.collect.Lists; import java.util.HashSet; import java.util.List; -import java.util.Objects; import java.util.Set; /** @@ -155,26 +154,6 @@ public abstract class Project extends SingleRel implements Hintable { return exps; } - protected boolean digestEquals0(Object obj) { - if (this == obj) { - return true; - } - if (this.getClass() != obj.getClass()) { - return false; - } - Project o = (Project) obj; - return getTraitSet() == o.getTraitSet() - && getInput().equals(o.getInput()) - && exps.equals(o.exps) - && hints.equals(o.hints) - && Pair.right(getRowType().getFieldList()).equals( - Pair.right(o.getRowType().getFieldList())); - } - - protected int digestHash0() { - return Objects.hash(traitSet, input, exps, hints); - } - public RelNode accept(RexShuttle shuttle) { List<RexNode> exps = shuttle.apply(this.exps); if (this.exps == exps) { diff --git a/core/src/main/java/org/apache/calcite/rel/core/Values.java b/core/src/main/java/org/apache/calcite/rel/core/Values.java index 6386318..8f25815 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Values.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Values.java @@ -36,7 +36,6 @@ import org.apache.calcite.util.Pair; import com.google.common.collect.ImmutableList; import java.util.List; -import java.util.Objects; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -152,23 +151,6 @@ public abstract class Values extends AbstractRelNode { return true; } - @Override public boolean digestEquals(Object obj) { - if (this == obj) { - return true; - } - if (this.getClass() != obj.getClass()) { - return false; - } - Values o = (Values) obj; - return getTraitSet() == o.getTraitSet() - && tuples.equals(o.tuples) - && getRowType().equals(o.getRowType()); - } - - @Override public int digestHash() { - return Objects.hash(traitSet, tuples); - } - @Override protected RelDataType deriveRowType() { return rowType; } diff --git a/core/src/main/java/org/apache/calcite/rel/core/Window.java b/core/src/main/java/org/apache/calcite/rel/core/Window.java index dc364ea..620afe6 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Window.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Window.java @@ -411,10 +411,7 @@ public abstract class Window extends SingleRel { } @Override public int hashCode() { - if (hash == 0) { - hash = Objects.hash(super.hashCode(), ordinal, distinct, ignoreNulls); - } - return hash; + return Objects.hash(super.hashCode(), ordinal, distinct, ignoreNulls); } @Override public RexCall clone(RelDataType type, List<RexNode> operands) { diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalAggregate.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalAggregate.java index 5940e97..115e423 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalAggregate.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalAggregate.java @@ -161,12 +161,4 @@ public final class LogicalAggregate extends Aggregate { return new LogicalAggregate(getCluster(), traitSet, hintList, input, groupSet, groupSets, aggCalls); } - - @Override public boolean digestEquals(Object obj) { - return digestEquals0(obj); - } - - @Override public int digestHash() { - return digestHash0(); - } } diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java index 894fd17..8995241 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java @@ -135,16 +135,4 @@ public final class LogicalFilter extends Filter { return super.explainTerms(pw) .itemIf("variablesSet", variablesSet, !variablesSet.isEmpty()); } - - @Override public boolean digestEquals(Object obj) { - if (this == obj) { - return true; - } - return digestEquals0(obj) - && variablesSet.equals(((LogicalFilter) obj).variablesSet); - } - - @Override public int digestHash() { - return Objects.hash(digestHash0(), variablesSet); - } } diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalJoin.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalJoin.java index ec34121..4b55785 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalJoin.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalJoin.java @@ -203,17 +203,4 @@ public final class LogicalJoin extends Join { return new LogicalJoin(getCluster(), traitSet, hintList, left, right, condition, variablesSet, joinType, semiJoinDone, systemFieldList); } - - @Override public boolean digestEquals(Object obj) { - if (this == obj) { - return true; - } - return digestEquals0(obj) - && semiJoinDone == ((LogicalJoin) obj).semiJoinDone - && systemFieldList.equals(((LogicalJoin) obj).systemFieldList); - } - - @Override public int digestHash() { - return Objects.hash(digestHash0(), semiJoinDone, systemFieldList); - } } diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java index 22fc72d..2696dba 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java @@ -136,12 +136,4 @@ public final class LogicalProject extends Project { return new LogicalProject(getCluster(), traitSet, hintList, input, getProjects(), rowType); } - - @Override public boolean digestEquals(Object obj) { - return digestEquals0(obj); - } - - @Override public int digestHash() { - return digestHash0(); - } } 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 1ab362b..5e087fc 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 @@ -400,8 +400,13 @@ public class JaninoRelMetadataProvider implements RelMetadataProvider { /** 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) || RexNode.class.isAssignableFrom(t.e)) { + 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(")"); diff --git a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoin.java b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoin.java index ebf669b..1b09be8 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoin.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoin.java @@ -113,7 +113,6 @@ public final class MultiJoin extends AbstractRelNode { @Override public void replaceInput(int ordinalInParent, RelNode p) { inputs.set(ordinalInParent, p); - recomputeDigest(); } @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) { diff --git a/core/src/main/java/org/apache/calcite/rex/RexCall.java b/core/src/main/java/org/apache/calcite/rex/RexCall.java index 3dfe714..6b8f2ac 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexCall.java +++ b/core/src/main/java/org/apache/calcite/rex/RexCall.java @@ -231,7 +231,17 @@ public class RexCall extends RexNode { } @Override public final @Nonnull String toString() { - return computeDigest(digestWithType()); + if (!needNormalize()) { + // Non-normalize describe is requested + return computeDigest(digestWithType()); + } + // This data race is intentional + String localDigest = digest; + if (localDigest == null) { + localDigest = computeDigest(digestWithType()); + digest = Objects.requireNonNull(localDigest); + } + return localDigest; } private boolean digestWithType() { @@ -326,10 +336,6 @@ public class RexCall extends RexNode { } @Override public int hashCode() { - if (hash == 0) { - assert digest == null; - hash = Objects.hash(op, operands); - } - return hash; + return Objects.hash(op, operands); } } diff --git a/core/src/main/java/org/apache/calcite/rex/RexDynamicParam.java b/core/src/main/java/org/apache/calcite/rex/RexDynamicParam.java index 6e2b1d7..7a59021 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexDynamicParam.java +++ b/core/src/main/java/org/apache/calcite/rex/RexDynamicParam.java @@ -65,14 +65,12 @@ public class RexDynamicParam extends RexVariable { @Override public boolean equals(Object obj) { return this == obj || obj instanceof RexDynamicParam + && digest.equals(((RexDynamicParam) obj).digest) && type.equals(((RexDynamicParam) obj).type) && index == ((RexDynamicParam) obj).index; } @Override public int hashCode() { - if (hash == 0) { - hash = Objects.hash(type, index); - } - return hash; + return Objects.hash(digest, type, index); } } diff --git a/core/src/main/java/org/apache/calcite/rex/RexLiteral.java b/core/src/main/java/org/apache/calcite/rex/RexLiteral.java index 92da77b..5617d86 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexLiteral.java +++ b/core/src/main/java/org/apache/calcite/rex/RexLiteral.java @@ -1083,19 +1083,13 @@ public class RexLiteral extends RexNode { } public boolean equals(Object obj) { - if (this == obj) { - return true; - } return (obj instanceof RexLiteral) && equals(((RexLiteral) obj).value, value) && equals(((RexLiteral) obj).type, type); } public int hashCode() { - if (hash == 0) { - hash = Objects.hash(value, type); - } - return hash; + return Objects.hash(value, type); } public static Comparable value(RexNode node) { diff --git a/core/src/main/java/org/apache/calcite/rex/RexNode.java b/core/src/main/java/org/apache/calcite/rex/RexNode.java index f4dd69c..88332a2 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexNode.java +++ b/core/src/main/java/org/apache/calcite/rex/RexNode.java @@ -56,11 +56,6 @@ public abstract class RexNode { // Effectively final. Set in each sub-class constructor, and never re-set. protected String digest; - /** - * Cache of hash code. - */ - protected int hash = 0; - //~ Methods ---------------------------------------------------------------- public abstract RelDataType getType(); diff --git a/core/src/main/java/org/apache/calcite/rex/RexOver.java b/core/src/main/java/org/apache/calcite/rex/RexOver.java index 6e006c9..e90fa3c 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexOver.java +++ b/core/src/main/java/org/apache/calcite/rex/RexOver.java @@ -215,10 +215,6 @@ public class RexOver extends RexCall { } @Override public int hashCode() { - if (hash == 0) { - hash = Objects.hash(super.hashCode(), window, - distinct, ignoreNulls, op.allowsFraming()); - } - return hash; + return Objects.hash(super.hashCode(), window, distinct, ignoreNulls, op.allowsFraming()); } } diff --git a/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java b/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java index 0dbdbcc..d702387 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java +++ b/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java @@ -42,6 +42,7 @@ public class RexSubQuery extends RexCall { ImmutableList<RexNode> operands, RelNode rel) { super(type, op, operands); this.rel = rel; + this.digest = computeDigest(false); } /** Creates an IN sub-query. */ @@ -138,14 +139,11 @@ public class RexSubQuery extends RexCall { @Override public boolean equals(Object obj) { return obj == this - || obj instanceof RexSubQuery + || obj instanceof RexCall && toString().equals(obj.toString()); } @Override public int hashCode() { - if (hash == 0) { - hash = toString().hashCode(); - } - return hash; + return toString().hashCode(); } } diff --git a/core/src/test/java/org/apache/calcite/test/HepPlannerTest.java b/core/src/test/java/org/apache/calcite/test/HepPlannerTest.java index 90651fa..a4269c5 100644 --- a/core/src/test/java/org/apache/calcite/test/HepPlannerTest.java +++ b/core/src/test/java/org/apache/calcite/test/HepPlannerTest.java @@ -153,7 +153,7 @@ class HepPlannerTest extends RelOptTestBase { assertIncludesExactlyOnce("best.getDescription()", best.toString(), "LogicalUnion"); assertIncludesExactlyOnce("best.getDigest()", - best.getDigest(), "LogicalUnion"); + best.getDigest().toString(), "LogicalUnion"); } private void assertIncludesExactlyOnce(String message, String digest, String substring) {