Copilot commented on code in PR #11201:
URL: https://github.com/apache/gravitino/pull/11201#discussion_r3295759217


##########
core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/SchemaMetaBaseSQLProvider.java:
##########
@@ -86,6 +86,22 @@ public String listSchemaPOsBySchemaIds(@Param("schemaIds") 
List<Long> schemaIds)
         + "</script>";
   }
 
+  public String listSchemaPOsByCatalogIdAndNamePrefix(
+      @Param("catalogId") Long catalogId,
+      @Param("schemaName") String schemaName,
+      @Param("descendantPrefix") String descendantPrefix) {
+    return "SELECT schema_id as schemaId, schema_name as schemaName,"
+        + " metalake_id as metalakeId, catalog_id as catalogId,"
+        + " schema_comment as schemaComment, properties, audit_info as 
auditInfo,"
+        + " current_version as currentVersion, last_version as lastVersion,"
+        + " deleted_at as deletedAt"
+        + " FROM "
+        + TABLE_NAME
+        + " WHERE catalog_id = #{catalogId}"
+        + " AND (schema_name = #{schemaName} OR schema_name LIKE 
CONCAT(#{descendantPrefix}, '%'))"

Review Comment:
   The prefix match for cascade deletion uses `LIKE` with an unescaped pattern. 
Since schema names are validated with patterns that allow `_` (and could allow 
other LIKE metacharacters such as `%` depending on catalog capability), `LIKE 
CONCAT(#{descendantPrefix}, '%')` can match unintended schemas (e.g., `a_b` 
also matches `axb...`), causing incorrect cascade deletes.
   
   Escape LIKE metacharacters in the prefix and add an `ESCAPE` clause so the 
match is a literal prefix match.
   



##########
iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/dispatcher/TestIcebergNamespaceHookDispatcher.java:
##########
@@ -268,12 +268,12 @@ public void 
testDropNamespaceDeletesTargetAndPhantomAncestors() throws Exception
     verify(mockDispatcher, never()).dropNamespace(mockContext, parent);
     verify(mockDispatcher, never()).dropNamespace(mockContext, grandparent);
 
+    // The leaf and both phantom ancestors are stale, so a single cascade 
delete of the outermost
+    // empty ancestor (A) removes the whole stale chain in one batched 
operation.
     ArgumentCaptor<NameIdentifier> captor = 
ArgumentCaptor.forClass(NameIdentifier.class);
-    verify(mockEntityStore, org.mockito.Mockito.times(3))
-        .delete(captor.capture(), eq(Entity.EntityType.SCHEMA));
-    List<String> deletedNames =
-        
captor.getAllValues().stream().map(NameIdentifier::name).collect(Collectors.toList());
-    Assertions.assertEquals(Arrays.asList("A:B:C", "A:B", "A"), deletedNames);
+    verify(mockEntityStore, org.mockito.Mockito.times(1))
+        .delete(captor.capture(), eq(Entity.EntityType.SCHEMA), eq(true));

Review Comment:
   This uses a fully-qualified reference to `org.mockito.Mockito.times(...)` 
inside the method body even though the file already relies on 
`org.mockito.Mockito` static imports for other helpers. To align with the 
project's Java import hygiene guideline (avoid FQNs in code), prefer a static 
import for `times` (or import `Mockito`) and call it without the package 
qualifier.



##########
iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/dispatcher/IcebergNamespaceHookDispatcher.java:
##########
@@ -148,16 +147,22 @@ public void dropNamespace(IcebergRequestContext context, 
Namespace namespace) {
         () -> {
           dispatcher.dropNamespace(context, namespace);
 
-          // TODO: Use cascade mode deletion
-          // Current behavior: only the leaf namespace is dropped from the 
Iceberg catalog above.
-          // We walk ancestors outermost-to-leaf and clean up a Gravitino 
entity only while the
-          // corresponding Iceberg namespace no longer exists, stopping at the 
first ancestor that
-          // still exists. As a result, ancestor entities are cleaned up only 
when the underlying
-          // Iceberg catalog also removes the empty parent namespaces on 
leaf-drop, which is
-          // catalog-implementation-dependent. For catalogs that keep empty 
parents, operators may
-          // need to drop empty parent namespaces manually.
+          // Only the leaf namespace is dropped from the Iceberg catalog 
above. Walk its
+          // ancestors outermost-to-leaf to find the outermost ancestor whose 
Iceberg namespace
+          // no longer exists, stopping at the first ancestor that still 
exists. Everything from
+          // that outermost empty namespace down to the leaf is now stale in 
Gravitino.

Review Comment:
   This comment says the code walks ancestors "outermost-to-leaf", but the 
implementation iterates from the innermost ancestor outward (`for (i = size-1; 
i >= 0; i--)`) because `HierarchicalSchemaUtil.getAncestorNames` returns 
ancestors ordered outermost→innermost. Updating the wording will help readers 
reason about why `outermostStale` ends up being the outermost missing ancestor.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to