You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by hy...@apache.org on 2020/06/18 03:55:12 UTC
[calcite] branch master updated: [CALCITE-4056] Remove Digest from
RelNode and RexCall
This is an automated email from the ASF dual-hosted git repository.
hyuan 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 b00c1fd [CALCITE-4056] Remove Digest from RelNode and RexCall
b00c1fd is described below
commit b00c1fd6e26b5e3cc1a810c9f862b184649cd1a6
Author: Haisheng Yuan <h....@alibaba-inc.com>
AuthorDate: Wed Jun 17 06:07:00 2020 -0500
[CALCITE-4056] Remove Digest from RelNode and RexCall
Close #2032
---
.../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, 495 insertions(+), 354 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 a0dee72..9967e2a 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,6 +105,14 @@ 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 5af195c..8d26c7e 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,6 +71,14 @@ 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 918919e..41d5d50 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,6 +166,14 @@ 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 f9f4d1a..d9e04c4 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,6 +130,14 @@ 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 1561d8f..0bd8aef 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,6 +121,14 @@ 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 7c62e2f..5bc8a8b 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,6 +84,14 @@ 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 00ca78a..f341921 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,6 +56,14 @@ 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
deleted file mode 100644
index 494cb14..0000000
--- a/core/src/main/java/org/apache/calcite/plan/Digest.java
+++ /dev/null
@@ -1,256 +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.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
new file mode 100644
index 0000000..d2716b1
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/plan/RelDigest.java
@@ -0,0 +1,52 @@
+/*
+ * 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 8484f54..208a87c 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptNode.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptNode.java
@@ -18,6 +18,8 @@ package org.apache.calcite.plan;
import org.apache.calcite.rel.type.RelDataType;
+import org.apiguardian.api.API;
+
import java.util.List;
/**
@@ -45,9 +47,20 @@ 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()
*/
- Digest getDigest();
+ @API(since = "1.24", status = API.Status.INTERNAL)
+ RelDigest getRelDigest();
/**
* 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 1083047..c3a8e4e 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -2068,6 +2068,7 @@ 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 36bd186..97cf205 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.Digest;
+import org.apache.calcite.plan.RelDigest;
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<Digest, HepRelVertex> mapDigestToVertex =
+ private final Map<RelDigest, 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.getDigest());
+ HepRelVertex equivVertex = mapDigestToVertex.get(rel.getRelDigest());
if (equivVertex != null) {
// Use existing vertex.
return equivVertex;
@@ -900,7 +900,7 @@ public class HepPlanner extends AbstractRelOptPlanner {
// reachable from here.
notifyDiscard(vertex.getCurrentRel());
}
- Digest oldKey = vertex.getCurrentRel().getDigest();
+ RelDigest oldKey = vertex.getCurrentRel().getRelDigest();
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.getDigest(), vertex);
+ mapDigestToVertex.put(rel.getRelDigest(), 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<Digest, HepRelVertex>> digestIter =
+ Iterator<Map.Entry<RelDigest, 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 92394b1..5d52e42 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,7 +16,6 @@
*/
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;
@@ -76,8 +75,18 @@ public class HepRelVertex extends AbstractRelNode {
return currentRel.getRowType();
}
- @Override protected Digest computeDigest() {
- return Digest.create(this, getRelTypeName() + '#' + getCurrentRel().getId());
+ @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();
}
/**
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 e821496..32486ca 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.Digest;
+import org.apache.calcite.plan.RelDigest;
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,16 +225,6 @@ 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();
}
@@ -547,6 +537,28 @@ 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 4842932..7227293 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.Digest;
+import org.apache.calcite.plan.RelDigest;
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<Digest, RelNode> mapDigestToRel =
+ private final Map<RelDigest, RelNode> mapDigestToRel =
new HashMap<>();
/**
@@ -889,11 +889,12 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
* @param rel Relational expression
*/
void rename(RelNode rel) {
- final Digest oldDigest = rel.getDigest();
+ String oldDigest = null;
+ if (LOGGER.isTraceEnabled()) {
+ oldDigest = rel.getDigest();
+ }
if (fixUpInputs(rel)) {
- final RelNode removed = mapDigestToRel.remove(oldDigest);
- assert removed == rel;
- final Digest newDigest = rel.recomputeDigest();
+ final RelDigest newDigest = rel.recomputeDigest();
LOGGER.trace("Rename #{} from '{}' to '{}'", rel.getId(), oldDigest, newDigest);
final RelNode equivRel = mapDigestToRel.put(newDigest, rel);
if (equivRel != null) {
@@ -959,7 +960,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.getDigest());
+ RelNode equivRel = mapDigestToRel.get(rel.getRelDigest());
if (equivRel != null && equivRel != rel) {
assert equivRel.getClass() == rel.getClass();
assert equivRel.getTraitSet().equals(rel.getTraitSet());
@@ -1022,25 +1023,33 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
private boolean fixUpInputs(RelNode rel) {
List<RelNode> inputs = rel.getInputs();
- int i = -1;
+ List<RelNode> newInputs = new ArrayList<>(inputs.size());
int changeCount = 0;
for (RelNode input : inputs) {
- ++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++;
+ 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);
}
+ changeCount++;
}
}
- RelMdUtil.clearCache(rel);
- return changeCount > 0;
+
+ 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;
}
private RelSet merge(RelSet set, RelSet set2) {
@@ -1168,7 +1177,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
// If it is equivalent to an existing expression, return the set that
// the equivalent expression belongs to.
- Digest digest = rel.getDigest();
+ RelDigest digest = rel.getRelDigest();
RelNode equivExp = mapDigestToRel.get(digest);
if (equivExp == null) {
// do nothing
@@ -1198,7 +1207,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
&& (set.equivalentSet == null)) {
LOGGER.trace(
"Register #{} {} (and merge sets, because it is a conversion)",
- rel.getId(), rel.getDigest());
+ rel.getId(), rel.getRelDigest());
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 c5ec873..1ce29f1 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.Digest;
+import org.apache.calcite.plan.RelDigest;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptCost;
import org.apache.calcite.plan.RelOptPlanner;
@@ -27,6 +27,7 @@ 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;
@@ -43,11 +44,13 @@ 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;
@@ -72,7 +75,8 @@ public abstract class AbstractRelNode implements RelNode {
/**
* The digest that uniquely identifies the node.
*/
- protected Digest digest;
+ @API(since = "1.24", status = API.Status.INTERNAL)
+ protected RelDigest digest;
private final RelOptCluster cluster;
@@ -97,8 +101,7 @@ public abstract class AbstractRelNode implements RelNode {
this.cluster = cluster;
this.traitSet = traitSet;
this.id = NEXT_ID.getAndIncrement();
- this.digest = Digest.initial(this);
- LOGGER.trace("new {}", digest);
+ this.digest = new RelDigest0();
}
//~ Methods ----------------------------------------------------------------
@@ -334,9 +337,8 @@ public abstract class AbstractRelNode implements RelNode {
return r;
}
- public Digest recomputeDigest() {
- digest = computeDigest();
- assert digest != null : "computeDigest() should be non-null";
+ public RelDigest recomputeDigest() {
+ digest.clear();
return digest;
}
@@ -348,9 +350,7 @@ public abstract class AbstractRelNode implements RelNode {
/** Description, consists of id plus digest */
public String toString() {
- StringBuilder sb = new StringBuilder();
- RelOptUtil.appendRelDescription(sb, this);
- return sb.toString();
+ return "rel#" + id + ':' + getDigest();
}
/** Description, consists of id plus digest */
@@ -359,7 +359,11 @@ public abstract class AbstractRelNode implements RelNode {
return this.toString();
}
- public final Digest getDigest() {
+ public final String getDigest() {
+ return digest.toString();
+ }
+
+ public final RelDigest getRelDigest() {
return digest;
}
@@ -368,17 +372,6 @@ 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
@@ -400,6 +393,73 @@ 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.
*
@@ -412,7 +472,7 @@ public abstract class AbstractRelNode implements RelNode {
private final List<Pair<String, Object>> values = new ArrayList<>();
- Digest digest = null;
+ String 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");
@@ -423,12 +483,39 @@ 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) {
- digest = Digest.create(node, values);
+ 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();
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 18ff76f..3d5a08e 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.Digest;
+import org.apache.calcite.plan.RelDigest;
import org.apache.calcite.plan.RelOptCost;
import org.apache.calcite.plan.RelOptNode;
import org.apache.calcite.plan.RelOptPlanner;
@@ -33,6 +33,8 @@ 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;
@@ -309,7 +311,8 @@ public interface RelNode extends RelOptNode, Cloneable {
*
* @return Digest of this relational expression
*/
- Digest recomputeDigest();
+ @API(since = "1.24", status = API.Status.INTERNAL)
+ RelDigest 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 a650543..ef06c96 100644
--- a/core/src/main/java/org/apache/calcite/rel/SingleRel.java
+++ b/core/src/main/java/org/apache/calcite/rel/SingleRel.java
@@ -82,6 +82,7 @@ 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 82de9fc..ec97781 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,6 +332,27 @@ 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 dd13485..d56daf7 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,6 +37,7 @@ 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
@@ -99,6 +100,24 @@ 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 5d615a8..9f14f97 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,6 +239,28 @@ 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 e3c1412..d9abce7 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,6 +48,7 @@ import com.google.common.collect.Lists;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -154,6 +155,26 @@ 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 8f25815..6386318 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,6 +36,7 @@ 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;
@@ -151,6 +152,23 @@ 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 620afe6..dc364ea 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,7 +411,10 @@ public abstract class Window extends SingleRel {
}
@Override public int hashCode() {
- return Objects.hash(super.hashCode(), ordinal, distinct, ignoreNulls);
+ if (hash == 0) {
+ hash = Objects.hash(super.hashCode(), ordinal, distinct, ignoreNulls);
+ }
+ return hash;
}
@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 115e423..5940e97 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,4 +161,12 @@ 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 8995241..894fd17 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,4 +135,16 @@ 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 4b55785..ec34121 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,4 +203,17 @@ 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 2696dba..22fc72d 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,4 +136,12 @@ 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 5e087fc..1ab362b 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,13 +400,8 @@ 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)) {
+ if (Primitive.is(t.e) || RexNode.class.isAssignableFrom(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 1b09be8..ebf669b 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,6 +113,7 @@ 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 6b8f2ac..3dfe714 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexCall.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexCall.java
@@ -231,17 +231,7 @@ public class RexCall extends RexNode {
}
@Override public final @Nonnull String toString() {
- 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;
+ return computeDigest(digestWithType());
}
private boolean digestWithType() {
@@ -336,6 +326,10 @@ public class RexCall extends RexNode {
}
@Override public int hashCode() {
- return Objects.hash(op, operands);
+ if (hash == 0) {
+ assert digest == null;
+ hash = Objects.hash(op, operands);
+ }
+ return hash;
}
}
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 7a59021..6e2b1d7 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexDynamicParam.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexDynamicParam.java
@@ -65,12 +65,14 @@ 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() {
- return Objects.hash(digest, type, index);
+ if (hash == 0) {
+ hash = Objects.hash(type, index);
+ }
+ return hash;
}
}
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 5617d86..92da77b 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexLiteral.java
@@ -1083,13 +1083,19 @@ 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() {
- return Objects.hash(value, type);
+ if (hash == 0) {
+ hash = Objects.hash(value, type);
+ }
+ return hash;
}
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 88332a2..f4dd69c 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexNode.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexNode.java
@@ -56,6 +56,11 @@ 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 e90fa3c..6e006c9 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexOver.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexOver.java
@@ -215,6 +215,10 @@ public class RexOver extends RexCall {
}
@Override public int hashCode() {
- return Objects.hash(super.hashCode(), window, distinct, ignoreNulls, op.allowsFraming());
+ if (hash == 0) {
+ hash = Objects.hash(super.hashCode(), window,
+ distinct, ignoreNulls, op.allowsFraming());
+ }
+ return hash;
}
}
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 d702387..0dbdbcc 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java
@@ -42,7 +42,6 @@ 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. */
@@ -139,11 +138,14 @@ public class RexSubQuery extends RexCall {
@Override public boolean equals(Object obj) {
return obj == this
- || obj instanceof RexCall
+ || obj instanceof RexSubQuery
&& toString().equals(obj.toString());
}
@Override public int hashCode() {
- return toString().hashCode();
+ if (hash == 0) {
+ hash = toString().hashCode();
+ }
+ return hash;
}
}
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 a4269c5..90651fa 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().toString(), "LogicalUnion");
+ best.getDigest(), "LogicalUnion");
}
private void assertIncludesExactlyOnce(String message, String digest, String substring) {