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 =

Reply via email to