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

Reply via email to