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


##########
catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalog.java:
##########
@@ -86,7 +86,10 @@ static boolean shouldValidateWarehouseProperty(String 
backend, String warehouse)
 
   @Override
   public Capability newCapability() {
-    return new 
IcebergCatalogCapability(HierarchicalSchemaUtil.schemaSeparator());
+    String backend = 
entity().getProperties().get(IcebergConstants.CATALOG_BACKEND);
+    return new IcebergCatalogCapability(
+        HierarchicalSchemaUtil.schemaSeparator(),
+        IcebergCatalogBackend.HIVE.name().equalsIgnoreCase(backend));

Review Comment:
   Thanks. Resolving this through a REST-backed catalog would require 
discovering and propagating the downstream backend name semantics, which the 
current capability boundary cannot do safely. I scoped the mixed-case assertion 
to the direct Hive backend via supportsCaseInsensitiveTableNameAliases(); 
REST-backed catalogs keep the exact-case path. I suggest treating REST -> Hive 
case folding as a known limitation of this patch.



##########
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)) {
+      return CapabilityResult.unsupported(
+          "The Iceberg Hive backend is case insensitive for schemas and 
tables.");
+    }
+    return Capability.super.caseSensitiveOnName(scope);
+  }

Review Comment:
   Good catch. I added Scope.VIEW to the Hive-backend case-insensitive 
capability and added normalization assertions for both Hive and non-Hive 
backends in e8396991ba.



##########
core/src/test/java/org/apache/gravitino/catalog/TestTopicOperationDispatcher.java:
##########
@@ -166,14 +169,65 @@ public void testCreateAndLoadTopic() throws IOException {
                 
AuditInfo.builder().withCreator("gravitino").withCreateTime(Instant.now()).build())
             .build();
     doReturn(unmatchedEntity).when(entityStore).get(any(), 
eq(Entity.EntityType.TOPIC), any());
-    Topic loadedTopic4 = topicOperationDispatcher.loadTopic(topicIdent1);
-    // Succeed to import the topic entity
+    UnsupportedOperationException exception =
+        Assertions.assertThrows(
+            UnsupportedOperationException.class,
+            () -> topicOperationDispatcher.loadTopic(topicIdent1));
+    Assertions.assertTrue(exception.getMessage().contains("immutable Kafka 
topic UUID"));
+    verify(entityStore).put(any(TopicEntity.class), eq(false));
+    verify(entityStore).restoreSoftDeletedEntity(any(TopicEntity.class));
     reset(entityStore);
     TopicEntity topicEntity =
         entityStore.get(topicIdent1, Entity.EntityType.TOPIC, 
TopicEntity.class);
     Assertions.assertEquals("test", topicEntity.auditInfo().creator());
-    // Audit info is gotten from the catalog, not from the entity store
-    Assertions.assertEquals("test", loadedTopic4.auditInfo().creator());
+  }
+
+  @Test
+  public void testConcurrentImportTopicReusesExistingEntity() throws 
IOException {
+    Namespace topicNs = Namespace.of(metalake, catalog, 
"schema_topic_concurrent_import");
+    NameIdentifier schemaIdent = NameIdentifier.of(topicNs.levels());
+    schemaOperationDispatcher.createSchema(
+        schemaIdent, "comment", ImmutableMap.of("k1", "v1", "k2", "v2"));
+    NameIdentifier topicIdent = NameIdentifier.of(topicNs, 
"topic_concurrent_import");
+    createTopicForLockTest(topicIdent);
+    TopicEntity importedTopicEntity = entityStore.get(topicIdent, TOPIC, 
TopicEntity.class);
+
+    reset(entityStore);
+    doThrow(new NoSuchEntityException("mock error"))
+        .doThrow(new NoSuchEntityException("mock error"))
+        .doReturn(importedTopicEntity)
+        .when(entityStore)
+        .get(any(), eq(TOPIC), any());
+    doThrow(new EntityAlreadyExistsException("mock conflict"))
+        .when(entityStore)
+        .put(any(TopicEntity.class), eq(false));
+
+    Topic loadedTopic =
+        Assertions.assertDoesNotThrow(() -> 
topicOperationDispatcher.loadTopic(topicIdent));
+    Assertions.assertEquals(topicIdent.name(), loadedTopic.name());
+    verify(entityStore).restoreSoftDeletedEntity(any(TopicEntity.class));

Review Comment:
   Added an end-to-end Kafka regression test in e8396991ba. It creates and 
imports a real Kafka topic, drops the catalog with cascade, recreates the 
catalog against the same broker, and verifies loadTopic plus read/write 
succeeds through the dispatcher/backend restoration path.



##########
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/integration/test/SparkCommonIT.java:
##########
@@ -630,6 +630,7 @@ protected void testAlterTableReplaceColumns() {
 
     tableInfo = getTableInfo(tableName);
     checkTableColumns(tableName, updateColumns, tableInfo);
+    sql(String.format("CREATE TABLE IF NOT EXISTS %S (id int)", tableName));

Review Comment:
   Fixed in e8396991ba. The CREATE TABLE IF NOT EXISTS statement now uses the 
complete post-REPLACE COLUMNS schema (id, name2, and age) before inserting the 
three-column row.



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