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());
+ }
}