This is an automated email from the ASF dual-hosted git repository.
roryqi pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/branch-1.3 by this push:
new e1866dd3b9 [Cherry-pick to branch-1.3] [#12887] fix(server): Validate
statistic name length (#12889) (#12894)
e1866dd3b9 is described below
commit e1866dd3b9a9885e83507b547f296236c8859abe
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Sep 4 09:32:00 2026 +0800
[Cherry-pick to branch-1.3] [#12887] fix(server): Validate statistic name
length (#12889) (#12894)
**Cherry-pick Information:**
- Original commit: 3b01c6ed10cf64f7d3a8a87607955c1d590ec4ee
- Target branch: `branch-1.3`
- Status: ✅ Clean cherry-pick (no conflicts)
Co-authored-by: roryqi <[email protected]>
---
.../java/org/apache/gravitino/stats/Statistic.java | 3 ++
.../server/web/rest/StatisticOperations.java | 45 +++++++++--------
.../server/web/rest/TestStatisticOperations.java | 58 ++++++++++++++++++++++
3 files changed, 86 insertions(+), 20 deletions(-)
diff --git a/api/src/main/java/org/apache/gravitino/stats/Statistic.java
b/api/src/main/java/org/apache/gravitino/stats/Statistic.java
index e54c82a0cb..c7e5f64aba 100644
--- a/api/src/main/java/org/apache/gravitino/stats/Statistic.java
+++ b/api/src/main/java/org/apache/gravitino/stats/Statistic.java
@@ -33,6 +33,9 @@ public interface Statistic extends Auditable {
/** The prefix for custom statistics. Custom statistics are user-defined
statistics. */
String CUSTOM_PREFIX = "custom-";
+ /** The maximum number of characters allowed in a statistic name. */
+ int MAX_NAME_LENGTH = 128;
+
/**
* Get the name of the statistic.
*
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
index e86ce980c8..a153dd691c 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
@@ -179,13 +179,7 @@ public class StatisticOperations {
Map<String, StatisticValue<?>> statisticMaps = Maps.newHashMap();
for (Map.Entry<String, StatisticValue<?>> entry :
request.getUpdates().entrySet()) {
- // Current we only support custom statistics
- if (!entry.getKey().startsWith(Statistic.CUSTOM_PREFIX)) {
- throw new IllegalStatisticNameException(
- "Statistic name must start with %s , but got: %s",
- Statistic.CUSTOM_PREFIX, entry.getKey());
- }
-
+ validateStatisticName(entry.getKey());
statisticMaps.put(entry.getKey(), entry.getValue());
}
@@ -388,18 +382,7 @@ public class StatisticOperations {
List<PartitionStatisticsUpdateDTO> updates = request.getUpdates();
for (PartitionStatisticsUpdateDTO update : updates) {
- update
- .statistics()
- .keySet()
- .forEach(
- statistic -> {
- if (!statistic.startsWith(Statistic.CUSTOM_PREFIX)) {
- // Current we only support custom statistics
- throw new IllegalStatisticNameException(
- "Statistic name must start with %s, but got: %s",
- Statistic.CUSTOM_PREFIX, statistic);
- }
- });
+
update.statistics().keySet().forEach(StatisticOperations::validateStatisticName);
}
MetadataObjectUtil.checkMetadataObject(metalake, object);
@@ -527,7 +510,29 @@ public class StatisticOperations {
return "";
}
- return StringUtils.join(request.getUpdates().keySet(), ",");
+ return request.getUpdates().keySet().stream()
+ .map(StatisticOperations::formatStatisticName)
+ .collect(Collectors.joining(","));
+ }
+
+ private static String formatStatisticName(String statisticName) {
+ if (statisticName != null && statisticName.length() >
Statistic.MAX_NAME_LENGTH) {
+ return String.format("<statistic name exceeds %d characters>",
Statistic.MAX_NAME_LENGTH);
+ }
+ return statisticName;
+ }
+
+ private static void validateStatisticName(String statisticName) {
+ if (statisticName.length() > Statistic.MAX_NAME_LENGTH) {
+ throw new IllegalStatisticNameException(
+ "Statistic name must not exceed %d characters",
Statistic.MAX_NAME_LENGTH);
+ }
+
+ // Currently we only support custom statistics.
+ if (!statisticName.startsWith(Statistic.CUSTOM_PREFIX)) {
+ throw new IllegalStatisticNameException(
+ "Statistic name must start with %s, but got: %s",
Statistic.CUSTOM_PREFIX, statisticName);
+ }
}
private static String getPartitionNames(PartitionStatisticsUpdateRequest
request) {
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestStatisticOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestStatisticOperations.java
index da591459de..887359c177 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestStatisticOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestStatisticOperations.java
@@ -38,6 +38,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.gravitino.Config;
import org.apache.gravitino.GravitinoEnv;
@@ -370,6 +371,63 @@ public class TestStatisticOperations extends
BaseOperationsTest {
UnmodifiableStatisticException.class.getSimpleName(),
errorResp4.getType());
}
+ @Test
+ public void testUpdateTableStatisticsNameLength() {
+ String maximumLengthName =
+ Statistic.CUSTOM_PREFIX
+ + StringUtils.repeat("x", Statistic.MAX_NAME_LENGTH -
Statistic.CUSTOM_PREFIX.length());
+ StatisticsUpdateRequest validRequest =
+ new StatisticsUpdateRequest(Map.of(maximumLengthName,
StatisticValues.longValue(1L)));
+ MetadataObject tableObject =
+ MetadataObjects.parse(
+ String.format("%s.%s.%s", catalog, schema, table),
MetadataObject.Type.TABLE);
+ when(tableDispatcher.tableExists(any())).thenReturn(true);
+
+ Response validResponse =
+ target(
+ "/metalakes/"
+ + metalake
+ + "/objects/"
+ + tableObject.type()
+ + "/"
+ + tableObject.fullName()
+ + "/statistics")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .put(entity(validRequest, MediaType.APPLICATION_JSON_TYPE));
+ Assertions.assertEquals(Response.Status.OK.getStatusCode(),
validResponse.getStatus());
+
+ String longName = maximumLengthName + "x";
+ StatisticsUpdateRequest request =
+ new StatisticsUpdateRequest(Map.of(longName,
StatisticValues.longValue(1L)));
+
+ Response response =
+ target(
+ "/metalakes/"
+ + metalake
+ + "/objects/"
+ + tableObject.type()
+ + "/"
+ + tableObject.fullName()
+ + "/statistics")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .put(entity(request, MediaType.APPLICATION_JSON_TYPE));
+
+ Assertions.assertEquals(Response.Status.BAD_REQUEST.getStatusCode(),
response.getStatus());
+ ErrorResponse errorResponse = response.readEntity(ErrorResponse.class);
+ Assertions.assertEquals(ErrorConstants.ILLEGAL_ARGUMENTS_CODE,
errorResponse.getCode());
+ Assertions.assertEquals(
+ IllegalStatisticNameException.class.getSimpleName(),
errorResponse.getType());
+ Assertions.assertTrue(
+ errorResponse
+ .getMessage()
+ .contains(
+ String.format(
+ "Statistic name must not exceed %d characters",
Statistic.MAX_NAME_LENGTH)));
+ Assertions.assertFalse(errorResponse.getMessage().contains(longName));
+ }
+
@Test
public void testUpdateTableStatisticsWithNullRequestBody() {
when(tableDispatcher.tableExists(any())).thenReturn(true);