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 af3bca3 [CALCITE-3786] Make digestEquals and digestHash available to be overridden af3bca3 is described below commit af3bca328a40d2d6515ea00e2094974cc725d4c3 Author: Haisheng Yuan <h.y...@alibaba-inc.com> AuthorDate: Tue Jun 23 17:20:37 2020 -0500 [CALCITE-3786] Make digestEquals and digestHash available to be overridden By default #digestEquals() and #digestHash() collect digest attributes from compute hash code. This should work well for most cases. If the method is a performance bottleneck for your project, or the default behavior can't handle your scenario properly, you can choose to override #digestEquals() and Only these operators are changed to override the default behavior, for performance and demonstration purposes. All the other operators remain unchanged. Close #2044 --- .../java/org/apache/calcite/plan/RelOptNode.java | 15 +---------- .../org/apache/calcite/plan/hep/HepRelVertex.java | 11 +++++--- .../org/apache/calcite/rel/AbstractRelNode.java | 26 ++++++++++++------ .../main/java/org/apache/calcite/rel/RelNode.java | 24 ++++++++++++++--- .../java/org/apache/calcite/rel/core/Filter.java | 19 +++++++++++++ .../java/org/apache/calcite/rel/core/Join.java | 21 +++++++++++++++ .../java/org/apache/calcite/rel/core/Project.java | 20 ++++++++++++++ .../apache/calcite/rel/logical/LogicalFilter.java | 9 +++++++ .../apache/calcite/rel/logical/LogicalJoin.java | 13 +++++++++ .../apache/calcite/rel/logical/LogicalProject.java | 8 ++++++ .../org/apache/calcite/rel/type/RelDataType.java | 31 ++++++++++++++++++++++ 11 files changed, 169 insertions(+), 28 deletions(-) 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..c21f745 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; /** @@ -49,18 +47,7 @@ public interface RelOptNode { * * @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(); + String getDigest(); /** * Retrieves this RelNode's traits. Note that although the RelTraitSet 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 1061a61..4ad6d28 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 @@ -91,9 +91,14 @@ public class HepRelVertex extends AbstractRelNode { return currentRel; } - @Override public RelWriter explainTerms(final RelWriter pw) { - return super.explainTerms(pw) - .item("currentRel", currentRel.getId()); + @Override protected boolean digestEquals(Object obj) { + return this == obj + || (obj instanceof HepRelVertex + && currentRel == ((HepRelVertex) obj).currentRel); + } + + @Override protected int digestHash() { + return currentRel.getId(); } @Override public String getDigest() { 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 4ab68a0..ec52c10 100644 --- a/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java +++ b/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java @@ -337,9 +337,8 @@ public abstract class AbstractRelNode implements RelNode { return r; } - public RelDigest recomputeDigest() { + public void recomputeDigest() { digest.clear(); - return digest; } public void replaceInput( @@ -394,9 +393,19 @@ public abstract class AbstractRelNode implements RelNode { } /** - * Equality check for RelNode digest + * Equality check for RelNode digest. + * + * <p>By default this method collects digest attributes from + * {@link #explainTerms(RelWriter)}, then compares each attribute pair. + * This should work well for most cases. If this method is a performance + * bottleneck for your project, or the default behavior can't handle + * your scenario properly, you can choose to override this method and + * {@link #digestHash()}. See {@code LogicalJoin} as an example.</p> + * + * @return Whether the 2 RelNodes are equivalent or have the same digest. + * @see #digestHash() */ - private boolean digestEquals(Object obj) { + protected boolean digestEquals(Object obj) { if (this == obj) { return true; } @@ -406,14 +415,15 @@ public abstract class AbstractRelNode implements RelNode { AbstractRelNode that = (AbstractRelNode) obj; return this.getTraitSet().equals(that.getTraitSet()) && this.getDigestItems().equals(that.getDigestItems()) - && Pair.right(getRowType().getFieldList()).equals( - Pair.right(that.getRowType().getFieldList())); + && this.getRowType().equalsSansFieldNames(that.getRowType()); } /** - * Compute hash code for RelNode digest + * Compute hash code for RelNode digest. + * + * @see #digestEquals(Object) */ - private int digestHash() { + protected int digestHash() { return Objects.hash(getTraitSet(), getDigestItems()); } 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..21fbd7e 100644 --- a/core/src/main/java/org/apache/calcite/rel/RelNode.java +++ b/core/src/main/java/org/apache/calcite/rel/RelNode.java @@ -307,12 +307,30 @@ public interface RelNode extends RelOptNode, Cloneable { RelNode onRegister(RelOptPlanner planner); /** - * Computes the digest, assigns it, and returns it. For planner use only. + * @return Digest string of this {@code RelNode} + */ + default String getDigest() { + return getRelDigest().toString(); + } + + /** + * Digest of the {@code RelNode}, for planner internal use only. + * + * <p>INTERNAL USE ONLY.</p> + * + * @return Digest of this {@code RelNode} + * @see #getDigest() + */ + @API(since = "1.24", status = API.Status.INTERNAL) + RelDigest getRelDigest(); + + /** + * Recomputes the digest. For planner internal use only. * - * @return Digest of this relational expression + * @see #getDigest() */ @API(since = "1.24", status = API.Status.INTERNAL) - RelDigest recomputeDigest(); + void recomputeDigest(); /** * Replaces the <code>ordinalInParent</code><sup>th</sup> input. You must 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..0efea06 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 @@ -152,4 +153,22 @@ public abstract class Filter extends SingleRel { return super.explainTerms(pw) .item("condition", condition); } + + protected boolean digestEquals0(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + Filter o = (Filter) obj; + return traitSet.equals(o.traitSet) + && input.equals(o.input) + && condition.equals(o.condition) + && getRowType().equalsSansFieldNames(o.getRowType()); + } + + protected int digestHash0() { + return Objects.hash(traitSet, input, 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..b8f6c13 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 @@ -233,6 +233,27 @@ public abstract class Join extends BiRel implements Hintable { !getSystemFieldList().isEmpty()); } + protected boolean digestEquals0(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + Join o = (Join) obj; + return traitSet.equals(o.traitSet) + && getInputs().equals(o.getInputs()) + && condition.equals(o.condition) + && joinType == o.joinType + && hints.equals(o.hints) + && getRowType().equalsSansFieldNames(o.getRowType()); + } + + protected int digestHash0() { + return Objects.hash(traitSet, left, right, + condition, joinType, hints); + } + @Override protected RelDataType deriveRowType() { return SqlValidatorUtil.deriveJoinRowType(left.getRowType(), right.getRowType(), joinType, getCluster().getTypeFactory(), null, 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..99083c0 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; /** @@ -288,6 +289,25 @@ public abstract class Project extends SingleRel implements Hintable { return pw; } + protected boolean digestEquals0(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + Project o = (Project) obj; + return traitSet.equals(o.traitSet) + && input.equals(o.input) + && exps.equals(o.exps) + && hints.equals(o.hints) + && getRowType().equalsSansFieldNames(o.getRowType()); + } + + protected int digestHash0() { + return Objects.hash(traitSet, input, exps, hints); + } + /** * Returns a mapping, or null if this projection is not a mapping. * 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..758d903 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,13 @@ public final class LogicalFilter extends Filter { return super.explainTerms(pw) .itemIf("variablesSet", variablesSet, !variablesSet.isEmpty()); } + + @Override protected boolean digestEquals(Object obj) { + return digestEquals0(obj) + && variablesSet.equals(((LogicalFilter) obj).variablesSet); + } + + @Override protected 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..4a9620b 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 @@ -191,6 +191,19 @@ public final class LogicalJoin extends Join { .itemIf("semiJoinDone", semiJoinDone, semiJoinDone); } + @Override protected boolean digestEquals(Object obj) { + if (this == obj) { + return true; + } + return digestEquals0(obj) + && semiJoinDone == ((LogicalJoin) obj).semiJoinDone + && systemFieldList.equals(((LogicalJoin) obj).systemFieldList); + } + + @Override protected int digestHash() { + return Objects.hash(digestHash0(), semiJoinDone, systemFieldList); + } + @Override public boolean isSemiJoinDone() { return semiJoinDone; } 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..6829c9d 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 protected boolean digestEquals(Object obj) { + return digestEquals0(obj); + } + + @Override protected int digestHash() { + return digestHash0(); + } } diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java index 3e6f474..0e41c0d 100644 --- a/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java +++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java @@ -21,6 +21,8 @@ import org.apache.calcite.sql.SqlIdentifier; import org.apache.calcite.sql.SqlIntervalQualifier; import org.apache.calcite.sql.type.SqlTypeName; +import org.apiguardian.api.API; + import java.nio.charset.Charset; import java.util.List; @@ -241,4 +243,33 @@ public interface RelDataType { */ boolean isDynamicStruct(); + /** + * @return whether the field types are equal with each other by ignoring + * the field names. If it is not a struct, just return the result of + * {@code #equals(Object)}. + */ + @API(since = "1.24", status = API.Status.INTERNAL) + default boolean equalsSansFieldNames(RelDataType that) { + if (this == that) { + return true; + } + if (that == null || getClass() != that.getClass()) { + return false; + } + if (isStruct()) { + List<RelDataTypeField> l1 = this.getFieldList(); + List<RelDataTypeField> l2 = that.getFieldList(); + if (l1.size() != l2.size()) { + return false; + } + for (int i = 0; i < l1.size(); i++) { + if (!l1.get(i).getType().equals(l2.get(i).getType())) { + return false; + } + } + return true; + } else { + return equals(that); + } + } }