This is an automated email from the ASF dual-hosted git repository.
yuqi1129 pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/branch-1.3 by this push:
new 284055780d [Cherry-pick to branch-1.3] [#13895] fix(iceberg): report
missing views after native rename (#13896) (#13937)
284055780d is described below
commit 284055780d15e5b6288fa81e08313b503e39f147
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Oct 19 17:15:40 2026 +0800
[Cherry-pick to branch-1.3] [#13895] fix(iceberg): report missing views
after native rename (#13896) (#13937)
**Cherry-pick Information:**
- Original commit: d8fb60ba331b87e4ffcda6fc1d96a1afb0cb9f27
- Target branch: `branch-1.3`
- Status: ⚠️ **Has conflicts - manual resolution required**
**Do not merge** until conflict markers are resolved and the
`cherry-pick-conflict` label is removed.
Please review and resolve the conflicts before merging.
---------
Co-authored-by: Qi Yu <[email protected]>
---
.../iceberg/common/ops/IcebergCatalogWrapper.java | 27 +++++++++-
.../common/ops/TestIcebergCatalogWrapper.java | 59 ++++++++++++++++++++++
2 files changed, 85 insertions(+), 1 deletion(-)
diff --git
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapper.java
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapper.java
index 95ad00836a..2b122ab49d 100644
---
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapper.java
+++
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapper.java
@@ -48,6 +48,7 @@ import org.apache.iceberg.catalog.Namespace;
import org.apache.iceberg.catalog.SupportsNamespaces;
import org.apache.iceberg.catalog.TableIdentifier;
import org.apache.iceberg.catalog.ViewCatalog;
+import org.apache.iceberg.exceptions.NoSuchViewException;
import org.apache.iceberg.io.FileIO;
import org.apache.iceberg.io.ResolvingFileIO;
import org.apache.iceberg.jdbc.JdbcCatalogWithMetadataLocationSupport;
@@ -63,11 +64,15 @@ import
org.apache.iceberg.rest.requests.UpdateNamespacePropertiesRequest;
import org.apache.iceberg.rest.requests.UpdateTableRequest;
import org.apache.iceberg.rest.responses.CreateNamespaceResponse;
import org.apache.iceberg.rest.responses.GetNamespaceResponse;
+import org.apache.iceberg.rest.responses.ImmutableLoadViewResponse;
import org.apache.iceberg.rest.responses.ListNamespacesResponse;
import org.apache.iceberg.rest.responses.ListTablesResponse;
import org.apache.iceberg.rest.responses.LoadTableResponse;
import org.apache.iceberg.rest.responses.LoadViewResponse;
import org.apache.iceberg.rest.responses.UpdateNamespacePropertiesResponse;
+import org.apache.iceberg.view.BaseView;
+import org.apache.iceberg.view.View;
+import org.apache.iceberg.view.ViewMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -361,8 +366,28 @@ public class IcebergCatalogWrapper implements
AutoCloseable {
return CatalogHandlers.updateView(getViewCatalog(), viewIdentifier,
request);
}
+ /**
+ * Loads a view response from one metadata snapshot.
+ *
+ * @param viewIdentifier the view to load
+ * @return the view metadata and its location
+ * @throws NoSuchViewException if the view does not exist or disappears
during loading
+ */
public LoadViewResponse loadView(TableIdentifier viewIdentifier) {
- return CatalogHandlers.loadView(getViewCatalog(), viewIdentifier);
+ View view = getViewCatalog().loadView(viewIdentifier);
+ Preconditions.checkState(
+ view instanceof BaseView, "Cannot wrap catalog that does not produce
BaseView");
+ ViewMetadata metadata = ((BaseView) view).operations().current();
+ if (metadata == null) {
+ throw new NoSuchViewException("View does not exist: %s", viewIdentifier);
+ }
+ // CatalogHandlers' private viewResponse helper assumes non-null metadata.
A native rename
+ // can remove the view after loadView's existence check, so build the same
response from the
+ // checked snapshot rather than loading it again through the handler.
+ return ImmutableLoadViewResponse.builder()
+ .metadata(metadata)
+ .metadataLocation(metadata.metadataFileLocation())
+ .build();
}
public void dropView(TableIdentifier viewIdentifier) {
diff --git
a/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/ops/TestIcebergCatalogWrapper.java
b/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/ops/TestIcebergCatalogWrapper.java
index 48941d7f8f..9b87546e25 100644
---
a/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/ops/TestIcebergCatalogWrapper.java
+++
b/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/ops/TestIcebergCatalogWrapper.java
@@ -33,6 +33,7 @@ import org.apache.gravitino.iceberg.common.IcebergConfig;
import org.apache.gravitino.iceberg.common.cache.SupportsMetadataLocation;
import org.apache.gravitino.iceberg.common.cache.TableMetadataCache;
import org.apache.gravitino.iceberg.common.utils.IcebergCatalogUtil;
+import org.apache.iceberg.CatalogProperties;
import org.apache.iceberg.PartitionSpec;
import org.apache.iceberg.Schema;
import org.apache.iceberg.TableMetadata;
@@ -40,14 +41,72 @@ import org.apache.iceberg.TableMetadataParser;
import org.apache.iceberg.catalog.Catalog;
import org.apache.iceberg.catalog.Namespace;
import org.apache.iceberg.catalog.TableIdentifier;
+import org.apache.iceberg.exceptions.NoSuchViewException;
+import org.apache.iceberg.jdbc.JdbcCatalog;
import org.apache.iceberg.rest.requests.CreateNamespaceRequest;
+import org.apache.iceberg.rest.responses.LoadViewResponse;
import org.apache.iceberg.types.Types;
+import org.apache.iceberg.view.ViewOperations;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Mockito;
public class TestIcebergCatalogWrapper {
+ @Test
+ void testLoadViewReportsMissingAfterNativeRename(@TempDir Path warehouseDir)
throws Exception {
+ Namespace namespace = Namespace.of("views");
+ TableIdentifier oldName = TableIdentifier.of(namespace, "old_name");
+ TableIdentifier newName = TableIdentifier.of(namespace, "new_name");
+ AtomicBoolean renameDuringLoad = new AtomicBoolean();
+ AtomicInteger operationLoads = new AtomicInteger();
+ try (JdbcCatalog catalog =
+ new JdbcCatalog() {
+ @Override
+ protected ViewOperations newViewOps(TableIdentifier identifier) {
+ // BaseMetastoreViewCatalog checks one operations instance, then
returns a BaseView
+ // backed by another. Rename between those two instances, without
timing or threads.
+ if (renameDuringLoad.get()
+ && identifier.equals(oldName)
+ && operationLoads.incrementAndGet() == 2) {
+ renameView(oldName, newName);
+ }
+ return super.newViewOps(identifier);
+ }
+ }) {
+ catalog.initialize(
+ "rename-test",
+ Map.of(
+ CatalogProperties.URI,
+ "jdbc:sqlite:" + warehouseDir.resolve("catalog.db"),
+ CatalogProperties.WAREHOUSE_LOCATION,
+ warehouseDir.toUri().toString(),
+ "jdbc.schema-version",
+ "V1"));
+ catalog.createNamespace(namespace);
+ catalog
+ .buildView(oldName)
+ .withSchema(new Schema(Types.NestedField.required(1, "id",
Types.IntegerType.get())))
+ .withDefaultNamespace(namespace)
+ .withQuery("spark", "SELECT 1 AS id")
+ .create();
+ IcebergCatalogWrapper wrapper =
+ Mockito.spy(new IcebergCatalogWrapper(new
IcebergConfig(unreachableConfig())));
+ Mockito.doReturn(catalog).when(wrapper).getCatalog();
+ renameDuringLoad.set(true);
+
+ NoSuchViewException failure =
+ Assertions.assertThrows(NoSuchViewException.class, () ->
wrapper.loadView(oldName));
+ Assertions.assertTrue(failure.getMessage().contains(oldName.toString()));
+ Assertions.assertFalse(catalog.viewExists(oldName));
+ LoadViewResponse response = wrapper.loadView(newName);
+ Assertions.assertNotNull(response.metadata());
+ Assertions.assertEquals(
+ response.metadata().metadataFileLocation(),
response.metadataLocation());
+ }
+ }
+
@Test
public void testCatalogShouldBeLazyLoaded() {
IcebergCatalogWrapper wrapper =