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 fdb22354ce [Cherry-pick to branch-1.3] [#13278] fix(core): clean up 
missing schemas on explicit cascading drops (#13279) (#13282)
fdb22354ce is described below

commit fdb22354cead724a48986f83a300b3a15cf11a10
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Sep 17 22:06:25 2026 +0800

    [Cherry-pick to branch-1.3] [#13278] fix(core): clean up missing schemas on 
explicit cascading drops (#13279) (#13282)
    
    ### What changes were proposed in this pull request?
    
    Backport dc2545f03594c239e0884bedb58fe862c67f4247 (#13279) to
    `branch-1.3`, resolving the cherry-pick conflicts.
    
    An explicit cascading drop removes a schema registration even when the
    source schema is already absent and reports success when either is
    removed. A non-cascading drop preserves the registration when the source
    schema is absent. Existing hierarchical ancestor cleanup is retained.
    
    The SecretManager cleanup and its test are excluded because schema
    write-through secrets are not supported on this branch.
    
    ### Why are the changes needed?
    
    Report metadata-only cleanup accurately and retain the original fix's
    distinction between explicit cascading cleanup and non-cascading drops.
    
    Related: #13278
    
    ### Does this PR introduce _any_ user-facing change?
    
    Yes. Metadata-only cascading cleanup returns `dropped: true`; a repeated
    drop returns false. A non-cascading drop of a missing source schema
    preserves its stored registration.
    
    ### How was this patch tested?
    
    - `./gradlew spotlessApply :core:test -PskipITs -PskipDockerTests=true
    -PskipWeb=true`
    - Core suite: 1601 tests, 0 failures, 0 errors, 2 skipped.
    - Regression tests cover source-side deletion, repeated drops, and
    non-cascading preservation.
    - `git diff --check`.
    
    ---------
    
    Co-authored-by: Qi Yu <[email protected]>
---
 .../catalog/SchemaOperationDispatcher.java         | 27 ++++++++---------
 .../catalog/TestSchemaOperationDispatcher.java     | 34 ++++++++++++++++++++++
 2 files changed, 46 insertions(+), 15 deletions(-)

diff --git 
a/core/src/main/java/org/apache/gravitino/catalog/SchemaOperationDispatcher.java
 
b/core/src/main/java/org/apache/gravitino/catalog/SchemaOperationDispatcher.java
index a6cd4589f0..54ea2df613 100644
--- 
a/core/src/main/java/org/apache/gravitino/catalog/SchemaOperationDispatcher.java
+++ 
b/core/src/main/java/org/apache/gravitino/catalog/SchemaOperationDispatcher.java
@@ -339,20 +339,17 @@ public class SchemaOperationDispatcher extends 
OperationDispatcher implements Sc
             return droppedFromCatalog;
           }
 
-          // For the unmanaged schema, it could happen that the schema:
-          // 1. It's not found in the catalog (dropped directly from 
underlying sources)
-          // 2. It's found in the catalog but not in the store (not managed by 
Gravitino)
-          // 3. It's found in the catalog and the store (managed by Gravitino)
-          // 4. Neither found in the catalog nor in the store.
-          // In all situations, we try to delete the schema from the store, 
but we don't take the
-          // return value of the store operation into account. We only take 
the return value of the
-          // catalog into account.
-          try {
-            store.delete(ident, SCHEMA, true);
-          } catch (NoSuchEntityException e) {
-            LOG.warn("The schema to be dropped does not exist in the store: 
{}", ident, e);
-          } catch (Exception e) {
-            throw new RuntimeException(e);
+          // A non-cascading drop preserves a missing registration because the 
source schema
+          // may have been renamed. An explicit cascading drop also removes 
stale metadata.
+          boolean droppedFromStore = false;
+          if (droppedFromCatalog || cascade) {
+            try {
+              droppedFromStore = store.delete(ident, SCHEMA, true);
+            } catch (NoSuchEntityException e) {
+              LOG.warn("The schema to be dropped does not exist in the store: 
{}", ident, e);
+            } catch (Exception e) {
+              throw new RuntimeException(e);
+            }
           }
 
           SchemaEntityCleaner.deleteOrphanedSchemaEntities(
@@ -364,7 +361,7 @@ public class SchemaOperationDispatcher extends 
OperationDispatcher implements Sc
                       catalogIdent,
                       c -> c.doWithSchemaOps(s -> s.schemaExists(schemaIdent)),
                       RuntimeException.class));
-          return droppedFromCatalog;
+          return droppedFromCatalog || droppedFromStore;
         });
   }
 
diff --git 
a/core/src/test/java/org/apache/gravitino/catalog/TestSchemaOperationDispatcher.java
 
b/core/src/test/java/org/apache/gravitino/catalog/TestSchemaOperationDispatcher.java
index 455e428c34..c452699214 100644
--- 
a/core/src/test/java/org/apache/gravitino/catalog/TestSchemaOperationDispatcher.java
+++ 
b/core/src/test/java/org/apache/gravitino/catalog/TestSchemaOperationDispatcher.java
@@ -369,6 +369,40 @@ public class TestSchemaOperationDispatcher extends 
TestOperationDispatcher {
         RuntimeException.class, () -> dispatcher.dropSchema(schemaIdent, 
false));
   }
 
+  @Test
+  void testDropMissingSchemaPreservesStoredEntity() throws Exception {
+    reset(entityStore);
+    NameIdentifier schemaIdent = NameIdentifier.of(metalake, catalog, 
"schema_renamed_out_of_band");
+    Map<String, String> props = ImmutableMap.of("k1", "v1", "k2", "v2");
+    dispatcher.createSchema(schemaIdent, "comment", props);
+
+    boolean droppedFromSource =
+        catalogManager.doWithCatalogWrapper(
+            NameIdentifier.of(metalake, catalog),
+            wrapper -> wrapper.doWithSchemaOps(ops -> 
ops.dropSchema(schemaIdent, false)));
+    Assertions.assertTrue(droppedFromSource);
+
+    Assertions.assertFalse(dispatcher.dropSchema(schemaIdent, false));
+    Assertions.assertTrue(entityStore.exists(schemaIdent, SCHEMA));
+  }
+
+  @Test
+  void testDropSchemaRemovedFromSourceReportsMetadataCleanup() throws 
Exception {
+    reset(entityStore);
+    NameIdentifier ident = NameIdentifier.of(metalake, catalog, 
"externally_dropped_schema");
+    dispatcher.createSchema(ident, "comment", ImmutableMap.of("k1", "v1", 
"k2", "v2"));
+    boolean droppedFromSource =
+        catalogManager.doWithCatalogWrapper(
+            NameIdentifier.of(metalake, catalog),
+            wrapper -> wrapper.doWithSchemaOps(ops -> ops.dropSchema(ident, 
true)));
+    Assertions.assertTrue(droppedFromSource);
+    Assertions.assertTrue(entityStore.exists(ident, SCHEMA));
+
+    Assertions.assertTrue(dispatcher.dropSchema(ident, true));
+    Assertions.assertFalse(entityStore.exists(ident, SCHEMA));
+    Assertions.assertFalse(dispatcher.dropSchema(ident, true));
+  }
+
   @Test
   public void testDropHierarchicalSchemaCleansUpOrphanedAncestors() throws 
IOException {
     // Clear any spy stubs leaked from other tests sharing the static 
entityStore.

Reply via email to