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 {