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/24 16:10:28 UTC

[calcite] branch master updated: [CALCITE-3786] Make digestEquals and digestHash available to be overridden

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....@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);
+    }
+  }
 }