jerryshao commented on code in PR #13771:
URL: https://github.com/apache/gravitino/pull/13771#discussion_r4257595573


##########
catalogs/catalog-kafka/src/test/java/org/apache/gravitino/catalog/kafka/integration/test/CatalogKafkaIT.java:
##########
@@ -192,6 +192,35 @@ public void testCatalog() throws ExecutionException, 
InterruptedException {
     Assertions.assertFalse(adminClient.listTopics().names().get().isEmpty());
   }
 
+  @Test
+  public void testCatalogReregistrationLoadsExistingTopic()
+      throws ExecutionException, InterruptedException {
+    String catalogName = 
GravitinoITUtils.genRandomName("test_catalog_reregistration");
+    String topicName = 
GravitinoITUtils.genRandomName("test_topic_reregistration");
+    NameIdentifier topicIdent = NameIdentifier.of(DEFAULT_SCHEMA_NAME, 
topicName);
+    Map<String, String> properties = ImmutableMap.of(BOOTSTRAP_SERVERS, 
kafkaBootstrapServers);
+
+    adminClient.createTopics(ImmutableList.of(new NewTopic(topicName, 1, 
(short) 1))).all().get();
+
+    Catalog originalCatalog = createCatalog(catalogName, "original catalog", 
properties);
+    Assertions.assertEquals(
+        topicName, 
originalCatalog.asTopicCatalog().loadTopic(topicIdent).name());
+
+    Assertions.assertTrue(metalake.dropCatalog(catalogName, true));
+    Assertions.assertTrue(topicExistsInKafka(topicName));
+
+    Catalog reregisteredCatalog = createCatalog(catalogName, "re-registered 
catalog", properties);
+    Topic restoredTopic =
+        Assertions.assertDoesNotThrow(
+            () -> reregisteredCatalog.asTopicCatalog().loadTopic(topicIdent));
+    Assertions.assertEquals(topicName, restoredTopic.name());
+    checkTopicReadWrite(topicName);

Review Comment:
   [Question] This joins the two topic halves for the restore case - thanks. Is 
the refusal case covered anywhere outside the stubbed unit test?
   
   What this drives is the `deleted_at > 0` branch: the cascade soft-deletes 
the topic row (`CatalogMetaService.java:314-315` via `store.delete(ident, 
CATALOG, true)` at `CatalogManager.java:1417`), so the re-registered catalog's 
`loadTopic` hits the retained `topic_id` and `restoreSoftDeletedTopic` rebinds 
it. Good.
   
   The other half of the new behaviour is the explicit refusal when the same 
immutable Kafka UUID is live under another Gravitino path - the multi-path 
exposure this change exists to close, since `convertToGravitinoId` derives the 
id from the topic UUID with no catalog or metalake component. That path is only 
asserted with `entityStore` stubbed in `TestTopicOperationDispatcher`. Here it 
would be cheap: create a second Kafka catalog against the same 
`bootstrap.servers` while the first is still live, and assert `loadTopic` on 
the second one refuses rather than moving the row. Without it, nothing shows 
that an active row is not rebound.
   
   Two smaller things on this test: it does not assert the restored topic's id 
equals the pre-drop one (deterministic from the UUID either way, so the 
assertion would not distinguish restore from fresh insert - but asserting that 
the governance relation survives would), and `checkTopicReadWrite` exercises 
Kafka rather than the store, so the only store-level assertion is that 
`loadTopic` did not throw.
   
   Verified by: read this test in full (`CatalogKafkaIT.java:195-222`) plus the 
existing `dropCatalog(name, true)` at :184 and the helpers at :537, :600, :607, 
:660; read `CatalogManager.dropCatalog:1358-1420` and the cascade in 
`CatalogMetaService.java:285-335`; read the three stubbed tests at 
`TestTopicOperationDispatcher.java:172-230`. Not executed - no Docker test run 
in this pass.



##########
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/integration/test/SparkCommonIT.java:
##########
@@ -630,7 +630,8 @@ protected void testAlterTableReplaceColumns() {
 
     tableInfo = getTableInfo(tableName);
     checkTableColumns(tableName, updateColumns, tableInfo);
-    sql(String.format("INSERT INTO %S VALUES(3, 'name2', 10)", tableName));
+    sql(String.format("CREATE TABLE IF NOT EXISTS %s (id int, name2 string, 
age long)", tableName));
+    sql(String.format("INSERT INTO %s VALUES(3, 'name2', 10)", tableName));

Review Comment:
   [Important] This drops the last mixed-case coverage from the shared base 
test, for the suites that are not the new Hive-backend Iceberg one, and the 
statement added in its place cannot fail.
   
   On `main` this line is `INSERT INTO %S` (`git show 
origin/main:...SparkCommonIT.java` at 633), and `testAlterTableReplaceColumns` 
is enabled for `SparkHiveCatalogIT` (:105-107) and every 
`SparkIcebergCatalogIT` subclass (:108-110): the direct-Hive backend, 
`SparkIcebergCatalogRestBackendIT` and `SparkIcebergCatalogRestRoutingIT`, each 
in v3.5 and v4.0. All of them pass `%S` on `main`. After this edit none of them 
exercises an uppercase identifier: the Hive catalog (which folds via 
`HiveCatalogCapability`) loses coverage it did not need to lose, and the two 
REST suites lose the only test that would have shown the REST-backed-by-Hive 
gap you decided to accept.
   
   The replacement `CREATE TABLE IF NOT EXISTS %s (...)` is a no-op in this 
shared test - same lowercase name as the table created at :611, so 
`tableExists` is trivially true on every backend and the statement can never 
fail. It was added to exercise `tableExists` through an uppercase spelling, and 
with `%s` it does not.
   
   Suggest keeping `%S` on both statements here and overriding this test in the 
suites where the uppercase path is a known gap (the two REST-backed Iceberg 
suites), rather than lowercasing the shared base. That keeps the Hive-catalog 
and direct-Hive-Iceberg coverage, and makes the REST gap visible and 
attributable instead of silent. The new 
`SparkIcebergCatalogHiveBackendIT.testCaseInsensitiveTableNameAfterReplaceColumns`
 covers one of the four suites.
   
   Verified by: diffed this file against `origin/main`; read 
`testAlterTableReplaceColumns` in full (:607-645) and `supportsReplaceColumns` 
in all six overrides; `grep -rn "class SparkIcebergCatalog.*extends"` for the 
suite hierarchy; read the new test at 
`SparkIcebergCatalogHiveBackendIT.java:37-55`.



##########
catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogCapability.java:
##########
@@ -48,6 +60,15 @@ public CapabilityResult supportsHierarchicalSchema() {
     return CapabilityResult.SUPPORTED;
   }
 
+  @Override
+  public CapabilityResult caseSensitiveOnName(Scope scope) {
+    if (hiveBackend && (scope == Scope.SCHEMA || scope == Scope.TABLE || scope 
== Scope.VIEW)) {
+      return CapabilityResult.unsupported(
+          "The Iceberg Hive backend is case insensitive for schemas, tables, 
and views.");

Review Comment:
   [Important] Thanks for adding VIEW here. The same fold is still missing from 
the native Hive catalog, where this PR's new create-only view write makes the 
identical failure reachable.
   
   `HiveCatalogCapability.caseSensitiveOnName` folds only SCHEMA, TABLE and 
COLUMN (`HiveCatalogCapability.java:61-72`), so `Scope.VIEW` stays 
case-sensitive for `catalog-hive` - and `catalog-hive` does expose views 
(`HiveCatalogOperations.java:100` implements `ViewCatalog`, delegating to 
`HiveViewCatalogOperations`). A view created through Gravitino carries 
`gravitino.identifier` in its HMS table parameters, because 
`internalCreateView` injects the `StringIdentifier` into the properties handed 
to the catalog ops (`ViewOperationDispatcher.java:397-402`) and the Hive view 
ops round-trip properties as HMS parameters.
   
   So loading that view through an uppercase spelling: 
`ViewNormalizeDispatcher` leaves the name alone, HMS folds it and returns the 
view with its stored `gravitino.identifier`, the stored-entity lookup under the 
uppercase path misses (`internalLoadView` ... `withImported(viewEntity != 
null)`), `importView` reads the external id and the new create-only write at 
`ViewOperationDispatcher.java:605` conflicts, so `loadView` throws the 
manual-cleanup error (`ViewOperationDispatcher.java:152-170`). On `main` that 
same load silently reassigned the row - the bug this PR fixes - so the refusal 
is intended, but it leaves ordinary mixed-case view access against HMS broken 
for the Hive catalog in exactly the way you just fixed for Iceberg-on-Hive.
   
   Adding `case VIEW:` next to SCHEMA/TABLE/COLUMN in `HiveCatalogCapability` 
looks like the same one-line change. If Hive views are deliberately out of 
scope, saying so in this comment or the PR description would be enough.
   
   Verified by: read this method and `HiveCatalogCapability.java:60-72` on this 
branch; confirmed `catalog-hive` implements `ViewCatalog` at 
`HiveCatalogOperations.java:100` and `HiveViewCatalogOperations.java:62`; read 
`ViewOperationDispatcher.java:140-175`, `397-402` and `548-612`, and confirmed 
`git diff origin/main...HEAD` changes line 605 from `store.put(viewEntity, 
true)` to `false`; `grep -rn caseSensitiveOnName` over api/core/catalogs shows 
overrides only in Hive, Glue and Iceberg. The one step I did not execute is the 
HMS round-trip for a view name; it is the same TBLS namespace the table case in 
this PR relies on.



-- 
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