This is an automated email from the ASF dual-hosted git repository.

yuqi1129 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new 731c873f3f [#13232] fix(api): remove Distribution.equals(Distribution) 
overload trap (#13233)
731c873f3f is described below

commit 731c873f3fd885591c34049c0f78770f780eaaee
Author: YangJie <[email protected]>
AuthorDate: Sun Sep 20 08:17:45 2026 -0400

    [#13232] fix(api): remove Distribution.equals(Distribution) overload trap 
(#13233)
    
    ### What changes were proposed in this pull request?
    
    This removes the `default boolean equals(Distribution)` overload from
    the `Distribution` interface and adds
    `Distributions.isNone(Distribution)`, a structural NONE check that
    preserves the cross-representation (DTO/impl) semantics the overload
    provided at its call sites. `isNone` accepts a `@Nullable` argument and
    treats a null distribution as NONE (a null means no distribution was
    specified). All eleven call sites are migrated (common `DTOConverters`,
    the jdbc-mysql/postgresql/clickhouse/oceanbase/hologres and
    iceberg/delta/hive converters, the hive IT, and the trino
    `HiveMetadataAdapter`); two now-redundant `distribution == null ||`
    guards (`DTOConverters`, `DeltaTableOperations`) are simplified
    accordingly. The javadoc documents that implementations must override
    `equals(Object)`/`hashCode`.
    
    ### Why are the changes needed?
    
    The overload never overrode `Object.equals`, so the same objects
    compared through an `Object` reference (identity) and as `Distribution`
    (structural) could disagree, and implementations relying on the default
    got identity equality in collections.
    
    Fix: #13232
    
    ### Does this PR introduce _any_ user-facing change?
    
    Yes. The public `default boolean equals(Distribution)` overload is
    removed from the `Distribution` interface, so implementers must override
    `equals(Object)`/`hashCode` (all in-repo implementations already do).
    `Distributions.isNone(Distribution)` is added and treats null as NONE.
    `TableOperationDispatcher` already normalizes a null distribution to
    `Distributions.NONE` before it reaches catalogs, so behavior at the
    migrated call sites is unchanged at runtime.
    
    ### How was this patch tested?
    
    Added `TestDistributions`, which pins `Distributions.isNone` across
    DTO-NONE, impl-NONE, HASH, EVEN, and null (null is treated as NONE). The
    overload removal is enforced at compile time; because `isNone` is new,
    this test does not run against the pre-fix tree.
---
 .../expressions/distributions/Distribution.java    | 29 +++-----
 .../expressions/distributions/Distributions.java   | 16 +++++
 .../distributions/TestDistributions.java           | 80 ++++++++++++++++++++++
 .../operations/ClickHouseTableOperations.java      |  2 +-
 .../operation/HologresTableOperations.java         |  2 +-
 .../operation/OceanBaseTableOperations.java        |  2 +-
 .../hive/integration/test/CatalogHive2IT.java      |  4 +-
 .../mysql/operation/MysqlTableOperations.java      |  2 +-
 .../operation/PostgreSqlTableOperations.java       |  2 +-
 .../lakehouse/delta/DeltaTableOperations.java      |  2 +-
 .../iceberg/IcebergCatalogOperations.java          |  2 +-
 .../hive/converter/HiveTableConverter.java         |  2 +-
 .../apache/gravitino/dto/util/DTOConverters.java   |  2 +-
 .../catalog/hive/HiveMetadataAdapter.java          |  2 +-
 14 files changed, 119 insertions(+), 30 deletions(-)

diff --git 
a/api/src/main/java/org/apache/gravitino/rel/expressions/distributions/Distribution.java
 
b/api/src/main/java/org/apache/gravitino/rel/expressions/distributions/Distribution.java
index 564b328fde..e393caf311 100644
--- 
a/api/src/main/java/org/apache/gravitino/rel/expressions/distributions/Distribution.java
+++ 
b/api/src/main/java/org/apache/gravitino/rel/expressions/distributions/Distribution.java
@@ -18,11 +18,20 @@
  */
 package org.apache.gravitino.rel.expressions.distributions;
 
-import java.util.Arrays;
 import org.apache.gravitino.annotation.Evolving;
 import org.apache.gravitino.rel.expressions.Expression;
 
-/** An interface that defines how data is distributed across partitions. */
+/**
+ * An interface that defines how data is distributed across partitions.
+ *
+ * <p>This interface intentionally does not define a {@code boolean 
equals(Distribution)} overload.
+ * Such an overload would not override {@link Object#equals(Object)}, so it 
would be invisible to
+ * {@code HashSet}/{@code HashMap} and to any code comparing through {@code 
Object} references,
+ * which would make structural equality silently dispatch-dependent. 
Implementations must override
+ * {@link Object#equals(Object)} and {@link Object#hashCode()} themselves 
(both {@code
+ * DistributionImpl} and {@code DistributionDTO} do). Use {@link 
Distributions#isNone(Distribution)}
+ * to test for the NONE distribution across representations.
+ */
 @Evolving
 public interface Distribution extends Expression {
 
@@ -46,20 +55,4 @@ public interface Distribution extends Expression {
   default Expression[] children() {
     return expressions();
   }
-
-  /**
-   * Indicates whether some other object is "equal to" this one.
-   *
-   * @param distribution The reference distribution object with which to 
compare.
-   * @return returns true if this object is the same as the obj argument; 
false otherwise.
-   */
-  default boolean equals(Distribution distribution) {
-    if (distribution == null) {
-      return false;
-    }
-
-    return strategy().equals(distribution.strategy())
-        && number() == distribution.number()
-        && Arrays.equals(expressions(), distribution.expressions());
-  }
 }
diff --git 
a/api/src/main/java/org/apache/gravitino/rel/expressions/distributions/Distributions.java
 
b/api/src/main/java/org/apache/gravitino/rel/expressions/distributions/Distributions.java
index c7aeb62189..555d1bd30b 100644
--- 
a/api/src/main/java/org/apache/gravitino/rel/expressions/distributions/Distributions.java
+++ 
b/api/src/main/java/org/apache/gravitino/rel/expressions/distributions/Distributions.java
@@ -20,6 +20,7 @@ package org.apache.gravitino.rel.expressions.distributions;
 
 import java.util.Arrays;
 import java.util.Objects;
+import javax.annotation.Nullable;
 import org.apache.gravitino.rel.expressions.Expression;
 import org.apache.gravitino.rel.expressions.NamedReference;
 
@@ -36,6 +37,21 @@ public class Distributions {
   public static final Distribution NONE =
       new DistributionImpl(Strategy.NONE, 0, Expression.EMPTY_EXPRESSION);
 
+  /**
+   * Returns true if the distribution is the NONE distribution. A null 
distribution means "no
+   * distribution specified" and is therefore treated as NONE. For non-null 
values the comparison is
+   * structural, so both the built-in implementation and DTO representations 
of NONE match.
+   *
+   * @param distribution The distribution to check; may be null.
+   * @return true if the distribution is null or represents the NONE 
distribution.
+   */
+  public static boolean isNone(@Nullable Distribution distribution) {
+    return distribution == null
+        || (distribution.strategy() == Strategy.NONE
+            && distribution.number() == 0
+            && Arrays.equals(distribution.expressions(), 
Expression.EMPTY_EXPRESSION));
+  }
+
   /** List bucketing strategy hash, TODO: #1505 Separate the bucket number 
from the Distribution. */
   public static final Distribution HASH =
       new DistributionImpl(Strategy.HASH, 0, Expression.EMPTY_EXPRESSION);
diff --git 
a/api/src/test/java/org/apache/gravitino/rel/expressions/distributions/TestDistributions.java
 
b/api/src/test/java/org/apache/gravitino/rel/expressions/distributions/TestDistributions.java
new file mode 100644
index 0000000000..9763957c3e
--- /dev/null
+++ 
b/api/src/test/java/org/apache/gravitino/rel/expressions/distributions/TestDistributions.java
@@ -0,0 +1,80 @@
+/*
+ * 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.gravitino.rel.expressions.distributions;
+
+import org.apache.gravitino.rel.expressions.Expression;
+import org.apache.gravitino.rel.expressions.NamedReference;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestDistributions {
+
+  // A separate Distribution implementation, used to prove 
Distributions.isNone recognizes NONE
+  // structurally across representations rather than by concrete type. 
DistributionDTO.NONE would be
+  // the natural other representation, but DistributionDTO lives in the common 
module and the api
+  // module does not depend on common (main or test), so it is not on this 
test classpath. An
+  // anonymous implementation stands in for that cross-representation check.
+  private static Distribution distributionOf(Strategy strategy, int number, 
Expression... exprs) {
+    return new Distribution() {
+      @Override
+      public Strategy strategy() {
+        return strategy;
+      }
+
+      @Override
+      public int number() {
+        return number;
+      }
+
+      @Override
+      public Expression[] expressions() {
+        return exprs;
+      }
+    };
+  }
+
+  @Test
+  public void testIsNone() {
+    Assertions.assertTrue(Distributions.isNone(Distributions.NONE));
+
+    // A structurally equal NONE distribution from a different implementation 
also matches.
+    Assertions.assertTrue(
+        Distributions.isNone(distributionOf(Strategy.NONE, 0, 
Expression.EMPTY_EXPRESSION)));
+
+    // A null distribution means "no distribution specified", which is treated 
as NONE.
+    Assertions.assertTrue(Distributions.isNone(null));
+    Assertions.assertFalse(Distributions.isNone(Distributions.HASH));
+    Assertions.assertFalse(Distributions.isNone(Distributions.RANGE));
+    Assertions.assertFalse(
+        Distributions.isNone(Distributions.even(10, 
NamedReference.field("col"))));
+    Assertions.assertFalse(
+        Distributions.isNone(distributionOf(Strategy.NONE, 5, 
Expression.EMPTY_EXPRESSION)));
+  }
+
+  @Test
+  public void testImplEqualityIsStructuralWithinSameClass() {
+    Assertions.assertEquals(Distributions.NONE, Distributions.NONE);
+    Assertions.assertNotEquals(
+        Distributions.even(10, NamedReference.field("col")),
+        Distributions.even(10, NamedReference.field("other")));
+    Assertions.assertEquals(
+        Distributions.even(10, NamedReference.field("col")),
+        Distributions.even(10, NamedReference.field("col")));
+  }
+}
diff --git 
a/catalogs-contrib/catalog-jdbc-clickhouse/src/main/java/org/apache/gravitino/catalog/clickhouse/operations/ClickHouseTableOperations.java
 
b/catalogs-contrib/catalog-jdbc-clickhouse/src/main/java/org/apache/gravitino/catalog/clickhouse/operations/ClickHouseTableOperations.java
index 148172cefe..55f4ad5d59 100644
--- 
a/catalogs-contrib/catalog-jdbc-clickhouse/src/main/java/org/apache/gravitino/catalog/clickhouse/operations/ClickHouseTableOperations.java
+++ 
b/catalogs-contrib/catalog-jdbc-clickhouse/src/main/java/org/apache/gravitino/catalog/clickhouse/operations/ClickHouseTableOperations.java
@@ -333,7 +333,7 @@ public class ClickHouseTableOperations extends 
JdbcTableOperations {
       SortOrder[] sortOrders) {
 
     Preconditions.checkArgument(
-        Distributions.NONE.equals(distribution), "ClickHouse does not support 
distribution");
+        Distributions.isNone(distribution), "ClickHouse does not support 
distribution");
 
     StringBuilder sqlBuilder = new StringBuilder();
 
diff --git 
a/catalogs-contrib/catalog-jdbc-hologres/src/main/java/org/apache/gravitino/catalog/hologres/operation/HologresTableOperations.java
 
b/catalogs-contrib/catalog-jdbc-hologres/src/main/java/org/apache/gravitino/catalog/hologres/operation/HologresTableOperations.java
index c78037d291..1ace95f3b1 100644
--- 
a/catalogs-contrib/catalog-jdbc-hologres/src/main/java/org/apache/gravitino/catalog/hologres/operation/HologresTableOperations.java
+++ 
b/catalogs-contrib/catalog-jdbc-hologres/src/main/java/org/apache/gravitino/catalog/hologres/operation/HologresTableOperations.java
@@ -227,7 +227,7 @@ public class HologresTableOperations extends 
JdbcTableOperations
     List<String> withEntries = new ArrayList<>();
 
     // Add distribution_key from Distribution parameter
-    if (!Distributions.NONE.equals(distribution)) {
+    if (!Distributions.isNone(distribution)) {
       validateDistribution(distribution);
       String distributionColumns =
           Arrays.stream(distribution.expressions())
diff --git 
a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/operation/OceanBaseTableOperations.java
 
b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/operation/OceanBaseTableOperations.java
index aa9a49cf53..bd09770adc 100644
--- 
a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/operation/OceanBaseTableOperations.java
+++ 
b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/operation/OceanBaseTableOperations.java
@@ -77,7 +77,7 @@ public class OceanBaseTableOperations extends 
JdbcTableOperations {
           "Currently we do not support Partitioning in oceanbase");
     }
 
-    if (!Distributions.NONE.equals(distribution)) {
+    if (!Distributions.isNone(distribution)) {
       throw new UnsupportedOperationException("OceanBase does not support 
distribution");
     }
 
diff --git 
a/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/CatalogHive2IT.java
 
b/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/CatalogHive2IT.java
index 6ec869c7f9..8b3d9a9158 100644
--- 
a/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/CatalogHive2IT.java
+++ 
b/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/CatalogHive2IT.java
@@ -453,8 +453,8 @@ public class CatalogHive2IT extends BaseIT {
   }
 
   private void compareDistributions(Distribution expected, Distribution 
actual) {
-    boolean expectedEmpty = expected == null || 
Distributions.NONE.equals(expected);
-    boolean actualEmpty = actual == null || Distributions.NONE.equals(actual);
+    boolean expectedEmpty = expected == null || Distributions.isNone(expected);
+    boolean actualEmpty = actual == null || Distributions.isNone(actual);
     Assertions.assertEquals(expectedEmpty, actualEmpty);
     if (expectedEmpty) {
       return;
diff --git 
a/catalogs/catalog-jdbc-mysql/src/main/java/org/apache/gravitino/catalog/mysql/operation/MysqlTableOperations.java
 
b/catalogs/catalog-jdbc-mysql/src/main/java/org/apache/gravitino/catalog/mysql/operation/MysqlTableOperations.java
index 813f44b6de..0417d05d08 100644
--- 
a/catalogs/catalog-jdbc-mysql/src/main/java/org/apache/gravitino/catalog/mysql/operation/MysqlTableOperations.java
+++ 
b/catalogs/catalog-jdbc-mysql/src/main/java/org/apache/gravitino/catalog/mysql/operation/MysqlTableOperations.java
@@ -90,7 +90,7 @@ public class MysqlTableOperations extends JdbcTableOperations 
{
     }
 
     Preconditions.checkArgument(
-        Distributions.NONE.equals(distribution), "MySQL does not support 
distribution");
+        Distributions.isNone(distribution), "MySQL does not support 
distribution");
 
     validateIncrementCol(columns, indexes);
     StringBuilder sqlBuilder = new StringBuilder();
diff --git 
a/catalogs/catalog-jdbc-postgresql/src/main/java/org/apache/gravitino/catalog/postgresql/operation/PostgreSqlTableOperations.java
 
b/catalogs/catalog-jdbc-postgresql/src/main/java/org/apache/gravitino/catalog/postgresql/operation/PostgreSqlTableOperations.java
index 9ce61a0281..bf6d7e99ee 100644
--- 
a/catalogs/catalog-jdbc-postgresql/src/main/java/org/apache/gravitino/catalog/postgresql/operation/PostgreSqlTableOperations.java
+++ 
b/catalogs/catalog-jdbc-postgresql/src/main/java/org/apache/gravitino/catalog/postgresql/operation/PostgreSqlTableOperations.java
@@ -165,7 +165,7 @@ public class PostgreSqlTableOperations extends 
JdbcTableOperations
           "Currently we do not support Partitioning in PostgreSQL");
     }
     Preconditions.checkArgument(
-        Distributions.NONE.equals(distribution), "PostgreSQL does not support 
distribution");
+        Distributions.isNone(distribution), "PostgreSQL does not support 
distribution");
 
     StringBuilder sqlBuilder = new StringBuilder();
     sqlBuilder
diff --git 
a/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/delta/DeltaTableOperations.java
 
b/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/delta/DeltaTableOperations.java
index f2b0f2ec55..2991a56437 100644
--- 
a/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/delta/DeltaTableOperations.java
+++ 
b/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/delta/DeltaTableOperations.java
@@ -198,7 +198,7 @@ public class DeltaTableOperations extends 
ManagedTableOperations {
     }
 
     Preconditions.checkArgument(
-        distribution == null || distribution.equals(Distributions.NONE),
+        Distributions.isNone(distribution),
         "Delta table doesn't support specifying distribution in CREATE TABLE. "
             + "Distribution is not applicable for external Delta tables.");
 
diff --git 
a/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogOperations.java
 
b/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogOperations.java
index 140db99ff0..79eec2fd8d 100644
--- 
a/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogOperations.java
+++ 
b/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogOperations.java
@@ -609,7 +609,7 @@ public class IcebergCatalogOperations
 
       // Gravitino NONE distribution means the client side doesn't specify 
distribution, which is
       // not the same as none distribution in Iceberg.
-      if (Distributions.NONE.equals(distribution)) {
+      if (Distributions.isNone(distribution)) {
         distribution =
             getIcebergDefaultDistribution(sortOrders.length > 0, 
partitioning.length > 0);
       }
diff --git 
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/converter/HiveTableConverter.java
 
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/converter/HiveTableConverter.java
index 27100a6026..efe8f3bc58 100644
--- 
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/converter/HiveTableConverter.java
+++ 
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/converter/HiveTableConverter.java
@@ -240,7 +240,7 @@ public class HiveTableConverter {
       }
     }
 
-    if (table.distribution() != null && 
!Distributions.NONE.equals(table.distribution())) {
+    if (table.distribution() != null && 
!Distributions.isNone(table.distribution())) {
       strgDesc.setBucketCols(
           Arrays.stream(table.distribution().expressions())
               .map(t -> ((NamedReference.FieldReference) t).fieldName()[0])
diff --git 
a/common/src/main/java/org/apache/gravitino/dto/util/DTOConverters.java 
b/common/src/main/java/org/apache/gravitino/dto/util/DTOConverters.java
index 717d955681..c9d36b951e 100644
--- a/common/src/main/java/org/apache/gravitino/dto/util/DTOConverters.java
+++ b/common/src/main/java/org/apache/gravitino/dto/util/DTOConverters.java
@@ -368,7 +368,7 @@ public class DTOConverters {
    * @return The distribution DTO.
    */
   public static DistributionDTO toDTO(Distribution distribution) {
-    if (Distributions.NONE.equals(distribution) || null == distribution) {
+    if (Distributions.isNone(distribution)) {
       return DistributionDTO.NONE;
     }
 
diff --git 
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/hive/HiveMetadataAdapter.java
 
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/hive/HiveMetadataAdapter.java
index d997d9f4f1..6925829bd4 100644
--- 
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/hive/HiveMetadataAdapter.java
+++ 
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/hive/HiveMetadataAdapter.java
@@ -217,7 +217,7 @@ public class HiveMetadataAdapter extends 
CatalogConnectorMetadataAdapter {
     }
 
     if (gravitinoTable.getDistribution() != null
-        && !Distributions.NONE.equals(gravitinoTable.getDistribution())) {
+        && !Distributions.isNone(gravitinoTable.getDistribution())) {
       properties.put(
           HivePropertyMeta.HIVE_BUCKET_KEY,
           Arrays.stream(gravitinoTable.getDistribution().expressions())

Reply via email to