This is an automated email from the ASF dual-hosted git repository.

jmclean pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new c15979fd54 [#10133] Fix TopicMetaService crash on malformed topic 
namespace (#10215)
c15979fd54 is described below

commit c15979fd54f479a67062d9ee34307f204f5f4518
Author: Ramesh Reddy Adutla 
<[email protected]>
AuthorDate: Wed Mar 4 23:09:00 2026 +0000

    [#10133] Fix TopicMetaService crash on malformed topic namespace (#10215)
    
    ### What changes were proposed in this pull request?
    
    Add namespace validation in `TopicMetaService`'s private methods that
    directly index into namespace levels, preventing
    `ArrayIndexOutOfBoundsException` on malformed input.
    
    ### Why are the changes needed?
    
    `TopicMetaService#getTopicPOByFullQualifiedName` and
    `listTopicPOsByFullQualifiedName` assume the topic namespace always has
    3 levels and directly access `namespaceLevels[0..2]`. For malformed
    input (fewer levels), this throws an uninformative
    `ArrayIndexOutOfBoundsException`.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No. Internal defensive validation — the external API is unchanged.
    Malformed identifiers now produce a clear `NoSuchEntityException`
    instead of `ArrayIndexOutOfBoundsException`.
    
    ### How was this patch tested?
    
    Added unit test
    
`testGetTopicByFullQualifiedNameMalformedNamespaceThrowsNoSuchEntityException`
    (as suggested in #10133) that:
    1. Uses reflection to call the private `getTopicPOByFullQualifiedName`
    method
    2. Passes a `NameIdentifier` with only 2-level namespace (missing
    schema)
    3. Asserts `NoSuchEntityException` is thrown (wrapped in
    `InvocationTargetException`)
    
    Closes #10133
    
    Co-authored-by: Copilot <[email protected]>
---
 .../relational/service/TopicMetaService.java       | 10 +++++++++
 .../relational/service/TestTopicMetaService.java   | 25 ++++++++++++++++++++++
 2 files changed, 35 insertions(+)

diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/TopicMetaService.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/TopicMetaService.java
index c443c7b668..ede75548b1 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/TopicMetaService.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/TopicMetaService.java
@@ -166,6 +166,10 @@ public class TopicMetaService {
   }
 
   private List<TopicPO> listTopicPOsByFullQualifiedName(Namespace namespace) {
+    if (namespace == null || namespace.length() != 3) {
+      throw new NoSuchEntityException(
+          "Topic namespace must have 3 levels, the input namespace is %s", 
namespace);
+    }
     String[] namespaceLevels = namespace.levels();
     List<TopicPO> topicPOs =
         SessionUtils.getWithoutCommit(
@@ -190,6 +194,12 @@ public class TopicMetaService {
   }
 
   private TopicPO getTopicPOByFullQualifiedName(NameIdentifier identifier) {
+    if (identifier == null
+        || identifier.namespace() == null
+        || identifier.namespace().length() != 3) {
+      throw new NoSuchEntityException(
+          "Topic identifier must have a 3-level namespace, the input 
identifier is %s", identifier);
+    }
     String[] namespaceLevels = identifier.namespace().levels();
     TopicPO topicPO =
         SessionUtils.getWithoutCommit(
diff --git 
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestTopicMetaService.java
 
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestTopicMetaService.java
index 14a1cdcf80..a8a3c6fc1f 100644
--- 
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestTopicMetaService.java
+++ 
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestTopicMetaService.java
@@ -19,14 +19,20 @@
 package org.apache.gravitino.storage.relational.service;
 
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.time.Instant;
 import java.util.List;
 import org.apache.gravitino.Entity;
 import org.apache.gravitino.EntityAlreadyExistsException;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.exceptions.NoSuchEntityException;
 import org.apache.gravitino.meta.TopicEntity;
 import org.apache.gravitino.storage.RandomIdGenerator;
 import org.apache.gravitino.storage.relational.TestJDBCBackend;
@@ -145,4 +151,23 @@ public class TestTopicMetaService extends TestJDBCBackend {
                 e ->
                     createTopicEntity(topicCopy.id(), topicCopy.namespace(), 
"topic", AUDIT_INFO)));
   }
+
+  @TestTemplate
+  public void 
testGetTopicByFullQualifiedNameMalformedNamespaceThrowsNoSuchEntityException()
+      throws Exception {
+    Method method =
+        TopicMetaService.class.getDeclaredMethod(
+            "getTopicPOByFullQualifiedName", NameIdentifier.class);
+    method.setAccessible(true);
+
+    NameIdentifier malformedIdentifier =
+        NameIdentifier.of(Namespace.of(metalakeName, catalogName), "topic");
+
+    InvocationTargetException invocationTargetException =
+        assertThrows(
+            InvocationTargetException.class,
+            () -> method.invoke(TopicMetaService.getInstance(), 
malformedIdentifier));
+
+    assertInstanceOf(NoSuchEntityException.class, 
invocationTargetException.getCause());
+  }
 }

Reply via email to