This is an automated email from the ASF dual-hosted git repository.
github-bot pushed a commit to branch site
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/site by this push:
new 0b4f4c2513 [CALCITE-7393] Support RelDataTypeDigest
0b4f4c2513 is described below
commit 0b4f4c2513b72f2c029af7ba21eca745eca8264a
Author: wenzhuang.zwz <[email protected]>
AuthorDate: Thu Jan 22 18:40:11 2026 +0800
[CALCITE-7393] Support RelDataTypeDigest
Use structured innerDigest instead of string digest for composite/UDT types
to reduce memory and improve hashCode/equals latency. Controlled by
`calcite.disable.generate.rel.data.type.digest.string` (default: false).
Legacy string digest is still used in hashCode/equals if explicitly set,
ensuring backward compatibility.
TestCase: TypeDigestBenchmark
---
.../calcite/config/CalciteSystemProperty.java | 8 ++
.../org/apache/calcite/jdbc/JavaRecordType.java | 12 ++
.../org/apache/calcite/rel/HasDigestString.java | 24 ++++
.../org/apache/calcite/rel/type/RelDataType.java | 22 ++++
.../apache/calcite/rel/type/RelDataTypeDigest.java | 26 ++++
.../apache/calcite/rel/type/RelDataTypeImpl.java | 138 ++++++++++++++++++---
.../org/apache/calcite/rel/type/RelRecordType.java | 34 +++++
.../rel/type/SingleColumnAliasRelDataType.java | 12 ++
.../org/apache/calcite/sql/type/ArraySqlType.java | 19 +++
.../org/apache/calcite/sql/type/MapSqlType.java | 21 ++++
.../apache/calcite/sql/type/MultisetSqlType.java | 19 +++
.../org/apache/calcite/rex/RexBuilderTest.java | 1 +
site/_docs/history.md | 4 +
.../calcite/benchmarks/TypeDigestBenchmark.java | 126 +++++++++++++++++++
14 files changed, 448 insertions(+), 18 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/config/CalciteSystemProperty.java
b/core/src/main/java/org/apache/calcite/config/CalciteSystemProperty.java
index e5ea8c2ea2..70db4505d6 100644
--- a/core/src/main/java/org/apache/calcite/config/CalciteSystemProperty.java
+++ b/core/src/main/java/org/apache/calcite/config/CalciteSystemProperty.java
@@ -138,6 +138,14 @@ public final class CalciteSystemProperty<T> {
public static final CalciteSystemProperty<Boolean> TOPDOWN_OPT =
booleanProperty("calcite.planner.topdown.opt", false);
+
+ /** Whether to disable generate rel data type digest string.
+ *
+ * <p> Disable generate rel data type digest string for every type can
+ * reduce composite type's digest memory and digest relative operation's
latency. */
+ public static final CalciteSystemProperty<Boolean>
DISABLE_GENERATE_REL_DATA_TYPE_DIGEST_STRING =
+ booleanProperty("calcite.disable.generate.rel.data.type.digest.string",
false);
+
/**
* Whether to run integration tests.
*/
diff --git a/core/src/main/java/org/apache/calcite/jdbc/JavaRecordType.java
b/core/src/main/java/org/apache/calcite/jdbc/JavaRecordType.java
index 72242d3934..2183c2ac28 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/JavaRecordType.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/JavaRecordType.java
@@ -51,4 +51,16 @@ public JavaRecordType(List<RelDataTypeField> fields, Class
clazz) {
@Override public int hashCode() {
return Objects.hash(fieldList, clazz);
}
+
+ @Override public boolean deepEquals(@Nullable Object obj) {
+ return this == obj
+ || obj instanceof JavaRecordType
+ && Objects.equals(fieldList, ((JavaRecordType) obj).fieldList)
+ && clazz == ((JavaRecordType) obj).clazz
+ && this.isNullable() == ((JavaRecordType) obj).isNullable();
+ }
+
+ @Override public int deepHashCode() {
+ return Objects.hash(fieldList, this.isNullable(), clazz);
+ }
}
diff --git a/core/src/main/java/org/apache/calcite/rel/HasDigestString.java
b/core/src/main/java/org/apache/calcite/rel/HasDigestString.java
new file mode 100644
index 0000000000..5058c38769
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/HasDigestString.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel;
+
+/**
+ * Interface for objects that have a digest string.
+ */
+public interface HasDigestString {
+ String getDigestString();
+}
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 4188d04698..071a1152a1 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
@@ -322,4 +322,26 @@ default boolean
equalsSansFieldNamesAndNullability(@Nullable RelDataType that) {
default boolean isMeasure() {
return getSqlTypeName() == SqlTypeName.MEASURE;
}
+
+ /**
+ * Returns the digest of this type.
+ *
+ * @return digest of this type
+ */
+ RelDataTypeDigest getDigest();
+
+ /**
+ * Deep equality check for RelDataType digest.
+ *
+ * @return Whether the 2 RelDataTypes are equivalent or have the same digest.
+ * @see #deepHashCode()
+ */
+ boolean deepEquals(@Nullable Object obj);
+
+ /**
+ * Compute deep hash code for RelDataType digest.
+ *
+ * @see #deepEquals(Object)
+ */
+ int deepHashCode();
}
diff --git
a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeDigest.java
b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeDigest.java
new file mode 100644
index 0000000000..ac805aae22
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeDigest.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel.type;
+
+import org.apache.calcite.rel.HasDigestString;
+
+/**
+ * Digest of a RelDataType.
+ */
+public interface RelDataTypeDigest extends HasDigestString {
+ RelDataType getType();
+}
diff --git
a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeImpl.java
b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeImpl.java
index 67ab341016..d896ece7e9 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeImpl.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeImpl.java
@@ -16,6 +16,7 @@
*/
package org.apache.calcite.rel.type;
+import org.apache.calcite.config.CalciteSystemProperty;
import org.apache.calcite.sql.SqlCollation;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlIntervalQualifier;
@@ -46,8 +47,8 @@
* RelDataTypeImpl is an abstract base for implementations of
* {@link RelDataType}.
*
- * <p>Identity is based upon the {@link #digest} field, which each derived
class
- * should set during construction.
+ * <p>Identity is based upon the {@link #digest} or {@link #innerDigest} field,
+ * which each derived class should set {@link #digest} or {@link #innerDigest}
during construction.
*/
public abstract class RelDataTypeImpl
implements RelDataType, RelDataTypeFamily {
@@ -60,7 +61,14 @@ public abstract class RelDataTypeImpl
//~ Instance fields --------------------------------------------------------
protected final @Nullable List<RelDataTypeField> fieldList;
- protected @Nullable String digest;
+
+ /**
+ * Use {@link #innerDigest} instead.
+ *
+ * @deprecated See {@link
CalciteSystemProperty#DISABLE_GENERATE_REL_DATA_TYPE_DIGEST_STRING}.
+ */
+ protected @Deprecated @Nullable String digest;
+ protected @Nullable RelDataTypeDigest innerDigest;
//~ Constructors -----------------------------------------------------------
@@ -232,18 +240,50 @@ private static void getFieldRecurse(List<Slot> slots,
RelDataType type,
return fieldList != null;
}
+ /**
+ * Gets the {@link RelDataTypeDigest} of this type.
+ * If a user has set the legacy string {@code digest} and {@code
innerDigest} has not
+ * been initialized yet, this method computes and initializes it.
+ */
+ @Override public RelDataTypeDigest getDigest() {
+ if (digest != null && innerDigest == null) {
+ innerDigest = new InnerRelDataTypeDigest();
+ }
+ return requireNonNull(innerDigest, "innerDigest");
+ }
+
@Override public boolean equals(@Nullable Object obj) {
- return this == obj
- || obj instanceof RelDataTypeImpl
- && Objects.equals(this.digest, ((RelDataTypeImpl) obj).digest);
+ if (obj == this) {
+ return true;
+ }
+ if (obj instanceof RelDataTypeImpl) {
+ final RelDataTypeImpl that = (RelDataTypeImpl) obj;
+ return this.getDigest().equals(that.getDigest());
+ }
+ return false;
}
@Override public int hashCode() {
- return Objects.hashCode(digest);
+ return getDigest().hashCode();
+ }
+
+ @Override public boolean deepEquals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || this.getClass() != obj.getClass()) {
+ return false;
+ }
+ return Objects.equals(this.getDigest().getDigestString(),
+ ((RelDataTypeImpl) obj).getDigest().getDigestString());
+ }
+
+ @Override public int deepHashCode() {
+ return Objects.hashCode(this.getDigest().getDigestString());
}
@Override public String getFullTypeString() {
- return requireNonNull(digest, "digest");
+ return requireNonNull(this.getDigest().getDigestString(), "digest");
}
@Override public boolean isNullable() {
@@ -309,23 +349,85 @@ protected abstract void generateTypeString(
boolean withDetail);
/**
- * Computes the digest field. This should be called in every non-abstract
- * subclass constructor once the type is fully defined.
+ * Init the lazy digest computing field {@link #innerDigest}.
+ * This should be called in every non-abstract subclass
+ * constructor once the type is fully defined.
*/
@SuppressWarnings("method.invocation.invalid")
protected void computeDigest(@UnknownInitialization RelDataTypeImpl this) {
- StringBuilder sb = new StringBuilder();
- generateTypeString(sb, true);
- if (!isNullable()) {
- sb.append(NON_NULLABLE_SUFFIX);
+ digest = null;
+ innerDigest = new InnerRelDataTypeDigest();
+ if
(!CalciteSystemProperty.DISABLE_GENERATE_REL_DATA_TYPE_DIGEST_STRING.value()) {
+ digest = this.getDigest().getDigestString();
}
- digest = sb.toString();
}
@Override public String toString() {
- StringBuilder sb = new StringBuilder();
- generateTypeString(sb, false);
- return sb.toString();
+ return getDigest().toString();
+ }
+
+ /** Implementation of {@link RelDataTypeDigest}. */
+ private class InnerRelDataTypeDigest implements RelDataTypeDigest {
+ /** Cached hash code. */
+ private int hash = 0;
+ /** Cached type string. */
+ private @Nullable String digestWithDetail = null; // NOTE: shorter detail
will be better
+ private @Nullable String digestWithoutDetail = null;
+
+ @Override public RelDataType getType() {
+ return RelDataTypeImpl.this;
+ }
+
+ @Override public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final RelDataTypeImpl.InnerRelDataTypeDigest otherDigest =
+ (RelDataTypeImpl.InnerRelDataTypeDigest) o;
+ if (digest != null) {
+ return digest.equals(otherDigest.getDigestString());
+ }
+ return deepEquals(otherDigest.getType());
+ }
+
+ @Override public int hashCode() {
+ if (digest != null) {
+ return Objects.hashCode(digest);
+ }
+ if (hash == 0) {
+ hash = deepHashCode();
+ }
+ return hash;
+ }
+
+ @Override public String getDigestString() {
+ // return user defined digest by set legacy digest string field.
+ if (digest != null) {
+ return digest;
+ }
+
+ if (digestWithDetail == null) {
+ StringBuilder sb = new StringBuilder();
+ generateTypeString(sb, true);
+ if (!isNullable()) {
+ sb.append(NON_NULLABLE_SUFFIX);
+ }
+ digestWithDetail = sb.toString();
+ }
+ return digestWithDetail;
+ }
+
+ @Override public String toString() {
+ if (digestWithoutDetail == null || digest != null) {
+ StringBuilder sb = new StringBuilder();
+ RelDataTypeImpl.this.generateTypeString(sb, false);
+ digestWithoutDetail = sb.toString();
+ }
+ return digestWithoutDetail;
+ }
}
@Override public RelDataTypePrecedenceList getPrecedenceList() {
diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelRecordType.java
b/core/src/main/java/org/apache/calcite/rel/type/RelRecordType.java
index 4f253bf225..a63b107ca0 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelRecordType.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelRecordType.java
@@ -27,6 +27,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import static java.util.Objects.requireNonNull;
@@ -146,6 +147,39 @@ public RelRecordType(List<RelDataTypeField> fields) {
sb.append(")");
}
+ @Override public boolean deepEquals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || this.getClass() != obj.getClass()) {
+ return false;
+ }
+
+ RelRecordType that = (RelRecordType) obj;
+ if (kind != that.kind || nullable != that.nullable) {
+ return false;
+ }
+
+ if (fieldList == null || that.fieldList == null) {
+ return fieldList == null && that.fieldList == null;
+ }
+
+ if (fieldList.size() != that.fieldList.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < fieldList.size(); i++) {
+ if (!fieldList.get(i).equals(that.fieldList.get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override public int deepHashCode() {
+ return Objects.hash(kind.ordinal(), nullable, fieldList);
+ }
+
/**
* Per {@link Serializable} API, provides a replacement object to be written
* during serialization.
diff --git
a/core/src/main/java/org/apache/calcite/rel/type/SingleColumnAliasRelDataType.java
b/core/src/main/java/org/apache/calcite/rel/type/SingleColumnAliasRelDataType.java
index b09196b577..28e0406f67 100644
---
a/core/src/main/java/org/apache/calcite/rel/type/SingleColumnAliasRelDataType.java
+++
b/core/src/main/java/org/apache/calcite/rel/type/SingleColumnAliasRelDataType.java
@@ -136,4 +136,16 @@ public SingleColumnAliasRelDataType(RelDataType original,
RelDataType alias) {
@Override public boolean isDynamicStruct() {
return original.isDynamicStruct();
}
+
+ @Override public RelDataTypeDigest getDigest() {
+ return original.getDigest();
+ }
+
+ @Override public boolean deepEquals(@Nullable Object obj) {
+ return original.deepEquals(obj);
+ }
+
+ @Override public int deepHashCode() {
+ return original.deepHashCode();
+ }
}
diff --git a/core/src/main/java/org/apache/calcite/sql/type/ArraySqlType.java
b/core/src/main/java/org/apache/calcite/sql/type/ArraySqlType.java
index 1124939c7b..7d1d1afc47 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/ArraySqlType.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/ArraySqlType.java
@@ -20,6 +20,10 @@
import org.apache.calcite.rel.type.RelDataTypeFamily;
import org.apache.calcite.rel.type.RelDataTypePrecedenceList;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.util.Objects;
+
import static
org.apache.calcite.sql.type.NonNullableAccessors.getComponentTypeOrThrow;
import static java.util.Objects.requireNonNull;
@@ -56,6 +60,21 @@ public ArraySqlType(RelDataType elementType, boolean
isNullable) {
sb.append(" ARRAY");
}
+ @Override public boolean deepEquals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || this.getClass() != obj.getClass()) {
+ return false;
+ }
+ ArraySqlType that = (ArraySqlType) obj;
+ return this.isNullable() == that.isNullable() &&
elementType.equals(that.elementType);
+ }
+
+ @Override public int deepHashCode() {
+ return Objects.hash(SqlTypeName.ARRAY.ordinal(), isNullable,
elementType.hashCode());
+ }
+
// implement RelDataType
@Override public RelDataType getComponentType() {
return elementType;
diff --git a/core/src/main/java/org/apache/calcite/sql/type/MapSqlType.java
b/core/src/main/java/org/apache/calcite/sql/type/MapSqlType.java
index 960fec7ac4..9ad2160575 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/MapSqlType.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/MapSqlType.java
@@ -19,6 +19,10 @@
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFamily;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.util.Objects;
+
import static java.util.Objects.requireNonNull;
/**
@@ -69,6 +73,23 @@ public MapSqlType(
.append(") MAP");
}
+ @Override public boolean deepEquals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || this.getClass() != obj.getClass()) {
+ return false;
+ }
+ MapSqlType that = (MapSqlType) obj;
+ return this.isNullable() == that.isNullable() &&
keyType.equals(that.keyType)
+ && valueType.equals(that.valueType);
+ }
+
+ @Override public int deepHashCode() {
+ return Objects.hash(SqlTypeName.MAP.ordinal(), this.isNullable,
keyType.hashCode(),
+ valueType.hashCode());
+ }
+
// implement RelDataType
@Override public RelDataTypeFamily getFamily() {
return this;
diff --git
a/core/src/main/java/org/apache/calcite/sql/type/MultisetSqlType.java
b/core/src/main/java/org/apache/calcite/sql/type/MultisetSqlType.java
index b12c836d89..cbea4062c2 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/MultisetSqlType.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/MultisetSqlType.java
@@ -20,6 +20,10 @@
import org.apache.calcite.rel.type.RelDataTypeFamily;
import org.apache.calcite.rel.type.RelDataTypePrecedenceList;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.util.Objects;
+
import static
org.apache.calcite.sql.type.NonNullableAccessors.getComponentTypeOrThrow;
import static java.util.Objects.requireNonNull;
@@ -56,6 +60,21 @@ public MultisetSqlType(RelDataType elementType, boolean
isNullable) {
sb.append(" MULTISET");
}
+ @Override public boolean deepEquals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || this.getClass() != obj.getClass()) {
+ return false;
+ }
+ MultisetSqlType that = (MultisetSqlType) obj;
+ return this.isNullable() == that.isNullable() &&
elementType.equals(that.elementType);
+ }
+
+ @Override public int deepHashCode() {
+ return Objects.hash(SqlTypeName.MULTISET.ordinal(), this.isNullable,
elementType.hashCode());
+ }
+
// implement RelDataType
@Override public RelDataType getComponentType() {
return elementType;
diff --git a/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java
b/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java
index 4b714e8632..2689e5ced3 100644
--- a/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java
@@ -1473,6 +1473,7 @@ private void checkBigDecimalLiteral(RexBuilder builder,
String val) {
/** Emulate a user defined type. */
private static class UDT extends RelDataTypeImpl {
+ @SuppressWarnings("deprecation")
UDT() {
this.digest = "(udt)NOT NULL";
}
diff --git a/site/_docs/history.md b/site/_docs/history.md
index 89d91bd266..dba2c98c34 100644
--- a/site/_docs/history.md
+++ b/site/_docs/history.md
@@ -49,6 +49,10 @@ ## <a
href="https://github.com/apache/calcite/releases/tag/calcite-1.42.0">1.42.
#### Breaking Changes
{: #breaking-1-42-0}
+* [<a
href="https://issues.apache.org/jira/browse/CALCITE-7393">CALCITE-7393</a>]
+`RelDataTypeImpl.digest` is deprecated. We recommend using
`RelDataTypeImpl.innerDigest` instead.
+See system property
`CalciteSystemProperty.DISABLE_GENERATE_REL_DATA_TYPE_DIGEST_STRING`.
+
* [<a
href="https://issues.apache.org/jira/browse/CALCITE-7301">CALCITE-7301</a>]
Prior to this change, most `SqlNode`s in the `org.apache.calcite.sql.ddl`
package could not be unparsed
when created with `SqlOperator#createCall`. To fix this, those `SqlNode`s now
implement their own `SqlOperator`.
diff --git
a/ubenchmark/src/jmh/java/org/apache/calcite/benchmarks/TypeDigestBenchmark.java
b/ubenchmark/src/jmh/java/org/apache/calcite/benchmarks/TypeDigestBenchmark.java
new file mode 100644
index 0000000000..a02a6e836a
--- /dev/null
+++
b/ubenchmark/src/jmh/java/org/apache/calcite/benchmarks/TypeDigestBenchmark.java
@@ -0,0 +1,126 @@
+/*
+ * 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.benchmarks;
+
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.sql.SqlCollation;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.tools.Frameworks;
+import org.apache.calcite.tools.RelBuilder;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Threads;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Benchmark for {@link RelDataType} digest generation and comparison.
+ */
+@Fork(value = 1, jvmArgsPrepend =
"-Dcalcite.disable.generate.type.digest.string=true")
+@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
+@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@State(Scope.Thread)
+@Threads(1)
+public class TypeDigestBenchmark {
+
+ @Param({"1", "50", "500", "5000", "50000"})
+ int topN;
+
+ RelDataType type;
+ RelDataType type2;
+
+ @Setup(Level.Trial)
+ public void setup() {
+ type = createType(topN);
+ type2 = createType(topN);
+ }
+
+ private RelDataType createType(int n) {
+ RelBuilder builder =
+ RelBuilder.create(Frameworks.newConfigBuilder()
+ .defaultSchema(Frameworks.createRootSchema(true))
+ .build());
+ final RelDataTypeFactory typeFactory = builder.getTypeFactory();
+
+ RelDataType varchar =
+ typeFactory.createTypeWithCharsetAndCollation(typeFactory
+ .createSqlType(SqlTypeName.VARCHAR, 100),
+ StandardCharsets.UTF_8, SqlCollation.IMPLICIT);
+
+ RelDataType leafObj = typeFactory.builder().add("k", varchar).add("v",
varchar)
+ .add("attrs", typeFactory.createMapType(varchar, varchar))
+ .add("tags", typeFactory.createArrayType(varchar, -1)).build();
+
+ final RelDataTypeFactory.Builder root = typeFactory.builder();
+ for (int i = 0; i < n; i++) {
+ int depth = 1 + (i % 8);
+ RelDataType t = leafObj;
+
+ for (int d = 0; d < depth; d++) {
+ RelDataType arrObj = typeFactory.createArrayType(t, -1);
+ RelDataType mapObj = typeFactory.createMapType(varchar, t);
+
+ t =
+ typeFactory.builder().add("lvl" + d, t).add("arr" + d,
arrObj).add("map" + d, mapObj)
+ .add("s" + d, varchar).build();
+ }
+
+ if ((i % 11) == 0) {
+ root.add("f" + i, typeFactory.createArrayType(t, -1));
+ } else if ((i % 11) == 1) {
+ root.add("f" + i, typeFactory.createMapType(varchar, t));
+ } else {
+ root.add("f" + i, t);
+ }
+ }
+
+ return root.build();
+ }
+
+ @Benchmark
+ public boolean testEquals() {
+ return type.hashCode() == type2.hashCode() && type.equals(type2);
+ }
+
+ public static void main(String[] args) throws RunnerException {
+ Options opt = new OptionsBuilder()
+ .include(TypeDigestBenchmark.class.getSimpleName())
+ .detectJvmArgs()
+ .build();
+
+ new Runner(opt).run();
+ }
+}