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 4b529ce4da [#12734] fix(flink-connector): skip no-op table alter to 
avoid updates must not be empty (#12735)
4b529ce4da is described below

commit 4b529ce4da8c73253488a946b26c8029f9be992e
Author: geyanggang <[email protected]>
AuthorDate: Mon Aug 31 20:20:40 2026 +0800

    [#12734] fix(flink-connector): skip no-op table alter to avoid updates must 
not be empty (#12735)
    
    ### What changes were proposed in this pull request?
    
    Skip forwarding an empty TableChange list to Gravitino when altering a
    table through the Flink connector. This covers all three code paths that
    could produce an empty change set:
    
    GravitinoHiveCatalog.applyGenericTableAlter (generic tables with no
    property/comment diff)
    BaseCatalog.alterTable(tablePath, newTable, ignoreIfNotExists)
    (comment-only alter with unchanged comment)
    BaseCatalog.alterTable(tablePath, newTable, tableChanges,
    ignoreIfNotExists) (empty Flink tableChanges)
    A new private helper BaseCatalog.alterGravitinoTable centralizes the
    empty-change guard for the native paths and only invalidates the native
    cache when an alter is actually performed.
    
    ### Why are the changes needed?
    
    When an ALTER TABLE results in no effective change, the connector
    produced an empty update list and called TableCatalog.alterTable.
    Server-side TableUpdatesRequest.validate() rejects empty updates with
    IllegalArgumentException: updates must not be empty, failing an
    effectively no-op alter (for example an idempotent ALTER TABLE on job
    startup).
    
    Fix: #12734
    
    ### Does this PR introduce _any_ user-facing change?
    
    No new APIs or property keys. Behavior change: an ALTER TABLE that
    results in no actual change now succeeds as a no-op instead of failing.
    
    ### How was this patch tested?
    
    Added unit tests:
    
    TestGravitinoHiveCatalog: no-op generic alter is skipped; property
    change is forwarded.
    TestBaseCatalog: empty tableChanges skipped; comment-only unchanged
    skipped; comment change forwarded.
    Ran ./gradlew :flink-connector:flink-common:test --tests
    "org.apache.gravitino.flink.connector.catalog.TestBaseCatalog" --tests
    "org.apache.gravitino.flink.connector.hive.TestGravitinoHiveCatalog"
    -PskipITs — all pass. Spotless applied.
---
 .../flink/connector/catalog/BaseCatalog.java       | 40 ++++++++--
 .../flink/connector/hive/GravitinoHiveCatalog.java | 12 ++-
 .../flink/connector/catalog/TestBaseCatalog.java   | 89 +++++++++++++++++++++
 .../connector/hive/TestGravitinoHiveCatalog.java   | 90 ++++++++++++++++++++++
 .../paimon/TestGravitinoPaimonCatalog.java         | 58 ++++++++++++--
 5 files changed, 274 insertions(+), 15 deletions(-)

diff --git 
a/flink-connector/flink-common/src/main/java/org/apache/gravitino/flink/connector/catalog/BaseCatalog.java
 
b/flink-connector/flink-common/src/main/java/org/apache/gravitino/flink/connector/catalog/BaseCatalog.java
index bc9d87eac6..8195aacdea 100644
--- 
a/flink-connector/flink-common/src/main/java/org/apache/gravitino/flink/connector/catalog/BaseCatalog.java
+++ 
b/flink-connector/flink-common/src/main/java/org/apache/gravitino/flink/connector/catalog/BaseCatalog.java
@@ -539,11 +539,11 @@ public abstract class BaseCatalog extends AbstractCatalog 
{
         throw new CatalogException(e);
       }
     } else {
-      catalog()
-          .asTableCatalog()
-          .alterTable(identifier, getGravitinoTableChanges(existingTable, 
newTable));
-      // Invalidate native catalog cache after successful alter
-      invalidateTable(tablePath);
+      TableChange[] changes = getGravitinoTableChanges(existingTable, 
newTable);
+      if (alterGravitinoTable(identifier, changes)) {
+        // Invalidate native catalog cache after successful alter
+        invalidateTable(tablePath);
+      }
     }
   }
 
@@ -593,9 +593,11 @@ public abstract class BaseCatalog extends AbstractCatalog {
         throw new CatalogException(e);
       }
     } else {
-      catalog().asTableCatalog().alterTable(identifier, 
getGravitinoTableChanges(tableChanges));
-      // Invalidate native catalog cache after successful alter
-      invalidateTable(tablePath);
+      TableChange[] changes = getGravitinoTableChanges(tableChanges);
+      if (alterGravitinoTable(identifier, changes)) {
+        // Invalidate native catalog cache after successful alter
+        invalidateTable(tablePath);
+      }
     }
   }
 
@@ -935,6 +937,28 @@ public abstract class BaseCatalog extends AbstractCatalog {
     }
   }
 
+  /**
+   * Applies the given table changes to the underlying Gravitino table, 
skipping the call when there
+   * is nothing to change.
+   *
+   * <p>When {@code changes} is empty the resolved table already matches the 
existing one (for
+   * example, re-applying the same options or a comment-only alter with an 
unchanged comment).
+   * Gravitino's {@code TableUpdatesRequest.validate} rejects an empty update 
list with "updates
+   * must not be empty", so a no-op alter must be skipped rather than 
forwarded.
+   *
+   * @param identifier the identifier of the table to alter
+   * @param changes the Gravitino table changes to apply
+   * @return {@code true} if the alter was forwarded to Gravitino, {@code 
false} if it was skipped
+   *     because there was nothing to change
+   */
+  private boolean alterGravitinoTable(NameIdentifier identifier, TableChange[] 
changes) {
+    if (changes.length == 0) {
+      return false;
+    }
+    catalog().asTableCatalog().alterTable(identifier, changes);
+    return true;
+  }
+
   @VisibleForTesting
   static TableChange[] getGravitinoTableChanges(
       CatalogBaseTable existingTable, CatalogBaseTable newTable) {
diff --git 
a/flink-connector/flink-common/src/main/java/org/apache/gravitino/flink/connector/hive/GravitinoHiveCatalog.java
 
b/flink-connector/flink-common/src/main/java/org/apache/gravitino/flink/connector/hive/GravitinoHiveCatalog.java
index 5a3b9971f4..7805f9624c 100644
--- 
a/flink-connector/flink-common/src/main/java/org/apache/gravitino/flink/connector/hive/GravitinoHiveCatalog.java
+++ 
b/flink-connector/flink-common/src/main/java/org/apache/gravitino/flink/connector/hive/GravitinoHiveCatalog.java
@@ -18,6 +18,7 @@
  */
 package org.apache.gravitino.flink.connector.hive;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -281,7 +282,8 @@ public class GravitinoHiveCatalog extends BaseCatalog {
     }
   }
 
-  private void applyGenericTableAlter(
+  @VisibleForTesting
+  void applyGenericTableAlter(
       ObjectPath tablePath, Table existingTable, ResolvedCatalogTable newTable)
       throws TableNotExistException, CatalogException {
     NameIdentifier identifier =
@@ -311,6 +313,14 @@ public class GravitinoHiveCatalog extends BaseCatalog {
           }
         });
 
+    // When the resolved table is identical to the existing one (for example, 
re-applying the same
+    // options), no TableChange is produced. Skip the alter call in that case: 
Gravitino's
+    // TableUpdatesRequest.validate rejects an empty update list with "updates 
must not be empty",
+    // and a no-op alter should succeed rather than fail.
+    if (changes.isEmpty()) {
+      return;
+    }
+
     try {
       catalog().asTableCatalog().alterTable(identifier, changes.toArray(new 
TableChange[0]));
     } catch (NoSuchTableException e) {
diff --git 
a/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/catalog/TestBaseCatalog.java
 
b/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/catalog/TestBaseCatalog.java
index 96e9215ba5..7c5db161c7 100644
--- 
a/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/catalog/TestBaseCatalog.java
+++ 
b/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/catalog/TestBaseCatalog.java
@@ -38,6 +38,7 @@ import org.apache.flink.table.catalog.ResolvedCatalogView;
 import org.apache.flink.table.catalog.ResolvedSchema;
 import org.apache.flink.table.catalog.TableChange;
 import org.apache.flink.table.catalog.exceptions.CatalogException;
+import org.apache.flink.table.catalog.exceptions.TableNotExistException;
 import org.apache.gravitino.Catalog;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
@@ -414,12 +415,86 @@ public class TestBaseCatalog {
     }
   }
 
+  @Test
+  public void testAlterTableWithEmptyTableChangesSkipsAlterCall() throws 
Exception {
+    // Flink may invoke alterTable with an empty change list (a no-op alter). 
The connector must
+    // not forward an empty update list to Gravitino, which would fail 
server-side with
+    // "updates must not be empty".
+    Catalog gravitinoCatalog = Mockito.mock(Catalog.class);
+    TableCatalog tableCatalog = Mockito.mock(TableCatalog.class);
+    Mockito.when(gravitinoCatalog.asTableCatalog()).thenReturn(tableCatalog);
+
+    Schema schema = Schema.newBuilder().column("id", DataTypes.INT()).build();
+    CatalogBaseTable table =
+        DefaultCatalogCompat.INSTANCE.createCatalogTable(
+            schema, "comment", ImmutableList.of(), ImmutableMap.of("key", 
"value"));
+
+    TestableBaseCatalog catalog =
+        new TestableBaseCatalog(Mockito.mock(AbstractCatalog.class), 
gravitinoCatalog, table);
+
+    catalog.alterTable(new ObjectPath("db", "tbl"), table, 
Collections.emptyList(), false);
+
+    Mockito.verify(tableCatalog, Mockito.never()).alterTable(Mockito.any(), 
Mockito.any());
+  }
+
+  @Test
+  public void testAlterTableWithCommentOnlyAndUnchangedCommentSkipsAlterCall() 
throws Exception {
+    // The two-argument alterTable diffs only the comment. When the comment is 
unchanged no
+    // TableChange is produced, so the connector must skip the alter call 
instead of forwarding an
+    // empty update list.
+    Catalog gravitinoCatalog = Mockito.mock(Catalog.class);
+    TableCatalog tableCatalog = Mockito.mock(TableCatalog.class);
+    Mockito.when(gravitinoCatalog.asTableCatalog()).thenReturn(tableCatalog);
+
+    Schema schema = Schema.newBuilder().column("id", DataTypes.INT()).build();
+    CatalogBaseTable table =
+        DefaultCatalogCompat.INSTANCE.createCatalogTable(
+            schema, "comment", ImmutableList.of(), ImmutableMap.of("key", 
"value"));
+
+    TestableBaseCatalog catalog =
+        new TestableBaseCatalog(Mockito.mock(AbstractCatalog.class), 
gravitinoCatalog, table);
+
+    catalog.alterTable(new ObjectPath("db", "tbl"), table, false);
+
+    Mockito.verify(tableCatalog, Mockito.never()).alterTable(Mockito.any(), 
Mockito.any());
+  }
+
+  @Test
+  public void testAlterTableWithCommentChangeForwardsAlterCall() throws 
Exception {
+    // A comment change produces a TableChange, so the alter must be forwarded 
to Gravitino.
+    Catalog gravitinoCatalog = Mockito.mock(Catalog.class);
+    TableCatalog tableCatalog = Mockito.mock(TableCatalog.class);
+    Mockito.when(gravitinoCatalog.asTableCatalog()).thenReturn(tableCatalog);
+
+    Schema schema = Schema.newBuilder().column("id", DataTypes.INT()).build();
+    CatalogBaseTable existingTable =
+        DefaultCatalogCompat.INSTANCE.createCatalogTable(
+            schema, "old comment", ImmutableList.of(), ImmutableMap.of("key", 
"value"));
+    CatalogBaseTable newTable =
+        DefaultCatalogCompat.INSTANCE.createCatalogTable(
+            schema, "new comment", ImmutableList.of(), ImmutableMap.of("key", 
"value"));
+
+    TestableBaseCatalog catalog =
+        new TestableBaseCatalog(
+            Mockito.mock(AbstractCatalog.class), gravitinoCatalog, 
existingTable);
+
+    catalog.alterTable(new ObjectPath("db", "tbl"), newTable, false);
+
+    Mockito.verify(tableCatalog, Mockito.times(1)).alterTable(Mockito.any(), 
Mockito.any());
+  }
+
   private static class TestableBaseCatalog extends BaseCatalog {
 
     private final AbstractCatalog delegate;
     private final Catalog gravitinoCatalog;
+    private final CatalogBaseTable existingTable;
 
     TestableBaseCatalog(AbstractCatalog delegate, Catalog gravitinoCatalog) {
+      this(delegate, gravitinoCatalog, null);
+    }
+
+    TestableBaseCatalog(
+        AbstractCatalog delegate, Catalog gravitinoCatalog, CatalogBaseTable 
existingTable) {
       super(
           "test",
           Collections.emptyMap(),
@@ -428,6 +503,7 @@ public class TestBaseCatalog {
           Mockito.mock(PartitionConverter.class));
       this.delegate = delegate;
       this.gravitinoCatalog = gravitinoCatalog;
+      this.existingTable = existingTable;
     }
 
     @Override
@@ -439,5 +515,18 @@ public class TestBaseCatalog {
     protected Catalog catalog() {
       return gravitinoCatalog;
     }
+
+    @Override
+    public CatalogBaseTable getTable(ObjectPath tablePath) throws 
TableNotExistException {
+      if (existingTable != null) {
+        return existingTable;
+      }
+      return super.getTable(tablePath);
+    }
+
+    @Override
+    protected void invalidateTable(ObjectPath tablePath) {
+      // No-op: the native cache is not exercised in these unit tests.
+    }
   }
 }
diff --git 
a/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/hive/TestGravitinoHiveCatalog.java
 
b/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/hive/TestGravitinoHiveCatalog.java
index 64cd45641b..4eea1d8bee 100644
--- 
a/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/hive/TestGravitinoHiveCatalog.java
+++ 
b/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/hive/TestGravitinoHiveCatalog.java
@@ -18,14 +18,24 @@
  */
 package org.apache.gravitino.flink.connector.hive;
 
+import com.google.common.collect.ImmutableMap;
 import java.util.Collections;
+import java.util.Map;
+import org.apache.flink.table.api.DataTypes;
+import org.apache.flink.table.api.Schema;
 import org.apache.flink.table.catalog.AbstractCatalog;
+import org.apache.flink.table.catalog.CatalogTable;
+import org.apache.flink.table.catalog.Column;
 import org.apache.flink.table.catalog.ObjectPath;
+import org.apache.flink.table.catalog.ResolvedCatalogTable;
+import org.apache.flink.table.catalog.ResolvedSchema;
 import org.apache.flink.table.catalog.exceptions.CatalogException;
 import org.apache.gravitino.Catalog;
 import org.apache.gravitino.exceptions.ForbiddenException;
 import org.apache.gravitino.flink.connector.PartitionConverter;
 import org.apache.gravitino.flink.connector.SchemaAndTablePropertiesConverter;
+import org.apache.gravitino.flink.connector.utils.DefaultCatalogCompat;
+import org.apache.gravitino.rel.Table;
 import org.apache.gravitino.rel.TableCatalog;
 import org.apache.hadoop.hive.conf.HiveConf;
 import org.junit.jupiter.api.Assertions;
@@ -50,10 +60,84 @@ public class TestGravitinoHiveCatalog {
     Assertions.assertSame(forbiddenException, catalogException.getCause());
   }
 
+  @Test
+  public void testGenericTableAlterSkipsCallWhenNoChanges() throws Exception {
+    // Existing table and the resolved new table describe the same state (same 
properties, same
+    // comment), so no TableChange is produced. The connector must not forward 
an empty update
+    // list to Gravitino, which would fail server-side with "updates must not 
be empty".
+    Map<String, String> sameProperties =
+        ImmutableMap.of("flink.connector", "kafka", "is_generic", "true");
+
+    Catalog gravitinoCatalog = Mockito.mock(Catalog.class);
+    TableCatalog tableCatalog = Mockito.mock(TableCatalog.class);
+    Mockito.when(gravitinoCatalog.asTableCatalog()).thenReturn(tableCatalog);
+
+    Table existingTable = Mockito.mock(Table.class);
+    Mockito.when(existingTable.properties()).thenReturn(sameProperties);
+    Mockito.when(existingTable.comment()).thenReturn("same comment");
+
+    ResolvedCatalogTable newTable = resolvedTable("same comment");
+
+    TestableGravitinoHiveCatalog catalog =
+        new TestableGravitinoHiveCatalog(gravitinoCatalog, sameProperties);
+
+    catalog.applyGenericTableAlter(new ObjectPath("db", "tbl"), existingTable, 
newTable);
+
+    // The alter call is skipped entirely because there is nothing to change.
+    Mockito.verify(tableCatalog, Mockito.never()).alterTable(Mockito.any(), 
Mockito.any());
+  }
+
+  @Test
+  public void testGenericTableAlterCallsAlterWhenPropertiesChange() throws 
Exception {
+    // The resolved new table changes a property, so the connector must 
forward the update.
+    Map<String, String> currentProperties =
+        ImmutableMap.of("flink.connector", "kafka", "is_generic", "true");
+    Map<String, String> updatedProperties =
+        ImmutableMap.of(
+            "flink.connector", "kafka", "flink.topic", "new-topic", 
"is_generic", "true");
+
+    Catalog gravitinoCatalog = Mockito.mock(Catalog.class);
+    TableCatalog tableCatalog = Mockito.mock(TableCatalog.class);
+    Mockito.when(gravitinoCatalog.asTableCatalog()).thenReturn(tableCatalog);
+
+    Table existingTable = Mockito.mock(Table.class);
+    Mockito.when(existingTable.properties()).thenReturn(currentProperties);
+    Mockito.when(existingTable.comment()).thenReturn("comment");
+
+    ResolvedCatalogTable newTable = resolvedTable("comment");
+
+    TestableGravitinoHiveCatalog catalog =
+        new TestableGravitinoHiveCatalog(gravitinoCatalog, updatedProperties);
+
+    catalog.applyGenericTableAlter(new ObjectPath("db", "tbl"), existingTable, 
newTable);
+
+    // A real change was present, so the alter call is forwarded to Gravitino.
+    Mockito.verify(tableCatalog, Mockito.times(1)).alterTable(Mockito.any(), 
Mockito.any());
+  }
+
+  private static ResolvedCatalogTable resolvedTable(String comment) {
+    Schema schema = Schema.newBuilder().column("id", DataTypes.INT()).build();
+    CatalogTable table =
+        DefaultCatalogCompat.INSTANCE.createCatalogTable(
+            schema, comment, Collections.emptyList(), Collections.emptyMap());
+    ResolvedSchema resolvedSchema =
+        new ResolvedSchema(
+            Collections.singletonList(Column.physical("id", DataTypes.INT())),
+            Collections.emptyList(),
+            null);
+    return new ResolvedCatalogTable(table, resolvedSchema);
+  }
+
   private static class TestableGravitinoHiveCatalog extends 
GravitinoHiveCatalog {
     private final Catalog gravitinoCatalog;
+    private final Map<String, String> genericTableProperties;
 
     TestableGravitinoHiveCatalog(Catalog gravitinoCatalog) {
+      this(gravitinoCatalog, Collections.emptyMap());
+    }
+
+    TestableGravitinoHiveCatalog(
+        Catalog gravitinoCatalog, Map<String, String> genericTableProperties) {
       super(
           "test",
           "default",
@@ -63,6 +147,7 @@ public class TestGravitinoHiveCatalog {
           hiveConf(),
           null);
       this.gravitinoCatalog = gravitinoCatalog;
+      this.genericTableProperties = genericTableProperties;
     }
 
     @Override
@@ -75,6 +160,11 @@ public class TestGravitinoHiveCatalog {
       return gravitinoCatalog;
     }
 
+    @Override
+    protected Map<String, String> 
toGravitinoGenericTableProperties(ResolvedCatalogTable table) {
+      return genericTableProperties;
+    }
+
     private static HiveConf hiveConf() {
       HiveConf hiveConf = new HiveConf();
       hiveConf.set("hive.metastore.uris", "thrift://localhost:9083");
diff --git 
a/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/paimon/TestGravitinoPaimonCatalog.java
 
b/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/paimon/TestGravitinoPaimonCatalog.java
index 89b7bc3fec..0e1c7355fc 100644
--- 
a/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/paimon/TestGravitinoPaimonCatalog.java
+++ 
b/flink-connector/flink-common/src/test/java/org/apache/gravitino/flink/connector/paimon/TestGravitinoPaimonCatalog.java
@@ -409,13 +409,15 @@ public class TestGravitinoPaimonCatalog {
 
     TestablePaimonCatalog cat = new TestablePaimonCatalog(mockFlinkCatalog, 
mockCatalog);
     ObjectPath path = new ObjectPath("mydb", "mytable");
+    org.apache.flink.table.api.Schema schema =
+        org.apache.flink.table.api.Schema.newBuilder().column("id", 
DataTypes.INT()).build();
+    // The existing table carries an old comment so the alter produces a real 
TableChange
+    // (comment update) rather than a no-op.
+    CatalogTable existingFlinkTable =
+        CatalogTable.of(schema, "old comment", Collections.emptyList(), 
Collections.emptyMap());
     CatalogTable newTable =
-        CatalogTable.of(
-            org.apache.flink.table.api.Schema.newBuilder().column("id", 
DataTypes.INT()).build(),
-            "new comment",
-            Collections.emptyList(),
-            Collections.emptyMap());
-    when(mockFlinkCatalog.getTable(path)).thenReturn(newTable);
+        CatalogTable.of(schema, "new comment", Collections.emptyList(), 
Collections.emptyMap());
+    when(mockFlinkCatalog.getTable(path)).thenReturn(existingFlinkTable);
 
     cat.alterTable(path, newTable, false);
 
@@ -423,6 +425,50 @@ public class TestGravitinoPaimonCatalog {
     verify(mockInnerCatalog).invalidateTable(Identifier.create("mydb", 
"mytable"));
   }
 
+  /**
+   * Verifies that a no-op Paimon alterTable (existing and new tables 
identical) neither forwards
+   * the alter to Gravitino nor invalidates the native cache.
+   */
+  @Test
+  public void testAlterTableNoOpDoesNotInvalidateNativeCache() throws 
Exception {
+    org.apache.paimon.catalog.Catalog mockInnerCatalog =
+        mock(org.apache.paimon.catalog.Catalog.class);
+    FlinkCatalog mockFlinkCatalog = mock(FlinkCatalog.class);
+    when(mockFlinkCatalog.catalog()).thenReturn(mockInnerCatalog);
+
+    Catalog mockCatalog = mock(Catalog.class);
+    TableCatalog mockTableCatalog = mock(TableCatalog.class);
+    when(mockCatalog.asTableCatalog()).thenReturn(mockTableCatalog);
+
+    Table existingTable = mock(Table.class);
+    org.apache.gravitino.rel.Column existingColumn =
+        org.apache.gravitino.rel.Column.of(
+            "id", org.apache.gravitino.rel.types.Types.IntegerType.get());
+    when(existingTable.columns())
+        .thenReturn(new org.apache.gravitino.rel.Column[] {existingColumn});
+    when(existingTable.index()).thenReturn(new Index[0]);
+    when(existingTable.properties()).thenReturn(Collections.emptyMap());
+    when(existingTable.distribution()).thenReturn(null);
+    when(existingTable.partitioning()).thenReturn(Transforms.EMPTY_TRANSFORM);
+    when(existingTable.comment()).thenReturn("same comment");
+    when(mockTableCatalog.loadTable(any())).thenReturn(existingTable);
+
+    TestablePaimonCatalog cat = new TestablePaimonCatalog(mockFlinkCatalog, 
mockCatalog);
+    ObjectPath path = new ObjectPath("mydb", "mytable");
+    org.apache.flink.table.api.Schema schema =
+        org.apache.flink.table.api.Schema.newBuilder().column("id", 
DataTypes.INT()).build();
+    // The existing table and the alter target share the same comment, so no 
TableChange is
+    // produced and the alter must be skipped.
+    CatalogTable sameTable =
+        CatalogTable.of(schema, "same comment", Collections.emptyList(), 
Collections.emptyMap());
+    when(mockFlinkCatalog.getTable(path)).thenReturn(sameTable);
+
+    cat.alterTable(path, sameTable, false);
+
+    verify(mockTableCatalog, never()).alterTable(any(), any());
+    verify(mockInnerCatalog, never()).invalidateTable(any());
+  }
+
   /** Verifies that successful Paimon dropTable invalidates the native cache. 
*/
   @Test
   public void testDropTableInvalidatesNativeCacheAfterSuccessfulPurge() throws 
Exception {

Reply via email to