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

jerryshao 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 39a19ea7c9 [Cherry-pick to branch-1.3] [#13312] fix(core): Validate 
name and comment length before persisting entities (#13322) (#13324)
39a19ea7c9 is described below

commit 39a19ea7c96cb9d3a7a19d9862f80b9155ef2c8a
Author: Jerry Shao <[email protected]>
AuthorDate: Fri Sep 18 20:39:59 2026 +0800

    [Cherry-pick to branch-1.3] [#13312] fix(core): Validate name and comment 
length before persisting entities (#13322) (#13324)
    
    **Cherry-pick Information:**
    - Original PR: #13322 (not merged yet; this backport should be merged
    after it)
    - Target branch: `branch-1.3`
    - Status: ⚠️ Cherry-picked with conflicts resolved
    
    **Conflict resolution:**
    - `TopicNormalizeDispatcher`: kept the branch-1.3-only array
    `normalizeCaseSensitive` helper alongside the new comment check.
    - `ModelCatalogOperationsIT`: dropped `assertPropertiesEqual`, a helper
    that only exists on main and came in with the patch context.
    - `TestFilesetMetaService`: kept as is on branch-1.3, since the rollback
    test changed on main does not exist here.
    - `TestFilesetCatalogOperations`: adapted the new tests to the
    branch-1.3 helpers (`createSchema` takes a test id, and
    `FilesetCatalogOperations` takes only the store).
    - `TestModelNormalizeDispatcher`: uses a lower-case schema name to match
    the name handling on branch-1.3.
    
    **Testing on branch-1.3:**
    - `./gradlew :core:test :server:test :catalogs:catalog-fileset:test
    :catalogs:catalog-model:test -PskipITs` passes.
    - `NameAndCommentLengthIT`, `MetalakeIT`, `AccessControlIT` and
    `ModelCatalogOperationsIT` pass in embedded mode on H2.
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    ---------
    
    Co-authored-by: Claude Opus 5 <[email protected]>
---
 .../catalog/fileset/FilesetCatalogOperations.java  |  11 +
 .../fileset/TestFilesetCatalogOperations.java      |  74 ++++++
 .../integration/test/ModelCatalogOperationsIT.java |  66 ++++++
 .../client/integration/test/MetalakeIT.java        |  30 +++
 .../integration/test/NameAndCommentLengthIT.java   | 214 +++++++++++++++++
 .../test/authorization/AccessControlIT.java        |  44 ++++
 .../src/main/java/org/apache/gravitino/Entity.java |   2 +-
 .../org/apache/gravitino/EntityFieldLimits.java    |  73 ++++++
 core/src/main/java/org/apache/gravitino/Field.java |  70 ++++++
 .../gravitino/catalog/CapabilityHelpers.java       |  21 ++
 .../catalog/ModelNormalizeDispatcher.java          |   6 +-
 .../catalog/TopicNormalizeDispatcher.java          |  14 ++
 .../org/apache/gravitino/meta/BaseMetalake.java    |   4 +-
 .../org/apache/gravitino/meta/CatalogEntity.java   |   6 +-
 .../org/apache/gravitino/meta/FilesetEntity.java   |   6 +-
 .../org/apache/gravitino/meta/GroupEntity.java     |   3 +-
 .../apache/gravitino/meta/JobTemplateEntity.java   |   4 +-
 .../apache/gravitino/meta/ModelVersionEntity.java  |   7 +
 .../org/apache/gravitino/meta/PolicyEntity.java    |   3 +-
 .../java/org/apache/gravitino/meta/RoleEntity.java |   3 +-
 .../org/apache/gravitino/meta/SchemaEntity.java    |   6 +-
 .../java/org/apache/gravitino/meta/TagEntity.java  |   6 +-
 .../org/apache/gravitino/meta/TopicEntity.java     |   6 +-
 .../java/org/apache/gravitino/meta/UserEntity.java |   3 +-
 .../converters/H2ExceptionConverter.java           |   5 +
 .../converters/MySQLExceptionConverter.java        |   5 +
 .../converters/PostgreSQLExceptionConverter.java   |   5 +
 .../converters/ValueTooLongExceptions.java         |  52 +++++
 .../test/java/org/apache/gravitino/TestField.java  |  26 +++
 .../catalog/TestModelNormalizeDispatcher.java      |  84 +++++++
 .../catalog/TestTopicNormalizeDispatcher.java      |  34 +++
 .../gravitino/meta/TestEntityFieldLimits.java      | 258 +++++++++++++++++++++
 .../converters/TestH2ExceptionConverter.java       |  15 ++
 .../converters/TestMySQLExceptionConverter.java    |  16 ++
 .../TestPostgreSQLExceptionConverter.java          |  16 ++
 .../org/apache/gravitino/tag/TestTagManager.java   |  45 ++++
 36 files changed, 1229 insertions(+), 14 deletions(-)

diff --git 
a/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogOperations.java
 
b/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogOperations.java
index ac75ec03c2..b725c27125 100644
--- 
a/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogOperations.java
+++ 
b/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogOperations.java
@@ -68,6 +68,7 @@ import java.util.stream.Collectors;
 import javax.annotation.Nullable;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.EntityStore;
 import org.apache.gravitino.GravitinoEnv;
 import org.apache.gravitino.NameIdentifier;
@@ -430,6 +431,11 @@ public class FilesetCatalogOperations extends 
ManagedSchemaOperations
       Map<String, String> storageLocations,
       Map<String, String> properties)
       throws NoSuchSchemaException, FilesetAlreadyExistsException {
+    // Check the comment before the storage locations are created, the entity 
validation only
+    // happens after that.
+    EntityFieldLimits.checkMaxLength(
+        comment, EntityFieldLimits.MAX_COMMENT_LENGTH, "comment", 
Entity.EntityType.FILESET);
+
     storageLocations.forEach(
         (name, path) -> {
           if (StringUtils.isBlank(name)) {
@@ -755,6 +761,11 @@ public class FilesetCatalogOperations extends 
ManagedSchemaOperations
   @Override
   public Schema createSchema(NameIdentifier ident, String comment, Map<String, 
String> properties)
       throws NoSuchCatalogException, SchemaAlreadyExistsException {
+    // Check the comment before the schema directories are created, the entity 
validation only
+    // happens after that.
+    EntityFieldLimits.checkMaxLength(
+        comment, EntityFieldLimits.MAX_COMMENT_LENGTH, "comment", 
Entity.EntityType.SCHEMA);
+
     if (disableFSOps) {
       return super.createSchema(ident, comment, properties);
     }
diff --git 
a/catalogs/catalog-fileset/src/test/java/org/apache/gravitino/catalog/fileset/TestFilesetCatalogOperations.java
 
b/catalogs/catalog-fileset/src/test/java/org/apache/gravitino/catalog/fileset/TestFilesetCatalogOperations.java
index 3860e6f58e..d3f670070b 100644
--- 
a/catalogs/catalog-fileset/src/test/java/org/apache/gravitino/catalog/fileset/TestFilesetCatalogOperations.java
+++ 
b/catalogs/catalog-fileset/src/test/java/org/apache/gravitino/catalog/fileset/TestFilesetCatalogOperations.java
@@ -69,9 +69,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.reflect.FieldUtils;
 import org.apache.gravitino.Config;
 import org.apache.gravitino.Configs;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.EntityStore;
 import org.apache.gravitino.EntityStoreFactory;
 import org.apache.gravitino.GravitinoEnv;
@@ -432,6 +434,26 @@ public class TestFilesetCatalogOperations {
         exception.getMessage());
   }
 
+  @Test
+  public void testCreateSchemaWithTooLongComment() throws IOException {
+    final long testId = generateTestId();
+    String name = "schema" + testId;
+    String schemaPath = TEST_ROOT_PATH + "/" + name;
+    String tooLongComment = StringUtils.repeat("a", 
EntityFieldLimits.MAX_COMMENT_LENGTH + 1);
+
+    IllegalArgumentException exception =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () -> createSchema(testId, name, tooLongComment, null, 
schemaPath));
+    Assertions.assertEquals(
+        "The comment of the schema must not exceed 256 characters", 
exception.getMessage());
+
+    // The schema directory must not be created for a rejected schema.
+    Path path = new Path(schemaPath);
+    FileSystem fs = path.getFileSystem(new Configuration());
+    Assertions.assertFalse(fs.exists(path));
+  }
+
   @Test
   public void testCreateSchemaWithCatalogLocation() throws IOException {
     final long testId = generateTestId();
@@ -1264,6 +1286,58 @@ public class TestFilesetCatalogOperations {
     }
   }
 
+  @Test
+  public void testCreateFilesetWithTooLongComment() throws IOException {
+    final long testId = generateTestId();
+    final String schemaName = "schema" + testId;
+    final String name = "fileset" + testId;
+    final String schemaPath = TEST_ROOT_PATH + "/" + schemaName;
+    createSchema(testId, schemaName, "comment", null, schemaPath);
+
+    String tooLongComment = StringUtils.repeat("a", 
EntityFieldLimits.MAX_COMMENT_LENGTH + 1);
+    IllegalArgumentException exception =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () ->
+                createFileset(name, schemaName, tooLongComment, 
Fileset.Type.MANAGED, null, null));
+    Assertions.assertEquals(
+        "The comment of the fileset must not exceed 256 characters", 
exception.getMessage());
+
+    // The fileset directory must not be created for a rejected fileset.
+    Path filesetPath = new Path(schemaPath, name);
+    FileSystem fs = filesetPath.getFileSystem(new Configuration());
+    Assertions.assertFalse(fs.exists(filesetPath));
+
+    createFileset(name, schemaName, "comment", Fileset.Type.MANAGED, null, 
null);
+    Assertions.assertTrue(fs.exists(filesetPath));
+  }
+
+  @Test
+  public void testUpdateFilesetCommentTooLong() throws IOException {
+    final long testId = generateTestId();
+    final String schemaName = "schema" + testId;
+    final String comment = "comment" + testId;
+    final String name = "fileset" + testId;
+    final String schemaPath = TEST_ROOT_PATH + "/" + schemaName;
+
+    createSchema(testId, schemaName, comment, null, schemaPath);
+    createFileset(name, schemaName, comment, Fileset.Type.MANAGED, null, null);
+
+    String tooLongComment = StringUtils.repeat("a", 
EntityFieldLimits.MAX_COMMENT_LENGTH + 1);
+    try (FilesetCatalogOperations ops = new FilesetCatalogOperations(store)) {
+      ops.initialize(Maps.newHashMap(), randomCatalogInfo(), 
FILESET_PROPERTIES_METADATA);
+      NameIdentifier filesetIdent = NameIdentifier.of("m1", "c1", schemaName, 
name);
+
+      IllegalArgumentException exception =
+          Assertions.assertThrows(
+              IllegalArgumentException.class,
+              () -> ops.alterFileset(filesetIdent, 
FilesetChange.updateComment(tooLongComment)));
+      Assertions.assertEquals(
+          "The comment of the fileset must not exceed 256 characters", 
exception.getMessage());
+      Assertions.assertEquals(comment, 
ops.loadFileset(filesetIdent).comment());
+    }
+  }
+
   @Test
   public void testRemoveFilesetComment() throws IOException {
     final long testId = generateTestId();
diff --git 
a/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/integration/test/ModelCatalogOperationsIT.java
 
b/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/integration/test/ModelCatalogOperationsIT.java
index 52bf115c41..099c18ef62 100644
--- 
a/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/integration/test/ModelCatalogOperationsIT.java
+++ 
b/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/integration/test/ModelCatalogOperationsIT.java
@@ -26,6 +26,7 @@ import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.Catalog;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
@@ -1258,6 +1259,71 @@ public class ModelCatalogOperationsIT extends BaseIT {
         "u2", 
gravitinoCatalog.asModelCatalog().getModelVersionUri(modelIdent1, "alias3", 
null));
   }
 
+  @Test
+  public void testRenameModelWithIllegalName() {
+    String modelName = RandomNameUtils.genRandomName("model_rename_spec");
+    NameIdentifier modelIdent = NameIdentifier.of(schemaName, modelName);
+    gravitinoCatalog.asModelCatalog().registerModel(modelIdent, null, null);
+
+    String tooLongName = StringUtils.repeat("m", 129);
+    IllegalArgumentException e =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () ->
+                gravitinoCatalog
+                    .asModelCatalog()
+                    .alterModel(modelIdent, ModelChange.rename(tooLongName)));
+    Assertions.assertTrue(e.getMessage().contains("is illegal"), 
e.getMessage());
+    
Assertions.assertTrue(gravitinoCatalog.asModelCatalog().modelExists(modelIdent));
+  }
+
+  @Test
+  public void testModelVersionAliasLength() {
+    String modelName = RandomNameUtils.genRandomName("model_alias_length");
+    NameIdentifier modelIdent = NameIdentifier.of(schemaName, modelName);
+    gravitinoCatalog.asModelCatalog().registerModel(modelIdent, null, null);
+
+    String tooLongAlias = StringUtils.repeat("a", 129);
+    IllegalArgumentException e =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () ->
+                gravitinoCatalog
+                    .asModelCatalog()
+                    .linkModelVersion(
+                        modelIdent, "uri", new String[] {tooLongAlias}, 
"comment", null));
+    Assertions.assertTrue(
+        e.getMessage().contains("The alias of the model version must not 
exceed 128 characters"),
+        e.getMessage());
+    Assertions.assertEquals(
+        0, 
gravitinoCatalog.asModelCatalog().listModelVersions(modelIdent).length);
+
+    String maxLengthAlias = StringUtils.repeat("a", 128);
+    gravitinoCatalog
+        .asModelCatalog()
+        .linkModelVersion(modelIdent, "uri", new String[] {maxLengthAlias}, 
"comment", null);
+    Assertions.assertArrayEquals(
+        new String[] {maxLengthAlias},
+        gravitinoCatalog.asModelCatalog().getModelVersion(modelIdent, 
0).aliases());
+
+    e =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () ->
+                gravitinoCatalog
+                    .asModelCatalog()
+                    .alterModelVersion(
+                        modelIdent,
+                        0,
+                        ModelVersionChange.updateAliases(new String[] 
{tooLongAlias}, null)));
+    Assertions.assertTrue(
+        e.getMessage().contains("The alias of the model version must not 
exceed 128 characters"),
+        e.getMessage());
+    Assertions.assertArrayEquals(
+        new String[] {maxLengthAlias},
+        gravitinoCatalog.asModelCatalog().getModelVersion(modelIdent, 
0).aliases());
+  }
+
   private void createMetalake() {
     GravitinoMetalake[] gravitinoMetalakes = client.listMetalakes();
     Assertions.assertEquals(0, gravitinoMetalakes.length);
diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/MetalakeIT.java
 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/MetalakeIT.java
index 7911f02cdf..0fb415b258 100644
--- 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/MetalakeIT.java
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/MetalakeIT.java
@@ -30,6 +30,7 @@ import com.google.common.collect.ImmutableMap;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.Catalog;
 import org.apache.gravitino.CatalogChange;
 import org.apache.gravitino.MetalakeChange;
@@ -357,4 +358,33 @@ public class MetalakeIT extends BaseIT {
       Assertions.assertFalse(client.dropMetalake(metalake.name()));
     }
   }
+
+  @Test
+  public void testMetalakeCommentLength() {
+    String tooLongComment = StringUtils.repeat("c", 257);
+
+    IllegalArgumentException e =
+        assertThrows(
+            IllegalArgumentException.class,
+            () -> client.createMetalake(metalakeNameA, tooLongComment, 
Collections.emptyMap()));
+    assertTrue(
+        e.getMessage().contains("The comment of the metalake must not exceed 
256 characters"),
+        e.getMessage());
+    assertThrows(NoSuchMetalakeException.class, () -> 
client.loadMetalake(metalakeNameA));
+
+    String maxLengthComment = StringUtils.repeat("c", 256);
+    GravitinoMetalake metalake =
+        client.createMetalake(metalakeNameA, maxLengthComment, 
Collections.emptyMap());
+    assertEquals(maxLengthComment, metalake.comment());
+
+    e =
+        assertThrows(
+            IllegalArgumentException.class,
+            () ->
+                client.alterMetalake(metalakeNameA, 
MetalakeChange.updateComment(tooLongComment)));
+    assertTrue(
+        e.getMessage().contains("The comment of the metalake must not exceed 
256 characters"),
+        e.getMessage());
+    assertEquals(maxLengthComment, 
client.loadMetalake(metalakeNameA).comment());
+  }
 }
diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/NameAndCommentLengthIT.java
 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/NameAndCommentLengthIT.java
new file mode 100644
index 0000000000..d0b7c69161
--- /dev/null
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/NameAndCommentLengthIT.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.client.integration.test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Collections;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.CatalogChange;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.client.GravitinoMetalake;
+import org.apache.gravitino.exceptions.NoSuchTagException;
+import org.apache.gravitino.file.Fileset;
+import org.apache.gravitino.file.FilesetChange;
+import org.apache.gravitino.integration.test.util.BaseIT;
+import org.apache.gravitino.integration.test.util.GravitinoITUtils;
+import org.apache.gravitino.policy.Policy;
+import org.apache.gravitino.policy.PolicyChange;
+import org.apache.gravitino.policy.PolicyContent;
+import org.apache.gravitino.policy.PolicyContents;
+import org.apache.gravitino.tag.Tag;
+import org.apache.gravitino.tag.TagChange;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+
+/**
+ * Verifies that names and comments longer than their metadata store columns 
are rejected with a
+ * clear error instead of a database error, on every metadata store backend.
+ */
+public class NameAndCommentLengthIT extends BaseIT {
+
+  private static final String TOO_LONG_NAME = StringUtils.repeat("n", 129);
+  private static final String MAX_LENGTH_NAME = StringUtils.repeat("n", 128);
+  private static final String TOO_LONG_COMMENT = StringUtils.repeat("c", 257);
+  private static final String MAX_LENGTH_COMMENT = StringUtils.repeat("c", 
256);
+
+  private final String metalakeName = 
GravitinoITUtils.genRandomName("length_it_metalake");
+  private GravitinoMetalake metalake;
+  private File localStorage;
+
+  @BeforeAll
+  public void setUp() throws IOException {
+    metalake = client.createMetalake(metalakeName, "comment", 
Collections.emptyMap());
+    localStorage = Files.createTempDirectory("length_it_storage").toFile();
+  }
+
+  @AfterAll
+  public void tearDown() throws IOException {
+    client.dropMetalake(metalakeName, true);
+    FileUtils.deleteDirectory(localStorage);
+  }
+
+  @Test
+  public void testTagNameAndCommentLength() {
+    assertTooLong(
+        "The name of the tag must not exceed 128 characters",
+        () -> metalake.createTag(TOO_LONG_NAME, "comment", 
Collections.emptyMap()));
+
+    String tagName = GravitinoITUtils.genRandomName("length_it_tag");
+    assertTooLong(
+        "The comment of the tag must not exceed 256 characters",
+        () -> metalake.createTag(tagName, TOO_LONG_COMMENT, 
Collections.emptyMap()));
+    Assertions.assertThrows(NoSuchTagException.class, () -> 
metalake.getTag(tagName));
+
+    Tag tag = metalake.createTag(MAX_LENGTH_NAME, MAX_LENGTH_COMMENT, 
Collections.emptyMap());
+    Assertions.assertEquals(MAX_LENGTH_NAME, tag.name());
+    Assertions.assertEquals(MAX_LENGTH_COMMENT, tag.comment());
+
+    assertTooLong(
+        "The name of the tag must not exceed 128 characters",
+        () -> metalake.alterTag(MAX_LENGTH_NAME, 
TagChange.rename(TOO_LONG_NAME)));
+    assertTooLong(
+        "The comment of the tag must not exceed 256 characters",
+        () -> metalake.alterTag(MAX_LENGTH_NAME, 
TagChange.updateComment(TOO_LONG_COMMENT)));
+    Assertions.assertEquals(MAX_LENGTH_COMMENT, 
metalake.getTag(MAX_LENGTH_NAME).comment());
+
+    Assertions.assertTrue(metalake.deleteTag(MAX_LENGTH_NAME));
+  }
+
+  @Test
+  public void testPolicyNameLength() {
+    PolicyContent content =
+        PolicyContents.custom(
+            ImmutableMap.of("rule1", "value1"), 
ImmutableSet.of(MetadataObject.Type.TABLE), null);
+
+    assertTooLong(
+        "The name of the policy must not exceed 128 characters",
+        () -> metalake.createPolicy(TOO_LONG_NAME, "custom", "comment", true, 
content));
+
+    Policy policy = metalake.createPolicy(MAX_LENGTH_NAME, "custom", 
"comment", true, content);
+    Assertions.assertEquals(MAX_LENGTH_NAME, policy.name());
+
+    assertTooLong(
+        "The name of the policy must not exceed 128 characters",
+        () -> metalake.alterPolicy(MAX_LENGTH_NAME, 
PolicyChange.rename(TOO_LONG_NAME)));
+    Assertions.assertEquals(MAX_LENGTH_NAME, 
metalake.getPolicy(MAX_LENGTH_NAME).name());
+
+    Assertions.assertTrue(metalake.deletePolicy(MAX_LENGTH_NAME));
+  }
+
+  @Test
+  public void testCatalogCommentLength() {
+    String catalogName = GravitinoITUtils.genRandomName("length_it_catalog");
+
+    assertTooLong(
+        "The comment of the catalog must not exceed 256 characters",
+        () -> createFilesetCatalog(catalogName, TOO_LONG_COMMENT));
+    Assertions.assertFalse(metalake.catalogExists(catalogName));
+
+    Catalog catalog = createFilesetCatalog(catalogName, MAX_LENGTH_COMMENT);
+    Assertions.assertEquals(MAX_LENGTH_COMMENT, catalog.comment());
+
+    assertTooLong(
+        "The comment of the catalog must not exceed 256 characters",
+        () -> metalake.alterCatalog(catalogName, 
CatalogChange.updateComment(TOO_LONG_COMMENT)));
+    Assertions.assertEquals(MAX_LENGTH_COMMENT, 
metalake.loadCatalog(catalogName).comment());
+
+    metalake.dropCatalog(catalogName, true);
+  }
+
+  @Test
+  public void testSchemaAndFilesetCommentLength() {
+    String catalogName = 
GravitinoITUtils.genRandomName("length_it_fileset_catalog");
+    Catalog catalog = createFilesetCatalog(catalogName, "comment");
+
+    // A rejected schema must not leave its directory behind.
+    String schemaName = GravitinoITUtils.genRandomName("length_it_schema");
+    assertTooLong(
+        "The comment of the schema must not exceed 256 characters",
+        () -> catalog.asSchemas().createSchema(schemaName, TOO_LONG_COMMENT, 
null));
+    Assertions.assertFalse(catalog.asSchemas().schemaExists(schemaName));
+    Assertions.assertFalse(new File(localStorage, catalogName + "/" + 
schemaName).exists());
+
+    catalog.asSchemas().createSchema(schemaName, MAX_LENGTH_COMMENT, null);
+    Assertions.assertTrue(new File(localStorage, catalogName + "/" + 
schemaName).exists());
+    Assertions.assertEquals(
+        MAX_LENGTH_COMMENT, 
catalog.asSchemas().loadSchema(schemaName).comment());
+
+    // A rejected fileset must not leave its directory behind.
+    NameIdentifier filesetIdent =
+        NameIdentifier.of(schemaName, 
GravitinoITUtils.genRandomName("length_it_fileset"));
+    assertTooLong(
+        "The comment of the fileset must not exceed 256 characters",
+        () ->
+            catalog
+                .asFilesetCatalog()
+                .createFileset(filesetIdent, TOO_LONG_COMMENT, 
Fileset.Type.MANAGED, null, null));
+    
Assertions.assertFalse(catalog.asFilesetCatalog().filesetExists(filesetIdent));
+    Assertions.assertFalse(
+        new File(localStorage, catalogName + "/" + schemaName + "/" + 
filesetIdent.name())
+            .exists());
+
+    Fileset fileset =
+        catalog
+            .asFilesetCatalog()
+            .createFileset(filesetIdent, MAX_LENGTH_COMMENT, 
Fileset.Type.MANAGED, null, null);
+    Assertions.assertEquals(MAX_LENGTH_COMMENT, fileset.comment());
+    Assertions.assertTrue(
+        new File(localStorage, catalogName + "/" + schemaName + "/" + 
filesetIdent.name())
+            .exists());
+
+    assertTooLong(
+        "The comment of the fileset must not exceed 256 characters",
+        () ->
+            catalog
+                .asFilesetCatalog()
+                .alterFileset(filesetIdent, 
FilesetChange.updateComment(TOO_LONG_COMMENT)));
+    Assertions.assertEquals(
+        MAX_LENGTH_COMMENT, 
catalog.asFilesetCatalog().loadFileset(filesetIdent).comment());
+
+    metalake.dropCatalog(catalogName, true);
+  }
+
+  private Catalog createFilesetCatalog(String catalogName, String comment) {
+    String location = localStorage.toURI() + catalogName;
+    return metalake.createCatalog(
+        catalogName,
+        Catalog.Type.FILESET,
+        "hadoop",
+        comment,
+        ImmutableMap.of("location", location));
+  }
+
+  private static void assertTooLong(String expectedMessage, Executable 
executable) {
+    IllegalArgumentException e =
+        Assertions.assertThrows(IllegalArgumentException.class, executable);
+    Assertions.assertTrue(e.getMessage().contains(expectedMessage), 
e.getMessage());
+  }
+}
diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/AccessControlIT.java
 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/AccessControlIT.java
index dfcb0f8516..2522ab5e53 100644
--- 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/AccessControlIT.java
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/AccessControlIT.java
@@ -27,6 +27,7 @@ import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.Catalog;
 import org.apache.gravitino.Configs;
 import org.apache.gravitino.MetadataObject;
@@ -736,4 +737,47 @@ public class AccessControlIT extends BaseIT {
     // Cleanup.
     metalake.deleteRole(roleName);
   }
+
+  @Test
+  void testUserGroupAndRoleNameLength() {
+    String tooLongName = StringUtils.repeat("n", 129);
+    String maxLengthName = StringUtils.repeat("n", 128);
+
+    IllegalArgumentException e =
+        Assertions.assertThrows(
+            IllegalArgumentException.class, () -> 
metalake.addUser(tooLongName));
+    Assertions.assertTrue(
+        e.getMessage().contains("The name of the user must not exceed 128 
characters"),
+        e.getMessage());
+    Assertions.assertEquals(maxLengthName, 
metalake.addUser(maxLengthName).name());
+    Assertions.assertTrue(metalake.removeUser(maxLengthName));
+
+    e =
+        Assertions.assertThrows(
+            IllegalArgumentException.class, () -> 
metalake.addGroup(tooLongName));
+    Assertions.assertTrue(
+        e.getMessage().contains("The name of the group must not exceed 128 
characters"),
+        e.getMessage());
+    Assertions.assertEquals(maxLengthName, 
metalake.addGroup(maxLengthName).name());
+    Assertions.assertTrue(metalake.removeGroup(maxLengthName));
+
+    SecurableObject metalakeObject =
+        SecurableObjects.ofMetalake(
+            metalakeName, 
Lists.newArrayList(Privileges.CreateCatalog.allow()));
+    e =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () ->
+                metalake.createRole(
+                    tooLongName, Collections.emptyMap(), 
Lists.newArrayList(metalakeObject)));
+    Assertions.assertTrue(
+        e.getMessage().contains("The name of the role must not exceed 128 
characters"),
+        e.getMessage());
+    Assertions.assertEquals(
+        maxLengthName,
+        metalake
+            .createRole(maxLengthName, Collections.emptyMap(), 
Lists.newArrayList(metalakeObject))
+            .name());
+    Assertions.assertTrue(metalake.deleteRole(maxLengthName));
+  }
 }
diff --git a/core/src/main/java/org/apache/gravitino/Entity.java 
b/core/src/main/java/org/apache/gravitino/Entity.java
index 3447b225b3..771f2e7f75 100644
--- a/core/src/main/java/org/apache/gravitino/Entity.java
+++ b/core/src/main/java/org/apache/gravitino/Entity.java
@@ -91,7 +91,7 @@ public interface Entity extends Serializable {
    * @throws IllegalArgumentException If the validation fails.
    */
   default void validate() throws IllegalArgumentException {
-    fields().forEach(Field::validate);
+    fields().forEach((field, value) -> field.validate(value, type()));
   }
 
   /**
diff --git a/core/src/main/java/org/apache/gravitino/EntityFieldLimits.java 
b/core/src/main/java/org/apache/gravitino/EntityFieldLimits.java
new file mode 100644
index 0000000000..8c7edab14c
--- /dev/null
+++ b/core/src/main/java/org/apache/gravitino/EntityFieldLimits.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino;
+
+import java.util.Locale;
+import javax.annotation.Nullable;
+
+/**
+ * The maximum lengths of entity fields persisted by the relational entity 
store.
+ *
+ * <p>These values must match the column definitions in {@code
+ * scripts/{h2,mysql,postgresql}/schema-*.sql}, so that values which cannot be 
stored are rejected
+ * before they reach the database.
+ */
+public final class EntityFieldLimits {
+
+  /** The maximum number of characters of an entity name or a model version 
alias. */
+  public static final int MAX_NAME_LENGTH = 128;
+
+  /** The maximum number of characters of an entity comment stored in a 
256-character column. */
+  public static final int MAX_COMMENT_LENGTH = 256;
+
+  private EntityFieldLimits() {}
+
+  /**
+   * Checks that a value does not exceed the given maximum number of 
characters, counted in Unicode
+   * code points.
+   *
+   * @param value The value to check, a null value always passes.
+   * @param maxLength The maximum number of characters allowed.
+   * @param fieldName The name of the field, used in the error message.
+   * @param entityType The type of the entity owning the field, used in the 
error message.
+   * @throws IllegalArgumentException If the value exceeds the maximum length.
+   */
+  public static void checkMaxLength(
+      @Nullable String value,
+      int maxLength,
+      String fieldName,
+      @Nullable Entity.EntityType entityType) {
+    // Count code points rather than UTF-16 chars, since MySQL (utf8mb4) and 
PostgreSQL count a
+    // supplementary character such as an emoji as one character.
+    if (value != null && value.codePointCount(0, value.length()) > maxLength) {
+      throw new IllegalArgumentException(exceedMaxLengthMessage(fieldName, 
entityType, maxLength));
+    }
+  }
+
+  private static String exceedMaxLengthMessage(
+      String fieldName, @Nullable Entity.EntityType entityType, int maxLength) 
{
+    if (entityType == null) {
+      return String.format("Field %s must not exceed %d characters", 
fieldName, maxLength);
+    }
+
+    return String.format(
+        "The %s of the %s must not exceed %d characters",
+        fieldName, entityType.name().toLowerCase(Locale.ROOT).replace('_', ' 
'), maxLength);
+  }
+}
diff --git a/core/src/main/java/org/apache/gravitino/Field.java 
b/core/src/main/java/org/apache/gravitino/Field.java
index 9306f61e10..2aba02e150 100644
--- a/core/src/main/java/org/apache/gravitino/Field.java
+++ b/core/src/main/java/org/apache/gravitino/Field.java
@@ -18,12 +18,15 @@
  */
 package org.apache.gravitino;
 
+import javax.annotation.Nullable;
 import lombok.EqualsAndHashCode;
 
 /** This class represents a field in the Apache Gravitino framework. */
 @EqualsAndHashCode
 public class Field {
 
+  private static final int UNLIMITED_LENGTH = -1;
+
   private String fieldName;
 
   private Class<?> typeClass;
@@ -32,6 +35,8 @@ public class Field {
 
   private boolean optional;
 
+  private int maxLength = UNLIMITED_LENGTH;
+
   private Field() {}
 
   /**
@@ -66,6 +71,40 @@ public class Field {
         .build();
   }
 
+  /**
+   * Creates a required String field instance whose value must not exceed the 
given length.
+   *
+   * @param fieldName The name of the field.
+   * @param description The description of the field.
+   * @param maxLength The maximum number of characters of the field value.
+   * @return A required Field instance.
+   */
+  public static Field required(String fieldName, String description, int 
maxLength) {
+    return new Builder(false)
+        .withName(fieldName)
+        .withTypeClass(String.class)
+        .withDescription(description)
+        .withMaxLength(maxLength)
+        .build();
+  }
+
+  /**
+   * Creates an optional String field instance whose value must not exceed the 
given length.
+   *
+   * @param fieldName The name of the field.
+   * @param description The description of the field.
+   * @param maxLength The maximum number of characters of the field value.
+   * @return An optional Field instance.
+   */
+  public static Field optional(String fieldName, String description, int 
maxLength) {
+    return new Builder(true)
+        .withName(fieldName)
+        .withTypeClass(String.class)
+        .withDescription(description)
+        .withMaxLength(maxLength)
+        .build();
+  }
+
   /**
    * Creates a required field instance.
    *
@@ -96,6 +135,18 @@ public class Field {
    * @throws IllegalArgumentException If the field value is invalid.
    */
   public <T> void validate(T fieldValue) {
+    validate(fieldValue, null);
+  }
+
+  /**
+   * Validates a field value according to the field's requirements.
+   *
+   * @param fieldValue The value to be validated.
+   * @param entityType The type of the entity owning the field, used in the 
error message.
+   * @param <T> The type of the field value.
+   * @throws IllegalArgumentException If the field value is invalid.
+   */
+  public <T> void validate(T fieldValue, @Nullable Entity.EntityType 
entityType) {
     if (fieldValue == null && !optional) {
       throw new IllegalArgumentException("Field " + fieldName + " is 
required");
     }
@@ -104,6 +155,10 @@ public class Field {
       throw new IllegalArgumentException(
           "Field " + fieldName + " is not of type " + typeClass.getName());
     }
+
+    if (maxLength != UNLIMITED_LENGTH && fieldValue instanceof String) {
+      EntityFieldLimits.checkMaxLength((String) fieldValue, maxLength, 
fieldName, entityType);
+    }
   }
 
   /** Builder class for creating Field instances. */
@@ -153,6 +208,17 @@ public class Field {
       return this;
     }
 
+    /**
+     * Sets the maximum number of characters of the field value. It only 
applies to String values.
+     *
+     * @param maxLength The maximum number of characters of the field value.
+     * @return The Builder instance.
+     */
+    public Builder withMaxLength(int maxLength) {
+      field.maxLength = maxLength;
+      return this;
+    }
+
     /**
      * Builds and returns the configured Field instance.
      *
@@ -168,6 +234,10 @@ public class Field {
         throw new IllegalArgumentException("Field type class is required");
       }
 
+      if (field.maxLength != UNLIMITED_LENGTH && field.maxLength <= 0) {
+        throw new IllegalArgumentException("Field max length must be 
positive");
+      }
+
       return field;
     }
   }
diff --git 
a/core/src/main/java/org/apache/gravitino/catalog/CapabilityHelpers.java 
b/core/src/main/java/org/apache/gravitino/catalog/CapabilityHelpers.java
index e29f2596af..39c1facc49 100644
--- a/core/src/main/java/org/apache/gravitino/catalog/CapabilityHelpers.java
+++ b/core/src/main/java/org/apache/gravitino/catalog/CapabilityHelpers.java
@@ -29,6 +29,7 @@ import org.apache.gravitino.Namespace;
 import org.apache.gravitino.connector.capability.Capability;
 import org.apache.gravitino.exceptions.NoSuchCatalogException;
 import org.apache.gravitino.file.FilesetChange;
+import org.apache.gravitino.model.ModelChange;
 import org.apache.gravitino.rel.Column;
 import org.apache.gravitino.rel.TableChange;
 import org.apache.gravitino.rel.ViewChange;
@@ -96,6 +97,18 @@ public class CapabilityHelpers {
         .toArray(FilesetChange[]::new);
   }
 
+  public static ModelChange[] applyCapabilities(Capability capabilities, 
ModelChange... changes) {
+    return Arrays.stream(changes)
+        .map(
+            change -> {
+              if (change instanceof ModelChange.RenameModel) {
+                return applyCapabilities((ModelChange.RenameModel) change, 
capabilities);
+              }
+              return change;
+            })
+        .toArray(ModelChange[]::new);
+  }
+
   public static ViewChange[] applyCapabilities(Capability capabilities, 
ViewChange... changes) {
     return Arrays.stream(changes)
         .map(
@@ -357,6 +370,14 @@ public class CapabilityHelpers {
     return FilesetChange.rename(newName);
   }
 
+  private static ModelChange applyCapabilities(
+      ModelChange.RenameModel renameModel, Capability capabilities) {
+    applyNameSpecification(Capability.Scope.MODEL, renameModel.newName(), 
capabilities);
+    String newName =
+        applyCaseSensitiveOnName(Capability.Scope.MODEL, 
renameModel.newName(), capabilities);
+    return ModelChange.rename(newName);
+  }
+
   private static ViewChange applyCapabilities(
       ViewChange.RenameView renameView, Capability capabilities) {
     String newName =
diff --git 
a/core/src/main/java/org/apache/gravitino/catalog/ModelNormalizeDispatcher.java 
b/core/src/main/java/org/apache/gravitino/catalog/ModelNormalizeDispatcher.java
index 5bf32f892b..f0c7a38e98 100644
--- 
a/core/src/main/java/org/apache/gravitino/catalog/ModelNormalizeDispatcher.java
+++ 
b/core/src/main/java/org/apache/gravitino/catalog/ModelNormalizeDispatcher.java
@@ -152,7 +152,11 @@ public class ModelNormalizeDispatcher implements 
ModelDispatcher {
   @Override
   public Model alterModel(NameIdentifier ident, ModelChange... changes)
       throws NoSuchModelException, IllegalArgumentException {
-    return dispatcher.alterModel(normalizeCaseSensitive(ident), changes);
+    Capability capability = getCapability(ident, catalogManager);
+    return dispatcher.alterModel(
+        // The constraints of the name spec may be more strict than underlying 
catalog,
+        // and for compatibility reasons, we only apply case-sensitive 
capabilities here.
+        normalizeCaseSensitive(ident), applyCapabilities(capability, changes));
   }
 
   /** {@inheritDoc} */
diff --git 
a/core/src/main/java/org/apache/gravitino/catalog/TopicNormalizeDispatcher.java 
b/core/src/main/java/org/apache/gravitino/catalog/TopicNormalizeDispatcher.java
index a104b4f671..eec94f8fb8 100644
--- 
a/core/src/main/java/org/apache/gravitino/catalog/TopicNormalizeDispatcher.java
+++ 
b/core/src/main/java/org/apache/gravitino/catalog/TopicNormalizeDispatcher.java
@@ -24,6 +24,8 @@ import static 
org.apache.gravitino.catalog.CapabilityHelpers.getCapability;
 
 import java.util.Map;
 import org.apache.commons.lang3.ArrayUtils;
+import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.connector.capability.Capability;
@@ -70,12 +72,19 @@ public class TopicNormalizeDispatcher implements 
TopicDispatcher {
   public Topic createTopic(
       NameIdentifier ident, String comment, DataLayout dataLayout, Map<String, 
String> properties)
       throws NoSuchSchemaException, TopicAlreadyExistsException {
+    // Check the comment before the underlying catalog creates the topic.
+    checkCommentLength(comment);
     return dispatcher.createTopic(normalizeNameIdentifier(ident), comment, 
dataLayout, properties);
   }
 
   @Override
   public Topic alterTopic(NameIdentifier ident, TopicChange... changes)
       throws NoSuchTopicException, IllegalArgumentException {
+    for (TopicChange change : changes) {
+      if (change instanceof TopicChange.UpdateTopicComment) {
+        checkCommentLength(((TopicChange.UpdateTopicComment) 
change).getNewComment());
+      }
+    }
     // The constraints of the name spec may be more strict than underlying 
catalog,
     // and for compatibility reasons, we only apply case-sensitive 
capabilities here.
     return dispatcher.alterTopic(normalizeCaseSensitive(ident), changes);
@@ -107,6 +116,11 @@ public class TopicNormalizeDispatcher implements 
TopicDispatcher {
     return applyCaseSensitive(topicIdents, Capability.Scope.TOPIC, 
capabilities);
   }
 
+  private static void checkCommentLength(String comment) {
+    EntityFieldLimits.checkMaxLength(
+        comment, EntityFieldLimits.MAX_COMMENT_LENGTH, "comment", 
Entity.EntityType.TOPIC);
+  }
+
   private NameIdentifier normalizeNameIdentifier(NameIdentifier topicIdent) {
     Capability capability = getCapability(topicIdent, catalogManager);
     return applyCapabilities(topicIdent, Capability.Scope.TOPIC, capability);
diff --git a/core/src/main/java/org/apache/gravitino/meta/BaseMetalake.java 
b/core/src/main/java/org/apache/gravitino/meta/BaseMetalake.java
index 4e2ca045f2..8ee4aaed53 100644
--- a/core/src/main/java/org/apache/gravitino/meta/BaseMetalake.java
+++ b/core/src/main/java/org/apache/gravitino/meta/BaseMetalake.java
@@ -27,6 +27,7 @@ import lombok.Getter;
 import lombok.ToString;
 import org.apache.gravitino.Auditable;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.Metalake;
@@ -42,7 +43,8 @@ public class BaseMetalake implements Metalake, Entity, 
Auditable, HasIdentifier
       Field.required("id", Long.class, "The metalake's unique identifier");
   public static final Field NAME = Field.required("name", String.class, "The 
metalake's name");
   public static final Field COMMENT =
-      Field.optional("comment", String.class, "The metalake's comment or 
description");
+      Field.optional(
+          "comment", "The metalake's comment or description", 
EntityFieldLimits.MAX_COMMENT_LENGTH);
   public static final Field PROPERTIES =
       Field.optional("properties", Map.class, "The properties associated with 
the metalake");
   public static final Field AUDIT_INFO =
diff --git a/core/src/main/java/org/apache/gravitino/meta/CatalogEntity.java 
b/core/src/main/java/org/apache/gravitino/meta/CatalogEntity.java
index d741370a3a..99c99e54bb 100644
--- a/core/src/main/java/org/apache/gravitino/meta/CatalogEntity.java
+++ b/core/src/main/java/org/apache/gravitino/meta/CatalogEntity.java
@@ -30,6 +30,7 @@ import org.apache.gravitino.Audit;
 import org.apache.gravitino.Auditable;
 import org.apache.gravitino.Catalog;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.Namespace;
@@ -47,7 +48,10 @@ public class CatalogEntity implements Entity, Auditable, 
HasIdentifier {
   public static final Field PROVIDER =
       Field.required("provider", String.class, "The provider of the catalog");
   public static final Field COMMENT =
-      Field.optional("comment", String.class, "The comment or description of 
the catalog");
+      Field.optional(
+          "comment",
+          "The comment or description of the catalog",
+          EntityFieldLimits.MAX_COMMENT_LENGTH);
   public static final Field PROPERTIES =
       Field.optional("properties", Map.class, "The properties associated with 
the catalog");
   public static final Field AUDIT_INFO =
diff --git a/core/src/main/java/org/apache/gravitino/meta/FilesetEntity.java 
b/core/src/main/java/org/apache/gravitino/meta/FilesetEntity.java
index c89a64f8c1..db8a466c33 100644
--- a/core/src/main/java/org/apache/gravitino/meta/FilesetEntity.java
+++ b/core/src/main/java/org/apache/gravitino/meta/FilesetEntity.java
@@ -29,6 +29,7 @@ import java.util.Objects;
 import lombok.ToString;
 import org.apache.gravitino.Auditable;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.Namespace;
@@ -42,7 +43,10 @@ public class FilesetEntity implements Entity, Auditable, 
HasIdentifier {
   public static final Field NAME =
       Field.required("name", String.class, "The name of the fileset entity.");
   public static final Field COMMENT =
-      Field.optional("comment", String.class, "The comment or description of 
the fileset entity.");
+      Field.optional(
+          "comment",
+          "The comment or description of the fileset entity.",
+          EntityFieldLimits.MAX_COMMENT_LENGTH);
   public static final Field TYPE =
       Field.required("type", Fileset.Type.class, "The type of the fileset 
entity.");
   public static final Field STORAGE_LOCATIONS =
diff --git a/core/src/main/java/org/apache/gravitino/meta/GroupEntity.java 
b/core/src/main/java/org/apache/gravitino/meta/GroupEntity.java
index b8cac5ba4c..6344661445 100644
--- a/core/src/main/java/org/apache/gravitino/meta/GroupEntity.java
+++ b/core/src/main/java/org/apache/gravitino/meta/GroupEntity.java
@@ -25,6 +25,7 @@ import java.util.Map;
 import java.util.Objects;
 import org.apache.gravitino.Auditable;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.Namespace;
@@ -37,7 +38,7 @@ public class GroupEntity implements Group, Entity, Auditable, 
HasIdentifier {
       Field.required("id", Long.class, " The unique id of the group entity.");
 
   public static final Field NAME =
-      Field.required("name", String.class, "The name of the group entity.");
+      Field.required("name", "The name of the group entity.", 
EntityFieldLimits.MAX_NAME_LENGTH);
 
   public static final Field ROLE_NAMES =
       Field.optional("role_names", List.class, "The role names of the group 
entity.");
diff --git 
a/core/src/main/java/org/apache/gravitino/meta/JobTemplateEntity.java 
b/core/src/main/java/org/apache/gravitino/meta/JobTemplateEntity.java
index fe60ea4678..7d0daaa92d 100644
--- a/core/src/main/java/org/apache/gravitino/meta/JobTemplateEntity.java
+++ b/core/src/main/java/org/apache/gravitino/meta/JobTemplateEntity.java
@@ -32,6 +32,7 @@ import lombok.ToString;
 import lombok.experimental.Accessors;
 import org.apache.gravitino.Auditable;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.Namespace;
@@ -101,7 +102,8 @@ public class JobTemplateEntity implements Entity, 
Auditable, HasIdentifier {
   public static final Field ID =
       Field.required("id", Long.class, "The unique id of the job template 
entity.");
   public static final Field NAME =
-      Field.required("name", String.class, "The name of the job template 
entity.");
+      Field.required(
+          "name", "The name of the job template entity.", 
EntityFieldLimits.MAX_NAME_LENGTH);
   public static final Field COMMENT =
       Field.optional(
           "comment", String.class, "The comment or description of the job 
template entity.");
diff --git 
a/core/src/main/java/org/apache/gravitino/meta/ModelVersionEntity.java 
b/core/src/main/java/org/apache/gravitino/meta/ModelVersionEntity.java
index 478b99946d..96d577b449 100644
--- a/core/src/main/java/org/apache/gravitino/meta/ModelVersionEntity.java
+++ b/core/src/main/java/org/apache/gravitino/meta/ModelVersionEntity.java
@@ -28,6 +28,7 @@ import java.util.Objects;
 import lombok.ToString;
 import org.apache.gravitino.Auditable;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.NameIdentifier;
@@ -139,6 +140,12 @@ public class ModelVersionEntity implements Entity, 
Auditable, HasIdentifier {
     Entity.super.validate();
     Preconditions.checkArgument(
         !uris.isEmpty(), "The uri of the model version entity must not be 
empty.");
+    if (aliases != null) {
+      aliases.forEach(
+          alias ->
+              EntityFieldLimits.checkMaxLength(
+                  alias, EntityFieldLimits.MAX_NAME_LENGTH, "alias", type()));
+    }
   }
 
   @Override
diff --git a/core/src/main/java/org/apache/gravitino/meta/PolicyEntity.java 
b/core/src/main/java/org/apache/gravitino/meta/PolicyEntity.java
index 2c122c3ca8..c0916ac8f5 100644
--- a/core/src/main/java/org/apache/gravitino/meta/PolicyEntity.java
+++ b/core/src/main/java/org/apache/gravitino/meta/PolicyEntity.java
@@ -29,6 +29,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.Audit;
 import org.apache.gravitino.Auditable;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.Namespace;
@@ -42,7 +43,7 @@ public class PolicyEntity implements Entity, Auditable, 
HasIdentifier {
   public static final Field ID =
       Field.required("id", Long.class, "The unique id of the policy entity.");
   public static final Field NAME =
-      Field.required("name", String.class, "The name of the policy entity.");
+      Field.required("name", "The name of the policy entity.", 
EntityFieldLimits.MAX_NAME_LENGTH);
   public static final Field POLICY_TYPE =
       Field.required("policyType", Policy.BuiltInType.class, "The type of the 
policy entity.");
   public static final Field COMMENT =
diff --git a/core/src/main/java/org/apache/gravitino/meta/RoleEntity.java 
b/core/src/main/java/org/apache/gravitino/meta/RoleEntity.java
index 6bde0291ec..9ded34692f 100644
--- a/core/src/main/java/org/apache/gravitino/meta/RoleEntity.java
+++ b/core/src/main/java/org/apache/gravitino/meta/RoleEntity.java
@@ -26,6 +26,7 @@ import java.util.Map;
 import java.util.Objects;
 import org.apache.gravitino.Auditable;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.Namespace;
@@ -39,7 +40,7 @@ public class RoleEntity implements Role, Entity, Auditable, 
HasIdentifier {
       Field.required("id", Long.class, " The unique id of the role entity.");
 
   public static final Field NAME =
-      Field.required("name", String.class, "The name of the role entity.");
+      Field.required("name", "The name of the role entity.", 
EntityFieldLimits.MAX_NAME_LENGTH);
 
   public static final Field PROPERTIES =
       Field.optional("properties", Map.class, "The properties of the role 
entity.");
diff --git a/core/src/main/java/org/apache/gravitino/meta/SchemaEntity.java 
b/core/src/main/java/org/apache/gravitino/meta/SchemaEntity.java
index 7848e20d39..4e1f2653af 100644
--- a/core/src/main/java/org/apache/gravitino/meta/SchemaEntity.java
+++ b/core/src/main/java/org/apache/gravitino/meta/SchemaEntity.java
@@ -25,6 +25,7 @@ import java.util.Map;
 import lombok.ToString;
 import org.apache.gravitino.Auditable;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.Namespace;
@@ -38,7 +39,10 @@ public class SchemaEntity implements Entity, Auditable, 
HasIdentifier {
   public static final Field AUDIT_INFO =
       Field.required("audit_info", AuditInfo.class, "The audit details of the 
schema");
   public static final Field COMMENT =
-      Field.optional("comment", String.class, "The comment or description of 
the schema");
+      Field.optional(
+          "comment",
+          "The comment or description of the schema",
+          EntityFieldLimits.MAX_COMMENT_LENGTH);
   public static final Field PROPERTIES =
       Field.optional("properties", Map.class, "The properties of the schema");
 
diff --git a/core/src/main/java/org/apache/gravitino/meta/TagEntity.java 
b/core/src/main/java/org/apache/gravitino/meta/TagEntity.java
index 1eabad889a..49eb5aff67 100644
--- a/core/src/main/java/org/apache/gravitino/meta/TagEntity.java
+++ b/core/src/main/java/org/apache/gravitino/meta/TagEntity.java
@@ -27,6 +27,7 @@ import java.util.Optional;
 import org.apache.gravitino.Audit;
 import org.apache.gravitino.Auditable;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.Namespace;
@@ -38,10 +39,11 @@ public class TagEntity implements Tag, Entity, Auditable, 
HasIdentifier {
       Field.required("id", Long.class, "The unique id of the tag entity.");
 
   public static final Field NAME =
-      Field.required("name", String.class, "The name of the tag entity.");
+      Field.required("name", "The name of the tag entity.", 
EntityFieldLimits.MAX_NAME_LENGTH);
 
   public static final Field COMMENT =
-      Field.optional("comment", String.class, "The comment of the tag 
entity.");
+      Field.optional(
+          "comment", "The comment of the tag entity.", 
EntityFieldLimits.MAX_COMMENT_LENGTH);
 
   public static final Field PROPERTIES =
       Field.optional("properties", Map.class, "The properties of the tag 
entity.");
diff --git a/core/src/main/java/org/apache/gravitino/meta/TopicEntity.java 
b/core/src/main/java/org/apache/gravitino/meta/TopicEntity.java
index a4479eeef2..03a3d58b09 100644
--- a/core/src/main/java/org/apache/gravitino/meta/TopicEntity.java
+++ b/core/src/main/java/org/apache/gravitino/meta/TopicEntity.java
@@ -25,6 +25,7 @@ import java.util.Objects;
 import lombok.ToString;
 import org.apache.gravitino.Auditable;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.Namespace;
@@ -37,7 +38,10 @@ public class TopicEntity implements Entity, Auditable, 
HasIdentifier {
   public static final Field NAME =
       Field.required("name", String.class, "The name of the topic entity.");
   public static final Field COMMENT =
-      Field.optional("comment", String.class, "The comment or description of 
the topic entity.");
+      Field.optional(
+          "comment",
+          "The comment or description of the topic entity.",
+          EntityFieldLimits.MAX_COMMENT_LENGTH);
   public static final Field AUDIT_INFO =
       Field.required("audit_info", AuditInfo.class, "The audit details of the 
topic entity.");
   public static final Field PROPERTIES =
diff --git a/core/src/main/java/org/apache/gravitino/meta/UserEntity.java 
b/core/src/main/java/org/apache/gravitino/meta/UserEntity.java
index c9d7089f85..da4094aa13 100644
--- a/core/src/main/java/org/apache/gravitino/meta/UserEntity.java
+++ b/core/src/main/java/org/apache/gravitino/meta/UserEntity.java
@@ -26,6 +26,7 @@ import java.util.Objects;
 import lombok.ToString;
 import org.apache.gravitino.Auditable;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.Namespace;
@@ -40,7 +41,7 @@ public class UserEntity implements User, Entity, Auditable, 
HasIdentifier {
       Field.required("id", Long.class, " The unique id of the user entity.");
 
   public static final Field NAME =
-      Field.required("name", String.class, "The name of the user entity.");
+      Field.required("name", "The name of the user entity.", 
EntityFieldLimits.MAX_NAME_LENGTH);
 
   public static final Field AUDIT_INFO =
       Field.required("audit_info", AuditInfo.class, "The audit details of the 
user entity.");
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/converters/H2ExceptionConverter.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/converters/H2ExceptionConverter.java
index ff297d8d27..1c83d34a2d 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/converters/H2ExceptionConverter.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/converters/H2ExceptionConverter.java
@@ -31,6 +31,9 @@ public class H2ExceptionConverter implements 
SQLExceptionConverter {
   /** It means found a duplicated primary key or unique key entry in H2. */
   private static final int DUPLICATED_ENTRY_ERROR_CODE = 23505;
 
+  /** It means a value is too long for its column in H2. */
+  private static final int VALUE_TOO_LONG_ERROR_CODE = 22001;
+
   @SuppressWarnings("FormatStringAnnotation")
   @Override
   public void toGravitinoException(SQLException se, Entity.EntityType type, 
String name)
@@ -41,6 +44,8 @@ public class H2ExceptionConverter implements 
SQLExceptionConverter {
       case MySQLExceptionConverter.DUPLICATED_ENTRY_ERROR_CODE:
         throw new EntityAlreadyExistsException(
             se, "The %s entity: %s already exists.", type.name(), name);
+      case VALUE_TOO_LONG_ERROR_CODE:
+        throw ValueTooLongExceptions.of(se, type, name);
       default:
         throw new IOException("error code: " + se.getErrorCode(), se);
     }
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/converters/MySQLExceptionConverter.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/converters/MySQLExceptionConverter.java
index 6b5035f23a..b7c8208eca 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/converters/MySQLExceptionConverter.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/converters/MySQLExceptionConverter.java
@@ -32,6 +32,9 @@ public class MySQLExceptionConverter implements 
SQLExceptionConverter {
   /** It means found a duplicated primary key or unique key entry in MySQL. */
   static final int DUPLICATED_ENTRY_ERROR_CODE = 1062;
 
+  /** It means a value is too long for its column in MySQL. */
+  private static final int DATA_TOO_LONG_ERROR_CODE = 1406;
+
   @SuppressWarnings("FormatStringAnnotation")
   @Override
   public void toGravitinoException(SQLException se, Entity.EntityType type, 
String name)
@@ -40,6 +43,8 @@ public class MySQLExceptionConverter implements 
SQLExceptionConverter {
       case DUPLICATED_ENTRY_ERROR_CODE:
         throw new EntityAlreadyExistsException(
             se, "The %s entity: %s already exists.", type.name(), name);
+      case DATA_TOO_LONG_ERROR_CODE:
+        throw ValueTooLongExceptions.of(se, type, name);
       default:
         throw new IOException(se);
     }
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/converters/PostgreSQLExceptionConverter.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/converters/PostgreSQLExceptionConverter.java
index 318e3865d9..730b180bec 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/converters/PostgreSQLExceptionConverter.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/converters/PostgreSQLExceptionConverter.java
@@ -31,6 +31,9 @@ import org.apache.gravitino.EntityAlreadyExistsException;
 public class PostgreSQLExceptionConverter implements SQLExceptionConverter {
   private static final String DUPLICATED_ENTRY_ERROR_CODE = "23505";
 
+  /** It means a value is too long for its column in PostgreSQL. */
+  private static final String STRING_DATA_RIGHT_TRUNCATION_ERROR_CODE = 
"22001";
+
   @Override
   @SuppressWarnings("FormatStringAnnotation")
   public void toGravitinoException(SQLException sqlException, 
Entity.EntityType type, String name)
@@ -40,6 +43,8 @@ public class PostgreSQLExceptionConverter implements 
SQLExceptionConverter {
       case DUPLICATED_ENTRY_ERROR_CODE:
         throw new EntityAlreadyExistsException(
             sqlException, "The %s entity: %s already exists.", type.name(), 
name);
+      case STRING_DATA_RIGHT_TRUNCATION_ERROR_CODE:
+        throw ValueTooLongExceptions.of(sqlException, type, name);
       default:
         throw new IOException(sqlException);
     }
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/converters/ValueTooLongExceptions.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/converters/ValueTooLongExceptions.java
new file mode 100644
index 0000000000..65901c4843
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/converters/ValueTooLongExceptions.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.storage.relational.converters;
+
+import java.sql.SQLException;
+import java.util.Locale;
+import org.apache.gravitino.Entity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Builds the exception for a SQL error reporting a value that is too long 
for its column. */
+final class ValueTooLongExceptions {
+  private static final Logger LOG = 
LoggerFactory.getLogger(ValueTooLongExceptions.class);
+
+  private ValueTooLongExceptions() {}
+
+  /**
+   * Creates the exception for a SQL error reporting a value that is too long 
for its column.
+   *
+   * <p>The SQL exception is logged but not attached as the cause, since the 
error response
+   * serializes the whole stack trace and would expose the database error 
message to the client.
+   *
+   * @param sqlException The SQL exception reporting the value that is too 
long.
+   * @param type The type of the entity being persisted.
+   * @param name The name of the entity being persisted.
+   * @return The {@link IllegalArgumentException} to throw.
+   */
+  static IllegalArgumentException of(
+      SQLException sqlException, Entity.EntityType type, String name) {
+    LOG.warn("Failed to persist the {} entity: {}", type, name, sqlException);
+    return new IllegalArgumentException(
+        String.format(
+            "The %s entity has a value that exceeds the maximum length of its 
column.",
+            type.name().toLowerCase(Locale.ROOT)));
+  }
+}
diff --git a/core/src/test/java/org/apache/gravitino/TestField.java 
b/core/src/test/java/org/apache/gravitino/TestField.java
index 28d9b7e79a..ad0d0a5823 100644
--- a/core/src/test/java/org/apache/gravitino/TestField.java
+++ b/core/src/test/java/org/apache/gravitino/TestField.java
@@ -53,6 +53,32 @@ class TestField {
     assertEquals("Field name is not of type java.lang.String", 
ex.getMessage());
   }
 
+  @Test
+  void testFieldMaxLength() {
+    Field field = Field.required("name", "Name of the person", 3);
+
+    assertDoesNotThrow(() -> field.validate("abc"));
+
+    IllegalArgumentException ex =
+        assertThrows(IllegalArgumentException.class, () -> 
field.validate("abcd"));
+    assertEquals("Field name must not exceed 3 characters", ex.getMessage());
+
+    ex =
+        assertThrows(
+            IllegalArgumentException.class, () -> field.validate("abcd", 
Entity.EntityType.TAG));
+    assertEquals("The name of the tag must not exceed 3 characters", 
ex.getMessage());
+
+    Field optional = Field.optional("comment", "Comment of the person", 3);
+    assertDoesNotThrow(() -> optional.validate(null));
+  }
+
+  @Test
+  void testBuilderInvalidMaxLength() {
+    IllegalArgumentException ex =
+        assertThrows(IllegalArgumentException.class, () -> 
Field.required("name", "Name", 0));
+    assertEquals("Field max length must be positive", ex.getMessage());
+  }
+
   @Test
   void testBuilderMissingName() {
     IllegalArgumentException ex =
diff --git 
a/core/src/test/java/org/apache/gravitino/catalog/TestModelNormalizeDispatcher.java
 
b/core/src/test/java/org/apache/gravitino/catalog/TestModelNormalizeDispatcher.java
new file mode 100644
index 0000000000..dd6c30b9b2
--- /dev/null
+++ 
b/core/src/test/java/org/apache/gravitino/catalog/TestModelNormalizeDispatcher.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.catalog;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.MetadataObjects;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.model.Model;
+import org.apache.gravitino.model.ModelChange;
+import org.apache.gravitino.utils.NameIdentifierUtil;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class TestModelNormalizeDispatcher extends TestOperationDispatcher {
+  private static ModelNormalizeDispatcher modelNormalizeDispatcher;
+  private static SchemaNormalizeDispatcher schemaNormalizeDispatcher;
+
+  @BeforeAll
+  public static void initialize() throws IOException, IllegalAccessException {
+    TestModelOperationDispatcher.initialize();
+    schemaNormalizeDispatcher =
+        new SchemaNormalizeDispatcher(
+            TestModelOperationDispatcher.schemaOperationDispatcher, 
catalogManager);
+    modelNormalizeDispatcher =
+        new ModelNormalizeDispatcher(
+            TestModelOperationDispatcher.modelOperationDispatcher, 
catalogManager);
+  }
+
+  @Test
+  public void testRenameNameSpec() {
+    String schemaName = "test_rename_name_spec";
+    schemaNormalizeDispatcher.createSchema(
+        NameIdentifier.of(metalake, catalog, schemaName), "comment", 
ImmutableMap.of("k1", "v1"));
+    NameIdentifier modelIdent = NameIdentifierUtil.ofModel(metalake, catalog, 
schemaName, "model");
+    modelNormalizeDispatcher.registerModel(modelIdent, "comment", 
ImmutableMap.of("k1", "v1"));
+
+    String tooLongName = StringUtils.repeat("m", 129);
+    Exception exception =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () -> modelNormalizeDispatcher.alterModel(modelIdent, 
ModelChange.rename(tooLongName)));
+    Assertions.assertEquals(
+        String.format("The MODEL name '%s' is illegal. Illegal name: %s", 
tooLongName, tooLongName),
+        exception.getMessage());
+
+    exception =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () ->
+                modelNormalizeDispatcher.alterModel(
+                    modelIdent, 
ModelChange.rename(MetadataObjects.METADATA_OBJECT_RESERVED_NAME)));
+    Assertions.assertEquals(
+        "The MODEL name '*' is reserved. Illegal name: *", 
exception.getMessage());
+
+    exception =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () -> modelNormalizeDispatcher.alterModel(modelIdent, 
ModelChange.rename("a?")));
+    Assertions.assertEquals(
+        "The MODEL name 'a?' is illegal. Illegal name: a?", 
exception.getMessage());
+
+    Model renamed = modelNormalizeDispatcher.alterModel(modelIdent, 
ModelChange.rename("model_1"));
+    Assertions.assertEquals("model_1", renamed.name());
+  }
+}
diff --git 
a/core/src/test/java/org/apache/gravitino/catalog/TestTopicNormalizeDispatcher.java
 
b/core/src/test/java/org/apache/gravitino/catalog/TestTopicNormalizeDispatcher.java
index ef25892410..6b6d8e9608 100644
--- 
a/core/src/test/java/org/apache/gravitino/catalog/TestTopicNormalizeDispatcher.java
+++ 
b/core/src/test/java/org/apache/gravitino/catalog/TestTopicNormalizeDispatcher.java
@@ -21,6 +21,8 @@ package org.apache.gravitino.catalog;
 import com.google.common.collect.ImmutableMap;
 import java.io.IOException;
 import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.MetadataObjects;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
@@ -101,4 +103,36 @@ public class TestTopicNormalizeDispatcher extends 
TestOperationDispatcher {
     Assertions.assertEquals(
         "The TOPIC name 'a?' is illegal. Illegal name: a?", 
exception.getMessage());
   }
+
+  @Test
+  public void testCommentLength() {
+    Namespace topicNs = Namespace.of(metalake, catalog, "testCommentLength");
+    Map<String, String> props = ImmutableMap.of("k1", "v1");
+    
schemaNormalizeDispatcher.createSchema(NameIdentifier.of(topicNs.levels()), 
"comment", props);
+
+    NameIdentifier topicIdent = NameIdentifier.of(topicNs, "topic");
+    String tooLongComment = StringUtils.repeat("a", 
EntityFieldLimits.MAX_COMMENT_LENGTH + 1);
+    Exception exception =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () -> topicNormalizeDispatcher.createTopic(topicIdent, 
tooLongComment, null, props));
+    Assertions.assertEquals(
+        "The comment of the topic must not exceed 256 characters", 
exception.getMessage());
+    Assertions.assertFalse(topicNormalizeDispatcher.topicExists(topicIdent));
+
+    String maxLengthComment = StringUtils.repeat("a", 
EntityFieldLimits.MAX_COMMENT_LENGTH);
+    Topic topic = topicNormalizeDispatcher.createTopic(topicIdent, 
maxLengthComment, null, props);
+    Assertions.assertEquals(maxLengthComment, topic.comment());
+
+    exception =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () ->
+                topicNormalizeDispatcher.alterTopic(
+                    topicIdent, TopicChange.updateComment(tooLongComment)));
+    Assertions.assertEquals(
+        "The comment of the topic must not exceed 256 characters", 
exception.getMessage());
+    Assertions.assertEquals(
+        maxLengthComment, 
topicNormalizeDispatcher.loadTopic(topicIdent).comment());
+  }
 }
diff --git 
a/core/src/test/java/org/apache/gravitino/meta/TestEntityFieldLimits.java 
b/core/src/test/java/org/apache/gravitino/meta/TestEntityFieldLimits.java
new file mode 100644
index 0000000000..d2a77ae387
--- /dev/null
+++ b/core/src/test/java/org/apache/gravitino/meta/TestEntityFieldLimits.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.meta;
+
+import static org.apache.gravitino.file.Fileset.LOCATION_NAME_UNKNOWN;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.authorization.Privileges;
+import org.apache.gravitino.authorization.SecurableObjects;
+import org.apache.gravitino.file.Fileset;
+import org.apache.gravitino.job.ShellJobTemplate;
+import org.apache.gravitino.model.ModelVersion;
+import org.apache.gravitino.policy.Policy;
+import org.apache.gravitino.policy.PolicyContents;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class TestEntityFieldLimits {
+
+  private static final Namespace NAMESPACE = Namespace.of("metalake");
+
+  @ParameterizedTest(name = "{0}")
+  @MethodSource("nameBuilders")
+  public void testNameLength(String entityType, Function<String, Entity> 
builder) {
+    assertLengthLimit(builder, "name", entityType, 
EntityFieldLimits.MAX_NAME_LENGTH);
+  }
+
+  @ParameterizedTest(name = "{0}")
+  @MethodSource("commentBuilders")
+  public void testCommentLength(String entityType, Function<String, Entity> 
builder) {
+    assertLengthLimit(builder, "comment", entityType, 
EntityFieldLimits.MAX_COMMENT_LENGTH);
+  }
+
+  @Test
+  public void testModelVersionAliasLength() {
+    Function<String, Entity> builder =
+        alias ->
+            ModelVersionEntity.builder()
+                .withModelIdentifier(NameIdentifier.of("m1", "c1", "s1", 
"model1"))
+                .withVersion(1)
+                .withAliases(Lists.newArrayList("alias", alias))
+                .withUris(ImmutableMap.of(ModelVersion.URI_NAME_UNKNOWN, 
"test_uri"))
+                .withAuditInfo(AuditInfo.EMPTY)
+                .build();
+    assertLengthLimit(builder, "alias", "model version", 
EntityFieldLimits.MAX_NAME_LENGTH);
+  }
+
+  @Test
+  public void testLengthCountsCodePoints() {
+    // An emoji is two UTF-16 chars but one character for MySQL (utf8mb4) and 
PostgreSQL, where a
+    // name of 128 emojis fits the column and must still pass validation when 
it is read back. H2
+    // counts UTF-16 chars, so it rejects such a name in the database instead, 
which the SQL
+    // exception converter reports as an IllegalArgumentException as well.
+    String emoji = new String(Character.toChars(0x1F600));
+    String maxLengthName = StringUtils.repeat(emoji, 
EntityFieldLimits.MAX_NAME_LENGTH);
+    Assertions.assertDoesNotThrow(() -> tagBuilder(maxLengthName, null));
+
+    IllegalArgumentException exception =
+        Assertions.assertThrows(
+            IllegalArgumentException.class, () -> tagBuilder(maxLengthName + 
emoji, null));
+    Assertions.assertEquals(
+        "The name of the tag must not exceed 128 characters", 
exception.getMessage());
+  }
+
+  @Test
+  public void testCheckMaxLength() {
+    Assertions.assertDoesNotThrow(() -> EntityFieldLimits.checkMaxLength(null, 
1, "name", null));
+    Assertions.assertDoesNotThrow(() -> EntityFieldLimits.checkMaxLength("a", 
1, "name", null));
+
+    IllegalArgumentException exception =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () -> EntityFieldLimits.checkMaxLength("ab", 1, "name", null));
+    Assertions.assertEquals("Field name must not exceed 1 characters", 
exception.getMessage());
+  }
+
+  private static void assertLengthLimit(
+      Function<String, Entity> builder, String fieldName, String entityType, 
int maxLength) {
+    Assertions.assertDoesNotThrow(() -> builder.apply(StringUtils.repeat("a", 
maxLength)));
+
+    String tooLong = StringUtils.repeat("a", maxLength + 1);
+    IllegalArgumentException exception =
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
builder.apply(tooLong));
+    Assertions.assertEquals(
+        String.format(
+            "The %s of the %s must not exceed %d characters", fieldName, 
entityType, maxLength),
+        exception.getMessage());
+  }
+
+  private static Stream<Arguments> nameBuilders() {
+    return Stream.of(
+        Arguments.of("tag", (Function<String, Entity>) name -> 
tagBuilder(name, null)),
+        Arguments.of(
+            "policy",
+            (Function<String, Entity>)
+                name ->
+                    PolicyEntity.builder()
+                        .withId(1L)
+                        .withName(name)
+                        .withNamespace(NAMESPACE)
+                        .withPolicyType(Policy.BuiltInType.CUSTOM)
+                        .withEnabled(false)
+                        .withContent(
+                            PolicyContents.custom(
+                                ImmutableMap.of("k", "v"),
+                                ImmutableSet.of(MetadataObject.Type.TABLE),
+                                ImmutableMap.of()))
+                        .withAuditInfo(AuditInfo.EMPTY)
+                        .build()),
+        Arguments.of(
+            "role",
+            (Function<String, Entity>)
+                name ->
+                    RoleEntity.builder()
+                        .withId(1L)
+                        .withName(name)
+                        .withAuditInfo(AuditInfo.EMPTY)
+                        .withSecurableObjects(
+                            Lists.newArrayList(
+                                SecurableObjects.ofCatalog(
+                                    "catalog", 
Lists.newArrayList(Privileges.UseCatalog.allow()))))
+                        .build()),
+        Arguments.of(
+            "user",
+            (Function<String, Entity>)
+                name ->
+                    UserEntity.builder()
+                        .withId(1L)
+                        .withName(name)
+                        .withAuditInfo(AuditInfo.EMPTY)
+                        .build()),
+        Arguments.of(
+            "group",
+            (Function<String, Entity>)
+                name ->
+                    GroupEntity.builder()
+                        .withId(1L)
+                        .withName(name)
+                        .withAuditInfo(AuditInfo.EMPTY)
+                        .build()),
+        Arguments.of(
+            "job template",
+            (Function<String, Entity>)
+                name ->
+                    JobTemplateEntity.builder()
+                        .withId(1L)
+                        .withName(name)
+                        .withNamespace(NAMESPACE)
+                        .withTemplateContent(
+                            JobTemplateEntity.TemplateContent.fromJobTemplate(
+                                ShellJobTemplate.builder()
+                                    .withName("template")
+                                    .withExecutable("/bin/echo")
+                                    .build()))
+                        .withAuditInfo(AuditInfo.EMPTY)
+                        .build()));
+  }
+
+  private static Stream<Arguments> commentBuilders() {
+    return Stream.of(
+        Arguments.of("tag", (Function<String, Entity>) comment -> 
tagBuilder("tag", comment)),
+        Arguments.of(
+            "metalake",
+            (Function<String, Entity>)
+                comment ->
+                    BaseMetalake.builder()
+                        .withId(1L)
+                        .withName("metalake")
+                        .withComment(comment)
+                        .withAuditInfo(AuditInfo.EMPTY)
+                        .withVersion(SchemaVersion.V_0_1)
+                        .build()),
+        Arguments.of(
+            "catalog",
+            (Function<String, Entity>)
+                comment ->
+                    CatalogEntity.builder()
+                        .withId(1L)
+                        .withName("catalog")
+                        .withComment(comment)
+                        .withType(Catalog.Type.RELATIONAL)
+                        .withProvider("test")
+                        .withAuditInfo(AuditInfo.EMPTY)
+                        .build()),
+        Arguments.of(
+            "schema",
+            (Function<String, Entity>)
+                comment ->
+                    SchemaEntity.builder()
+                        .withId(1L)
+                        .withName("schema")
+                        .withComment(comment)
+                        .withAuditInfo(AuditInfo.EMPTY)
+                        .build()),
+        Arguments.of(
+            "fileset",
+            (Function<String, Entity>)
+                comment ->
+                    FilesetEntity.builder()
+                        .withId(1L)
+                        .withName("fileset")
+                        .withComment(comment)
+                        .withFilesetType(Fileset.Type.MANAGED)
+                        
.withStorageLocations(ImmutableMap.of(LOCATION_NAME_UNKNOWN, "location"))
+                        .withAuditInfo(AuditInfo.EMPTY)
+                        .build()),
+        Arguments.of(
+            "topic",
+            (Function<String, Entity>)
+                comment ->
+                    TopicEntity.builder()
+                        .withId(1L)
+                        .withName("topic")
+                        .withComment(comment)
+                        .withAuditInfo(AuditInfo.EMPTY)
+                        .build()));
+  }
+
+  private static TagEntity tagBuilder(String name, String comment) {
+    return TagEntity.builder()
+        .withId(1L)
+        .withName(name)
+        .withNamespace(NAMESPACE)
+        .withComment(comment)
+        .withAuditInfo(AuditInfo.EMPTY)
+        .build();
+  }
+}
diff --git 
a/core/src/test/java/org/apache/gravitino/storage/relational/converters/TestH2ExceptionConverter.java
 
b/core/src/test/java/org/apache/gravitino/storage/relational/converters/TestH2ExceptionConverter.java
index f07abd3b59..883a1c2f48 100644
--- 
a/core/src/test/java/org/apache/gravitino/storage/relational/converters/TestH2ExceptionConverter.java
+++ 
b/core/src/test/java/org/apache/gravitino/storage/relational/converters/TestH2ExceptionConverter.java
@@ -36,4 +36,19 @@ public class TestH2ExceptionConverter {
         () -> converter.toGravitinoException(mockException, 
Entity.EntityType.METALAKE, "test"),
         String.format("The %s entity: %s already exists.", 
Entity.EntityType.METALAKE, "test"));
   }
+
+  @Test
+  public void testConvertValueTooLongException() {
+    SQLException sqlException = new SQLException("Value too long for column", 
"22001", 22001);
+    H2ExceptionConverter converter = new H2ExceptionConverter();
+    IllegalArgumentException exception =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () -> converter.toGravitinoException(sqlException, 
Entity.EntityType.TAG, "test"));
+    Assertions.assertEquals(
+        "The tag entity has a value that exceeds the maximum length of its 
column.",
+        exception.getMessage());
+    // The database error must not be exposed to the client through the cause.
+    Assertions.assertNull(exception.getCause());
+  }
 }
diff --git 
a/core/src/test/java/org/apache/gravitino/storage/relational/converters/TestMySQLExceptionConverter.java
 
b/core/src/test/java/org/apache/gravitino/storage/relational/converters/TestMySQLExceptionConverter.java
index 9a94db6e10..d69300f5c8 100644
--- 
a/core/src/test/java/org/apache/gravitino/storage/relational/converters/TestMySQLExceptionConverter.java
+++ 
b/core/src/test/java/org/apache/gravitino/storage/relational/converters/TestMySQLExceptionConverter.java
@@ -37,4 +37,20 @@ public class TestMySQLExceptionConverter {
         () -> converter.toGravitinoException(mockException, 
Entity.EntityType.METALAKE, "test"),
         String.format("The %s entity: %s already exists.", 
Entity.EntityType.METALAKE, "test"));
   }
+
+  @Test
+  public void testConvertValueTooLongException() {
+    SQLException sqlException =
+        new SQLException("Data too long for column 'tag_name'", "22001", 1406);
+    MySQLExceptionConverter converter = new MySQLExceptionConverter();
+    IllegalArgumentException exception =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () -> converter.toGravitinoException(sqlException, 
Entity.EntityType.TAG, "test"));
+    Assertions.assertEquals(
+        "The tag entity has a value that exceeds the maximum length of its 
column.",
+        exception.getMessage());
+    // The database error must not be exposed to the client through the cause.
+    Assertions.assertNull(exception.getCause());
+  }
 }
diff --git 
a/core/src/test/java/org/apache/gravitino/storage/relational/converters/TestPostgreSQLExceptionConverter.java
 
b/core/src/test/java/org/apache/gravitino/storage/relational/converters/TestPostgreSQLExceptionConverter.java
index 9c6d9c4ea2..d31eda0fd3 100644
--- 
a/core/src/test/java/org/apache/gravitino/storage/relational/converters/TestPostgreSQLExceptionConverter.java
+++ 
b/core/src/test/java/org/apache/gravitino/storage/relational/converters/TestPostgreSQLExceptionConverter.java
@@ -45,4 +45,20 @@ public class TestPostgreSQLExceptionConverter {
         IOException.class,
         () -> converter.toGravitinoException(sqlException, 
Entity.EntityType.METALAKE, "test"));
   }
+
+  @Test
+  public void testConvertValueTooLongException() {
+    SQLException sqlException =
+        new SQLException("value too long for type character varying(128)", 
"22001");
+    PostgreSQLExceptionConverter converter = new 
PostgreSQLExceptionConverter();
+    IllegalArgumentException exception =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () -> converter.toGravitinoException(sqlException, 
Entity.EntityType.TAG, "test"));
+    Assertions.assertEquals(
+        "The tag entity has a value that exceeds the maximum length of its 
column.",
+        exception.getMessage());
+    // The database error must not be exposed to the client through the cause.
+    Assertions.assertNull(exception.getCause());
+  }
 }
diff --git a/core/src/test/java/org/apache/gravitino/tag/TestTagManager.java 
b/core/src/test/java/org/apache/gravitino/tag/TestTagManager.java
index 775759f7d5..57182308b2 100644
--- a/core/src/test/java/org/apache/gravitino/tag/TestTagManager.java
+++ b/core/src/test/java/org/apache/gravitino/tag/TestTagManager.java
@@ -51,11 +51,13 @@ import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.reflect.FieldUtils;
 import org.apache.gravitino.Catalog;
 import org.apache.gravitino.Config;
 import org.apache.gravitino.Configs;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.EntityStore;
 import org.apache.gravitino.EntityStoreFactory;
 import org.apache.gravitino.GravitinoEnv;
@@ -435,6 +437,49 @@ public class TestTagManager {
     Assertions.assertEquals(expectedProp2, removedPropTag.properties());
   }
 
+  @Test
+  public void testTagNameAndCommentLength() {
+    String maxLengthName = StringUtils.repeat("a", 
EntityFieldLimits.MAX_NAME_LENGTH);
+    String tooLongName = maxLengthName + "a";
+    String maxLengthComment = StringUtils.repeat("c", 
EntityFieldLimits.MAX_COMMENT_LENGTH);
+    String tooLongComment = maxLengthComment + "c";
+
+    Exception e =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () -> tagManager.createTag(METALAKE, tooLongName, null, null));
+    Assertions.assertEquals("The name of the tag must not exceed 128 
characters", e.getMessage());
+
+    e =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () -> tagManager.createTag(METALAKE, "tag1", tooLongComment, 
null));
+    Assertions.assertEquals(
+        "The comment of the tag must not exceed 256 characters", 
e.getMessage());
+
+    Tag tag = tagManager.createTag(METALAKE, maxLengthName, maxLengthComment, 
null);
+    Assertions.assertEquals(maxLengthName, tag.name());
+    Assertions.assertEquals(maxLengthComment, tag.comment());
+
+    e =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () -> tagManager.alterTag(METALAKE, maxLengthName, 
TagChange.rename(tooLongName)));
+    Assertions.assertEquals("The name of the tag must not exceed 128 
characters", e.getMessage());
+
+    e =
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () ->
+                tagManager.alterTag(
+                    METALAKE, maxLengthName, 
TagChange.updateComment(tooLongComment)));
+    Assertions.assertEquals(
+        "The comment of the tag must not exceed 256 characters", 
e.getMessage());
+
+    Tag unchanged = tagManager.getTag(METALAKE, maxLengthName);
+    Assertions.assertEquals(maxLengthComment, unchanged.comment());
+  }
+
   @Test
   public void testAlterTagRenameToExistingTag() {
     tagManager.createTag(METALAKE, "tag1", null, null);

Reply via email to