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 29749776d9 [Cherry-pick to branch-1.3] [#14039] fix(core): Return 
consistent conflicts for metadata renames (#14040) (#14137)
29749776d9 is described below

commit 29749776d95d2ecc6c804b27db640982ae2d8e89
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Oct 23 22:54:09 2026 +0800

    [Cherry-pick to branch-1.3] [#14039] fix(core): Return consistent conflicts 
for metadata renames (#14040) (#14137)
    
    **Cherry-pick Information:**
    - Original PR: #14040
    - Original commit: `5c8c1d99fed331360e87f5f0730251655398c135`
    - Target branch: `branch-1.3`
    - Status: Conflicts resolved in
    f6656bc1e19dbf4cadeecd92b9c2de75109be337.
    
    Preserves branch-1.3's Policy/Tag tree locks and adapts the backported
    tests to its constructors and helpers.
    
    **Validation:** 461 targeted tests passed (Core 87, Fileset 227, Model
    36, REST 111), plus Spotless, API Javadoc, and OpenAPI validation.
    Docker integration tests were not run.
    
    ---------
    
    Co-authored-by: mchades <[email protected]>
    Co-authored-by: Jerry Shao <[email protected]>
---
 .../org/apache/gravitino/SupportsCatalogs.java     |   3 +-
 .../org/apache/gravitino/SupportsMetalakes.java    |   3 +-
 .../org/apache/gravitino/file/FilesetCatalog.java  |   3 +-
 .../org/apache/gravitino/model/ModelCatalog.java   |   3 +-
 .../apache/gravitino/policy/PolicyOperations.java  |   3 +-
 .../org/apache/gravitino/rel/TableCatalog.java     |   3 +-
 .../catalog/fileset/FilesetCatalogOperations.java  |  18 +-
 .../fileset/TestFilesetCatalogOperations.java      |  41 +++
 .../catalog/model/ModelCatalogOperations.java      |  15 +-
 .../catalog/model/TestModelCatalogOperations.java  |  34 +++
 .../apache/gravitino/catalog/CatalogManager.java   |  14 +-
 .../gravitino/catalog/ManagedTableOperations.java  |  15 +-
 .../apache/gravitino/metalake/MetalakeManager.java |  13 +-
 .../org/apache/gravitino/policy/PolicyManager.java |  16 +-
 .../java/org/apache/gravitino/tag/TagManager.java  |   8 +-
 .../org/apache/gravitino/utils/RenameUtils.java    |  51 ++++
 .../apache/gravitino/policy/TestPolicyManager.java |  27 ++
 .../storage/relational/TestRenameConflicts.java    | 309 +++++++++++++++++++++
 .../org/apache/gravitino/tag/TestTagManager.java   |  32 +++
 .../apache/gravitino/utils/TestRenameUtils.java    |  63 +++++
 docs/open-api/models.yaml                          |   9 +
 docs/open-api/policies.yaml                        |   9 +
 .../server/web/rest/TestCatalogOperations.java     |  21 ++
 .../server/web/rest/TestFilesetOperations.java     |  22 ++
 .../server/web/rest/TestMetalakeOperations.java    |  24 ++
 .../server/web/rest/TestModelOperations.java       |  22 ++
 .../server/web/rest/TestPolicyOperations.java      |  22 ++
 .../server/web/rest/TestTableOperations.java       |  22 ++
 28 files changed, 792 insertions(+), 33 deletions(-)

diff --git a/api/src/main/java/org/apache/gravitino/SupportsCatalogs.java 
b/api/src/main/java/org/apache/gravitino/SupportsCatalogs.java
index 1009a1f332..a48c83f24a 100644
--- a/api/src/main/java/org/apache/gravitino/SupportsCatalogs.java
+++ b/api/src/main/java/org/apache/gravitino/SupportsCatalogs.java
@@ -125,10 +125,11 @@ public interface SupportsCatalogs {
    * @param changes the changes to apply to the catalog.
    * @return The altered catalog.
    * @throws NoSuchCatalogException If the catalog does not exist.
+   * @throws CatalogAlreadyExistsException If the new name is already used by 
another catalog.
    * @throws IllegalArgumentException If the changes cannot be applied to the 
catalog.
    */
   Catalog alterCatalog(String catalogName, CatalogChange... changes)
-      throws NoSuchCatalogException, IllegalArgumentException;
+      throws NoSuchCatalogException, CatalogAlreadyExistsException, 
IllegalArgumentException;
 
   /**
    * Drop a catalog with specified name. Please make sure:
diff --git a/api/src/main/java/org/apache/gravitino/SupportsMetalakes.java 
b/api/src/main/java/org/apache/gravitino/SupportsMetalakes.java
index 515b2d836c..31a382ab8f 100644
--- a/api/src/main/java/org/apache/gravitino/SupportsMetalakes.java
+++ b/api/src/main/java/org/apache/gravitino/SupportsMetalakes.java
@@ -83,10 +83,11 @@ public interface SupportsMetalakes {
    * @param changes The changes to apply.
    * @return The altered metalake.
    * @throws NoSuchMetalakeException If the metalake does not exist.
+   * @throws MetalakeAlreadyExistsException If the new name is already used by 
another metalake.
    * @throws IllegalArgumentException If the changes cannot be applied to the 
metalake.
    */
   Metalake alterMetalake(String name, MetalakeChange... changes)
-      throws NoSuchMetalakeException, IllegalArgumentException;
+      throws NoSuchMetalakeException, MetalakeAlreadyExistsException, 
IllegalArgumentException;
 
   /**
    * Drop a metalake with specified name. Please make sure:
diff --git a/api/src/main/java/org/apache/gravitino/file/FilesetCatalog.java 
b/api/src/main/java/org/apache/gravitino/file/FilesetCatalog.java
index c0c1885917..4bf8e11bd9 100644
--- a/api/src/main/java/org/apache/gravitino/file/FilesetCatalog.java
+++ b/api/src/main/java/org/apache/gravitino/file/FilesetCatalog.java
@@ -140,10 +140,11 @@ public interface FilesetCatalog {
    * @param changes The changes to apply to the fileset.
    * @return The altered fileset metadata.
    * @throws NoSuchFilesetException If the fileset does not exist.
+   * @throws FilesetAlreadyExistsException If the new name is already used by 
another fileset.
    * @throws IllegalArgumentException If the change is rejected by the 
implementation.
    */
   Fileset alterFileset(NameIdentifier ident, FilesetChange... changes)
-      throws NoSuchFilesetException, IllegalArgumentException;
+      throws NoSuchFilesetException, FilesetAlreadyExistsException, 
IllegalArgumentException;
 
   /**
    * Drop a fileset from the catalog.
diff --git a/api/src/main/java/org/apache/gravitino/model/ModelCatalog.java 
b/api/src/main/java/org/apache/gravitino/model/ModelCatalog.java
index e10df19ce2..233d41c83c 100644
--- a/api/src/main/java/org/apache/gravitino/model/ModelCatalog.java
+++ b/api/src/main/java/org/apache/gravitino/model/ModelCatalog.java
@@ -312,10 +312,11 @@ public interface ModelCatalog {
    * @param changes the several {@link ModelChange} instances to apply to the 
model
    * @return the updated {@link Model} instance
    * @throws NoSuchModelException If the model does not exist
+   * @throws ModelAlreadyExistsException If the new name is already used by 
another model.
    * @throws IllegalArgumentException If the change is rejected by the 
implementation
    */
   Model alterModel(NameIdentifier ident, ModelChange... changes)
-      throws NoSuchModelException, IllegalArgumentException;
+      throws NoSuchModelException, ModelAlreadyExistsException, 
IllegalArgumentException;
 
   /**
    * Applies the specified {@link ModelVersionChange changes} to a model 
version identified by its
diff --git 
a/api/src/main/java/org/apache/gravitino/policy/PolicyOperations.java 
b/api/src/main/java/org/apache/gravitino/policy/PolicyOperations.java
index 1bbd8062ac..644459654c 100644
--- a/api/src/main/java/org/apache/gravitino/policy/PolicyOperations.java
+++ b/api/src/main/java/org/apache/gravitino/policy/PolicyOperations.java
@@ -94,10 +94,11 @@ public interface PolicyOperations {
    * @param changes The changes to apply to the policy.
    * @return The altered policy.
    * @throws NoSuchPolicyException If the policy does not exist.
+   * @throws PolicyAlreadyExistsException If the new name is already used by 
another policy.
    * @throws IllegalArgumentException If the changes cannot be associated with 
the policy.
    */
   Policy alterPolicy(String name, PolicyChange... changes)
-      throws NoSuchPolicyException, IllegalArgumentException;
+      throws NoSuchPolicyException, PolicyAlreadyExistsException, 
IllegalArgumentException;
 
   /**
    * Delete a policy under a metalake.
diff --git a/api/src/main/java/org/apache/gravitino/rel/TableCatalog.java 
b/api/src/main/java/org/apache/gravitino/rel/TableCatalog.java
index 888526ffd5..a5ed9c88e7 100644
--- a/api/src/main/java/org/apache/gravitino/rel/TableCatalog.java
+++ b/api/src/main/java/org/apache/gravitino/rel/TableCatalog.java
@@ -281,10 +281,11 @@ public interface TableCatalog {
    * @param changes Table changes to apply to the table.
    * @return The updated table metadata.
    * @throws NoSuchTableException If the table does not exist.
+   * @throws TableAlreadyExistsException If the new name is already used by 
another table.
    * @throws IllegalArgumentException If the change is rejected by the 
implementation.
    */
   Table alterTable(NameIdentifier ident, TableChange... changes)
-      throws NoSuchTableException, IllegalArgumentException;
+      throws NoSuchTableException, TableAlreadyExistsException, 
IllegalArgumentException;
 
   /**
    * Removes both the metadata and the directory associated with the table 
from the file system if
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 433c52401b..62db7197de 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.EntityAlreadyExistsException;
 import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.EntityStore;
 import org.apache.gravitino.GravitinoEnv;
@@ -97,7 +98,6 @@ import org.apache.gravitino.credential.CredentialConstants;
 import org.apache.gravitino.credential.CredentialUtils;
 import org.apache.gravitino.credential.PathBasedCredentialContext;
 import org.apache.gravitino.dto.file.FileInfoDTO;
-import org.apache.gravitino.exceptions.AlreadyExistsException;
 import org.apache.gravitino.exceptions.ConnectionFailedException;
 import org.apache.gravitino.exceptions.FilesetAlreadyExistsException;
 import org.apache.gravitino.exceptions.GravitinoRuntimeException;
@@ -123,6 +123,7 @@ import org.apache.gravitino.utils.FilesetUtil;
 import org.apache.gravitino.utils.NameIdentifierUtil;
 import org.apache.gravitino.utils.NamespaceUtil;
 import org.apache.gravitino.utils.PrincipalUtils;
+import org.apache.gravitino.utils.RenameUtils;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileStatus;
 import org.apache.hadoop.fs.FileSystem;
@@ -656,7 +657,7 @@ public class FilesetCatalogOperations extends 
ManagedSchemaOperations
 
   @Override
   public Fileset alterFileset(NameIdentifier ident, FilesetChange... changes)
-      throws NoSuchFilesetException, IllegalArgumentException {
+      throws NoSuchFilesetException, FilesetAlreadyExistsException, 
IllegalArgumentException {
     try {
       if (!store.exists(ident, Entity.EntityType.FILESET)) {
         throw new NoSuchFilesetException(FILESET_DOES_NOT_EXIST_MSG, ident);
@@ -685,10 +686,15 @@ public class FilesetCatalogOperations extends 
ManagedSchemaOperations
       throw ExceptionMessages.wrap("Failed to update fileset " + ident, ioe);
     } catch (NoSuchEntityException nsee) {
       throw new NoSuchFilesetException(nsee, FILESET_DOES_NOT_EXIST_MSG, 
ident);
-    } catch (AlreadyExistsException aee) {
-      // This is happened when renaming a fileset to an existing fileset name.
-      throw ExceptionMessages.wrap(
-          "Fileset with the same name " + ident.name() + " already exists", 
aee);
+    } catch (EntityAlreadyExistsException e) {
+      String newName =
+          RenameUtils.getLastRenameName(
+              ident.name(),
+              changes,
+              FilesetChange.RenameFileset.class,
+              FilesetChange.RenameFileset::getNewName);
+      throw new FilesetAlreadyExistsException(
+          e, "Fileset %s already exists", NameIdentifier.of(ident.namespace(), 
newName));
     }
   }
 
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 378dd580b7..da358bcc46 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
@@ -74,6 +74,7 @@ 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.EntityAlreadyExistsException;
 import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.EntityStore;
 import org.apache.gravitino.EntityStoreFactory;
@@ -99,6 +100,7 @@ import 
org.apache.gravitino.credential.CatalogCredentialManager;
 import org.apache.gravitino.credential.Credential;
 import org.apache.gravitino.credential.CredentialConstants;
 import org.apache.gravitino.exceptions.ConnectionFailedException;
+import org.apache.gravitino.exceptions.FilesetAlreadyExistsException;
 import org.apache.gravitino.exceptions.GravitinoRuntimeException;
 import org.apache.gravitino.exceptions.NoSuchFilesetException;
 import org.apache.gravitino.exceptions.NoSuchSchemaException;
@@ -1155,6 +1157,45 @@ public class TestFilesetCatalogOperations {
     }
   }
 
+  @ParameterizedTest
+  @EnumSource(Fileset.Type.class)
+  void testRenameFilesetToExistingName(Fileset.Type type) throws IOException {
+    long testId = generateTestId();
+    String schemaName = "rename_conflict_" + testId;
+    String schemaPath = TEST_ROOT_PATH + "/" + schemaName;
+    createSchema(testId, schemaName, "schema", null, schemaPath);
+    Fileset source =
+        createFileset("source", schemaName, "source comment", type, null, 
schemaPath + "/source");
+    Fileset target =
+        createFileset("target", schemaName, "target comment", type, null, 
schemaPath + "/target");
+    NameIdentifier sourceIdent = NameIdentifier.of("m1", "c1", schemaName, 
"source");
+    NameIdentifier targetIdent = NameIdentifier.of("m1", "c1", schemaName, 
"target");
+
+    try (FilesetCatalogOperations ops = new FilesetCatalogOperations(store)) {
+      ops.initialize(Maps.newHashMap(), randomCatalogInfo("m1", "c1"), 
FILESET_PROPERTIES_METADATA);
+      for (FilesetChange[] changes :
+          new FilesetChange[][] {
+            {FilesetChange.rename("target")},
+            {FilesetChange.rename("intermediate"), 
FilesetChange.rename("target")}
+          }) {
+        FilesetAlreadyExistsException exception =
+            Assertions.assertThrows(
+                FilesetAlreadyExistsException.class, () -> 
ops.alterFileset(sourceIdent, changes));
+        Assertions.assertEquals(
+            "Fileset " + targetIdent + " already exists", 
exception.getMessage());
+        Assertions.assertInstanceOf(EntityAlreadyExistsException.class, 
exception.getCause());
+        Assertions.assertEquals(source.comment(), 
ops.loadFileset(sourceIdent).comment());
+        Assertions.assertEquals(
+            source.storageLocations(), 
ops.loadFileset(sourceIdent).storageLocations());
+        Assertions.assertEquals(target.comment(), 
ops.loadFileset(targetIdent).comment());
+        Assertions.assertEquals(
+            target.storageLocations(), 
ops.loadFileset(targetIdent).storageLocations());
+        Assertions.assertFalse(
+            ops.filesetExists(NameIdentifier.of(sourceIdent.namespace(), 
"intermediate")));
+      }
+    }
+  }
+
   @Test
   public void testAlterFilesetProperties() throws IOException {
     final long testId = generateTestId();
diff --git 
a/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
 
b/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
index 1d0c232366..86dfa19d21 100644
--- 
a/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
+++ 
b/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
@@ -58,6 +58,7 @@ import org.apache.gravitino.utils.ExceptionMessages;
 import org.apache.gravitino.utils.NameIdentifierUtil;
 import org.apache.gravitino.utils.NamespaceUtil;
 import org.apache.gravitino.utils.PrincipalUtils;
+import org.apache.gravitino.utils.RenameUtils;
 
 public class ModelCatalogOperations extends ManagedSchemaOperations
     implements CatalogOperations, ModelCatalog {
@@ -301,7 +302,7 @@ public class ModelCatalogOperations extends 
ManagedSchemaOperations
   /** {@inheritDoc} */
   @Override
   public Model alterModel(NameIdentifier ident, ModelChange... changes)
-      throws NoSuchModelException, IllegalArgumentException {
+      throws NoSuchModelException, ModelAlreadyExistsException, 
IllegalArgumentException {
     try {
       if (!store.exists(ident, Entity.EntityType.MODEL)) {
         throw new NoSuchModelException("Model %s does not exist", ident);
@@ -324,9 +325,15 @@ public class ModelCatalogOperations extends 
ManagedSchemaOperations
       throw ExceptionMessages.wrap("Failed to load model " + ident, ioe);
     } catch (NoSuchEntityException nsee) {
       throw new NoSuchModelException(nsee, "Model %s does not exist", ident);
-    } catch (EntityAlreadyExistsException eaee) {
-      // This is happened when renaming a model to an existing model name.
-      throw ExceptionMessages.wrap("Model already exist " + ident.name(), 
eaee);
+    } catch (EntityAlreadyExistsException e) {
+      String newName =
+          RenameUtils.getLastRenameName(
+              ident.name(),
+              changes,
+              ModelChange.RenameModel.class,
+              ModelChange.RenameModel::newName);
+      throw new ModelAlreadyExistsException(
+          e, "Model %s already exists", NameIdentifier.of(ident.namespace(), 
newName));
     }
   }
 
diff --git 
a/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
 
b/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
index 34fcc5c900..6c5aeb0a0d 100644
--- 
a/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
+++ 
b/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
@@ -54,6 +54,7 @@ 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.EntityAlreadyExistsException;
 import org.apache.gravitino.EntityStore;
 import org.apache.gravitino.EntityStoreFactory;
 import org.apache.gravitino.GravitinoEnv;
@@ -942,6 +943,39 @@ public class TestModelCatalogOperations {
     Assertions.assertEquals(properties, alteredModel.properties());
   }
 
+  @Test
+  void testRenameModelToExistingName() {
+    String schemaName = randomSchemaName();
+    createSchema(schemaName);
+    NameIdentifier source =
+        NameIdentifierUtil.ofModel(METALAKE_NAME, CATALOG_NAME, schemaName, 
"source");
+    NameIdentifier target = NameIdentifier.of(source.namespace(), "target");
+    ops.registerModel(
+        source,
+        "source comment",
+        
StringIdentifier.newPropertiesWithId(StringIdentifier.fromId(idGenerator.nextId()),
 null));
+    ops.registerModel(
+        target,
+        "target comment",
+        
StringIdentifier.newPropertiesWithId(StringIdentifier.fromId(idGenerator.nextId()),
 null));
+
+    for (ModelChange[] changes :
+        new ModelChange[][] {
+          {ModelChange.rename("target")},
+          {ModelChange.rename("intermediate"), ModelChange.rename("target")}
+        }) {
+      ModelAlreadyExistsException exception =
+          Assertions.assertThrows(
+              ModelAlreadyExistsException.class, () -> ops.alterModel(source, 
changes));
+      Assertions.assertEquals("Model " + target + " already exists", 
exception.getMessage());
+      Assertions.assertInstanceOf(EntityAlreadyExistsException.class, 
exception.getCause());
+      Assertions.assertEquals("source comment", 
ops.getModel(source).comment());
+      Assertions.assertEquals("target comment", 
ops.getModel(target).comment());
+      Assertions.assertFalse(
+          ops.modelExists(NameIdentifier.of(source.namespace(), 
"intermediate")));
+    }
+  }
+
   @Test
   void testAddModelProperty() {
     String schemaName = randomSchemaName();
diff --git 
a/core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java 
b/core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java
index 153e8b7002..b840c8d25f 100644
--- a/core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java
+++ b/core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java
@@ -118,6 +118,7 @@ import org.apache.gravitino.utils.ExceptionMessages;
 import org.apache.gravitino.utils.IsolatedClassLoader;
 import org.apache.gravitino.utils.NamespaceUtil;
 import org.apache.gravitino.utils.PrincipalUtils;
+import org.apache.gravitino.utils.RenameUtils;
 import org.apache.gravitino.utils.ThrowableFunction;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -1153,11 +1154,12 @@ public class CatalogManager implements 
CatalogDispatcher, Closeable {
    * @param changes The changes to apply to the catalog.
    * @return A metadata snapshot of the altered catalog.
    * @throws NoSuchCatalogException If the specified catalog does not exist.
+   * @throws CatalogAlreadyExistsException If the new name is already used by 
another catalog.
    * @throws IllegalArgumentException If an unsupported catalog change is 
provided.
    */
   @Override
   public Catalog alterCatalog(NameIdentifier ident, CatalogChange... changes)
-      throws NoSuchCatalogException, IllegalArgumentException {
+      throws NoSuchCatalogException, CatalogAlreadyExistsException, 
IllegalArgumentException {
 
     TreeLockUtils.doWithTreeLock(
         ident,
@@ -1254,6 +1256,16 @@ public class CatalogManager implements 
CatalogDispatcher, Closeable {
             LOG.warn("Failed to alter catalog {} with unknown change", ident, 
iae);
             throw iae;
 
+          } catch (EntityAlreadyExistsException e) {
+            String newName =
+                RenameUtils.getLastRenameName(
+                    ident.name(),
+                    changes,
+                    CatalogChange.RenameCatalog.class,
+                    CatalogChange.RenameCatalog::getNewName);
+            throw new CatalogAlreadyExistsException(
+                e, "Catalog %s already exists", 
NameIdentifier.of(ident.namespace(), newName));
+
           } catch (IOException ioe) {
             LOG.error("Failed to alter catalog {}", ident, ioe);
             throw new RuntimeException(ioe);
diff --git 
a/core/src/main/java/org/apache/gravitino/catalog/ManagedTableOperations.java 
b/core/src/main/java/org/apache/gravitino/catalog/ManagedTableOperations.java
index 0e226abcbb..6b48716b0f 100644
--- 
a/core/src/main/java/org/apache/gravitino/catalog/ManagedTableOperations.java
+++ 
b/core/src/main/java/org/apache/gravitino/catalog/ManagedTableOperations.java
@@ -30,6 +30,7 @@ import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 import org.apache.gravitino.Entity;
@@ -164,10 +165,11 @@ public abstract class ManagedTableOperations implements 
TableCatalog {
 
   @Override
   public Table alterTable(NameIdentifier ident, TableChange... changes)
-      throws NoSuchTableException, IllegalArgumentException {
+      throws NoSuchTableException, TableAlreadyExistsException, 
IllegalArgumentException {
     // The alterTable in ManagedTableOperations only updates the table 
metadata in the entity store.
     // It doesn't handle any additional operations like modifying physical 
data, etc. Those
     // operations should be handled in the specific catalog implementation.
+    AtomicReference<NameIdentifier> targetIdent = new AtomicReference<>(ident);
     try {
       TableEntity newTableEntity =
           store()
@@ -175,14 +177,17 @@ public abstract class ManagedTableOperations implements 
TableCatalog {
                   ident,
                   TableEntity.class,
                   Entity.EntityType.TABLE,
-                  oldEntity -> applyChanges(oldEntity, changes));
+                  oldEntity -> {
+                    TableEntity updatedEntity = applyChanges(oldEntity, 
changes);
+                    targetIdent.set(updatedEntity.nameIdentifier());
+                    return updatedEntity;
+                  });
 
       return toGenericTable(newTableEntity);
     } catch (NoSuchEntityException e) {
       throw new NoSuchTableException(e, "Table %s does not exist", ident);
     } catch (EntityAlreadyExistsException e) {
-      throw new IllegalArgumentException(
-          "Failed to rename table " + ident + " due to table already exists: 
", e);
+      throw new TableAlreadyExistsException(e, "Table %s already exists", 
targetIdent.get());
     } catch (IOException e) {
       throw new RuntimeException("Failed to alter table " + ident, e);
     }
@@ -233,7 +238,7 @@ public abstract class ManagedTableOperations implements 
TableCatalog {
             rename
                 .getNewSchemaName()
                 .map(s -> NamespaceUtil.ofTable(oldNs.level(0), 
oldNs.level(1), s))
-                .orElse(oldNs);
+                .orElse(newNs);
 
       } else if (change instanceof TableChange.UpdateComment updateComment) {
         newComment = updateComment.getNewComment();
diff --git 
a/core/src/main/java/org/apache/gravitino/metalake/MetalakeManager.java 
b/core/src/main/java/org/apache/gravitino/metalake/MetalakeManager.java
index b2a01b6a06..fca345ad85 100644
--- a/core/src/main/java/org/apache/gravitino/metalake/MetalakeManager.java
+++ b/core/src/main/java/org/apache/gravitino/metalake/MetalakeManager.java
@@ -56,6 +56,7 @@ import org.apache.gravitino.storage.IdGenerator;
 import org.apache.gravitino.utils.Executable;
 import org.apache.gravitino.utils.NameIdentifierUtil;
 import org.apache.gravitino.utils.PrincipalUtils;
+import org.apache.gravitino.utils.RenameUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -305,12 +306,13 @@ public class MetalakeManager implements 
MetalakeDispatcher, Closeable {
    * @param changes The array of MetalakeChange objects representing the 
changes to apply.
    * @return The altered Metalake instance after applying the changes.
    * @throws NoSuchMetalakeException If the Metalake with the given identifier 
does not exist.
+   * @throws MetalakeAlreadyExistsException If the new name is already used by 
another metalake.
    * @throws IllegalArgumentException If the provided changes are invalid.
    * @throws RuntimeException If altering the Metalake encounters storage 
issues.
    */
   @Override
   public BaseMetalake alterMetalake(NameIdentifier ident, MetalakeChange... 
changes)
-      throws NoSuchMetalakeException, IllegalArgumentException {
+      throws NoSuchMetalakeException, MetalakeAlreadyExistsException, 
IllegalArgumentException {
     Executable<BaseMetalake, RuntimeException> exceptionExecutable =
         () -> {
           try {
@@ -341,6 +343,15 @@ public class MetalakeManager implements 
MetalakeDispatcher, Closeable {
             LOG.warn("Altering Metalake {} failed due to invalid changes", 
ident, iae);
             throw iae;
 
+          } catch (EntityAlreadyExistsException e) {
+            String newName =
+                RenameUtils.getLastRenameName(
+                    ident.name(),
+                    changes,
+                    MetalakeChange.RenameMetalake.class,
+                    MetalakeChange.RenameMetalake::getNewName);
+            throw new MetalakeAlreadyExistsException(e, "Metalake %s already 
exists", newName);
+
           } catch (IOException ioe) {
             LOG.error("Loading Metalake {} failed due to storage issues", 
ident, ioe);
             throw new RuntimeException(ioe);
diff --git a/core/src/main/java/org/apache/gravitino/policy/PolicyManager.java 
b/core/src/main/java/org/apache/gravitino/policy/PolicyManager.java
index 22256fba5f..0e9f14349d 100644
--- a/core/src/main/java/org/apache/gravitino/policy/PolicyManager.java
+++ b/core/src/main/java/org/apache/gravitino/policy/PolicyManager.java
@@ -50,6 +50,7 @@ import org.apache.gravitino.utils.MetadataObjectUtil;
 import org.apache.gravitino.utils.NameIdentifierUtil;
 import org.apache.gravitino.utils.NamespaceUtil;
 import org.apache.gravitino.utils.PrincipalUtils;
+import org.apache.gravitino.utils.RenameUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -175,7 +176,8 @@ public class PolicyManager implements PolicyDispatcher {
   }
 
   @Override
-  public PolicyEntity alterPolicy(String metalake, String policyName, 
PolicyChange... changes) {
+  public PolicyEntity alterPolicy(String metalake, String policyName, 
PolicyChange... changes)
+      throws NoSuchPolicyException, PolicyAlreadyExistsException {
     NameIdentifier metalakeIdent = NameIdentifierUtil.ofMetalake(metalake);
     checkMetalake(metalakeIdent, entityStore);
     return TreeLockUtils.doWithTreeLock(
@@ -192,10 +194,14 @@ public class PolicyManager implements PolicyDispatcher {
             throw new NoSuchPolicyException(
                 "Policy with name %s under metalake %s does not exist", 
policyName, metalake);
           } catch (EntityAlreadyExistsException e) {
-            throw new RuntimeException(
-                String.format(
-                    "Trying to alter policy %s under metalake %s, but the new 
name already exists",
-                    policyName, metalake));
+            String newName =
+                RenameUtils.getLastRenameName(
+                    policyName,
+                    changes,
+                    PolicyChange.RenamePolicy.class,
+                    PolicyChange.RenamePolicy::getNewName);
+            throw new PolicyAlreadyExistsException(
+                e, "Policy with name %s under metalake %s already exists", 
newName, metalake);
           } catch (IOException ioe) {
             LOG.error("Failed to alter policy {} under metalake {}", 
policyName, metalake, ioe);
             throw new RuntimeException(ioe);
diff --git a/core/src/main/java/org/apache/gravitino/tag/TagManager.java 
b/core/src/main/java/org/apache/gravitino/tag/TagManager.java
index 49b8e112b4..971001ae6b 100644
--- a/core/src/main/java/org/apache/gravitino/tag/TagManager.java
+++ b/core/src/main/java/org/apache/gravitino/tag/TagManager.java
@@ -53,6 +53,7 @@ import org.apache.gravitino.utils.MetadataObjectUtil;
 import org.apache.gravitino.utils.NameIdentifierUtil;
 import org.apache.gravitino.utils.NamespaceUtil;
 import org.apache.gravitino.utils.PrincipalUtils;
+import org.apache.gravitino.utils.RenameUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -176,11 +177,8 @@ public class TagManager implements TagDispatcher {
                 "Tag with name %s under metalake %s does not exist", name, 
metalake);
           } catch (EntityAlreadyExistsException e) {
             String newName =
-                Arrays.stream(changes)
-                    .filter(c -> c instanceof TagChange.RenameTag)
-                    .map(c -> ((TagChange.RenameTag) c).getNewName())
-                    .findFirst()
-                    .orElse(name);
+                RenameUtils.getLastRenameName(
+                    name, changes, TagChange.RenameTag.class, 
TagChange.RenameTag::getNewName);
             throw new TagAlreadyExistsException(
                 e, "Tag with name %s under metalake %s already exists", 
newName, metalake);
           } catch (IOException ioe) {
diff --git a/core/src/main/java/org/apache/gravitino/utils/RenameUtils.java 
b/core/src/main/java/org/apache/gravitino/utils/RenameUtils.java
new file mode 100644
index 0000000000..34df8c7da0
--- /dev/null
+++ b/core/src/main/java/org/apache/gravitino/utils/RenameUtils.java
@@ -0,0 +1,51 @@
+/*
+ * 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.utils;
+
+import java.util.function.Function;
+
+/** Utilities for resolving names from ordered rename changes. */
+public final class RenameUtils {
+
+  private RenameUtils() {}
+
+  /**
+   * Returns the name from the last rename change, or the original name if 
there is no rename.
+   *
+   * <p>This resolves only the name, not namespace changes such as moving a 
table to another schema.
+   *
+   * @param originalName the name before applying changes
+   * @param changes the changes in application order
+   * @param renameType the type of rename change
+   * @param nameExtractor extracts the new name from a rename change
+   * @param <C> the change type
+   * @param <R> the rename change type
+   * @return the final name after applying the rename changes
+   */
+  public static <C, R extends C> String getLastRenameName(
+      String originalName, C[] changes, Class<R> renameType, Function<R, 
String> nameExtractor) {
+    String name = originalName;
+    for (C change : changes) {
+      if (renameType.isInstance(change)) {
+        name = nameExtractor.apply(renameType.cast(change));
+      }
+    }
+    return name;
+  }
+}
diff --git 
a/core/src/test/java/org/apache/gravitino/policy/TestPolicyManager.java 
b/core/src/test/java/org/apache/gravitino/policy/TestPolicyManager.java
index 181af71e4a..fced5fc92b 100644
--- a/core/src/test/java/org/apache/gravitino/policy/TestPolicyManager.java
+++ b/core/src/test/java/org/apache/gravitino/policy/TestPolicyManager.java
@@ -57,6 +57,7 @@ import org.apache.gravitino.Catalog;
 import org.apache.gravitino.Config;
 import org.apache.gravitino.Configs;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityAlreadyExistsException;
 import org.apache.gravitino.EntityStore;
 import org.apache.gravitino.EntityStoreFactory;
 import org.apache.gravitino.GravitinoEnv;
@@ -321,6 +322,32 @@ public class TestPolicyManager {
         e.getMessage());
   }
 
+  @Test
+  void testRenamePolicyToExistingName() {
+    PolicyContent content =
+        PolicyContents.custom(ImmutableMap.of("rule", "value"), 
SUPPORTS_OBJECT_TYPES, null);
+    PolicyEntity source = createCustomPolicy(METALAKE, "source", content);
+    PolicyEntity target = createCustomPolicy(METALAKE, "target", content);
+    for (PolicyChange[] changes :
+        new PolicyChange[][] {
+          {PolicyChange.rename("target")},
+          {PolicyChange.rename("intermediate"), PolicyChange.rename("target")}
+        }) {
+      PolicyAlreadyExistsException exception =
+          Assertions.assertThrows(
+              PolicyAlreadyExistsException.class,
+              () -> policyManager.alterPolicy(METALAKE, "source", changes));
+      Assertions.assertEquals(
+          "Policy with name target under metalake " + METALAKE + " already 
exists",
+          exception.getMessage());
+      Assertions.assertInstanceOf(EntityAlreadyExistsException.class, 
exception.getCause());
+      Assertions.assertEquals(source.id(), policyManager.getPolicy(METALAKE, 
"source").id());
+      Assertions.assertEquals(target.id(), policyManager.getPolicy(METALAKE, 
"target").id());
+      Assertions.assertThrows(
+          NoSuchPolicyException.class, () -> policyManager.getPolicy(METALAKE, 
"intermediate"));
+    }
+  }
+
   @Test
   public void testCreateAndListPolicies() {
     String policyName1 = "policy1" + UUID.randomUUID().toString().replace("-", 
"");
diff --git 
a/core/src/test/java/org/apache/gravitino/storage/relational/TestRenameConflicts.java
 
b/core/src/test/java/org/apache/gravitino/storage/relational/TestRenameConflicts.java
new file mode 100644
index 0000000000..ce049e2880
--- /dev/null
+++ 
b/core/src/test/java/org/apache/gravitino/storage/relational/TestRenameConflicts.java
@@ -0,0 +1,309 @@
+/*
+ * 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;
+
+import static org.apache.gravitino.TestCatalog.PROPERTY_KEY1;
+import static org.apache.gravitino.TestCatalog.PROPERTY_KEY2;
+import static org.apache.gravitino.TestCatalog.PROPERTY_KEY5_PREFIX;
+
+import java.util.Collections;
+import java.util.Map;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.CatalogChange;
+import org.apache.gravitino.Config;
+import org.apache.gravitino.Configs;
+import org.apache.gravitino.Entity.EntityType;
+import org.apache.gravitino.EntityAlreadyExistsException;
+import org.apache.gravitino.EntityStore;
+import org.apache.gravitino.GravitinoEnv;
+import org.apache.gravitino.MetalakeChange;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.cache.NoOpsCache;
+import org.apache.gravitino.catalog.CatalogManager;
+import org.apache.gravitino.catalog.ManagedTableOperations;
+import org.apache.gravitino.connector.SupportsSchemas;
+import org.apache.gravitino.exceptions.CatalogAlreadyExistsException;
+import org.apache.gravitino.exceptions.MetalakeAlreadyExistsException;
+import org.apache.gravitino.exceptions.TableAlreadyExistsException;
+import org.apache.gravitino.lock.LockManager;
+import org.apache.gravitino.meta.BaseMetalake;
+import org.apache.gravitino.meta.CatalogEntity;
+import org.apache.gravitino.meta.TableEntity;
+import org.apache.gravitino.metalake.MetalakeManager;
+import org.apache.gravitino.rel.TableChange;
+import org.apache.gravitino.rel.expressions.distributions.Distributions;
+import org.apache.gravitino.rel.expressions.sorts.SortOrder;
+import org.apache.gravitino.rel.expressions.transforms.Transform;
+import org.apache.gravitino.rel.indexes.Indexes;
+import org.apache.gravitino.storage.IdGenerator;
+import org.apache.gravitino.storage.RandomIdGenerator;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.TestTemplate;
+import org.mockito.Mockito;
+
+/** Verifies rename conflict translation against real metadata database 
constraints. */
+public class TestRenameConflicts extends TestJDBCBackend {
+  private Config config;
+  private RelationalEntityStore store;
+  private Object previousConfig;
+  private Object previousLockManager;
+  private LockManager lockManager;
+  private EntityChangeLogPoller poller;
+
+  @BeforeEach
+  void setUpManagers() throws IllegalAccessException {
+    config = new Config(false) {};
+    config.set(Configs.CACHE_ENABLED, false);
+    config.set(Configs.CATALOG_LOAD_ISOLATED, false);
+    previousConfig = FieldUtils.readField(GravitinoEnv.getInstance(), 
"config", true);
+    previousLockManager = FieldUtils.readField(GravitinoEnv.getInstance(), 
"lockManager", true);
+    if (lockManager == null) {
+      lockManager = new LockManager(config);
+    }
+    FieldUtils.writeField(GravitinoEnv.getInstance(), "config", config, true);
+    FieldUtils.writeField(GravitinoEnv.getInstance(), "lockManager", 
lockManager, true);
+    store = new RelationalEntityStore();
+    FieldUtils.writeField(store, "backend", backend, true);
+    FieldUtils.writeField(store, "cache", new NoOpsCache(config), true);
+    poller = new EntityChangeLogPoller(3600);
+    FieldUtils.writeField(store, "entityChangeLogPoller", poller, true);
+  }
+
+  @AfterEach
+  void restoreEnvironment() throws Exception {
+    poller.close();
+    FieldUtils.writeField(GravitinoEnv.getInstance(), "config", 
previousConfig, true);
+    FieldUtils.writeField(GravitinoEnv.getInstance(), "lockManager", 
previousLockManager, true);
+  }
+
+  @TestTemplate
+  void testRenameMetalakeToExistingName() throws Exception {
+    BaseMetalake source = createAndInsertMakeLake("source");
+    BaseMetalake target = createAndInsertMakeLake("target");
+    MetalakeManager manager = new MetalakeManager(store, 
RandomIdGenerator.INSTANCE);
+    for (MetalakeChange[] changes :
+        new MetalakeChange[][] {
+          {MetalakeChange.rename("target")},
+          {MetalakeChange.rename("intermediate"), 
MetalakeChange.rename("target")}
+        }) {
+      MetalakeAlreadyExistsException exception =
+          Assertions.assertThrows(
+              MetalakeAlreadyExistsException.class,
+              () -> manager.alterMetalake(source.nameIdentifier(), changes));
+      Assertions.assertEquals("Metalake target already exists", 
exception.getMessage());
+      Assertions.assertInstanceOf(EntityAlreadyExistsException.class, 
exception.getCause());
+      Assertions.assertEquals(source.id(), 
manager.loadMetalake(source.nameIdentifier()).id());
+      Assertions.assertEquals(target.id(), 
manager.loadMetalake(target.nameIdentifier()).id());
+      Assertions.assertFalse(store.exists(NameIdentifier.of("intermediate"), 
EntityType.METALAKE));
+    }
+  }
+
+  @TestTemplate
+  void testRenameCatalogToExistingName() throws Exception {
+    createAndInsertMakeLake("lake");
+    NameIdentifier source = NameIdentifier.of("lake", "source");
+    NameIdentifier target = NameIdentifier.of("lake", "target");
+    Map<String, String> properties =
+        Map.of(
+            "provider",
+            "test",
+            PROPERTY_KEY1,
+            "value1",
+            PROPERTY_KEY2,
+            "value2",
+            PROPERTY_KEY5_PREFIX + "1",
+            "value3");
+    try (CatalogManager manager = new CatalogManager(config, store, 
RandomIdGenerator.INSTANCE)) {
+      manager.createCatalog(source, Catalog.Type.RELATIONAL, "test", "source 
comment", properties);
+      manager.createCatalog(target, Catalog.Type.RELATIONAL, "test", "target 
comment", properties);
+      CatalogEntity originalSource = store.get(source, EntityType.CATALOG, 
CatalogEntity.class);
+      CatalogEntity originalTarget = store.get(target, EntityType.CATALOG, 
CatalogEntity.class);
+      for (CatalogChange[] changes :
+          new CatalogChange[][] {
+            {CatalogChange.rename("target")},
+            {CatalogChange.rename("intermediate"), 
CatalogChange.rename("target")}
+          }) {
+        CatalogAlreadyExistsException exception =
+            Assertions.assertThrows(
+                CatalogAlreadyExistsException.class, () -> 
manager.alterCatalog(source, changes));
+        Assertions.assertEquals("Catalog " + target + " already exists", 
exception.getMessage());
+        Assertions.assertInstanceOf(EntityAlreadyExistsException.class, 
exception.getCause());
+        Assertions.assertEquals(
+            originalSource.id(), store.get(source, EntityType.CATALOG, 
CatalogEntity.class).id());
+        Assertions.assertEquals(
+            originalTarget.id(), store.get(target, EntityType.CATALOG, 
CatalogEntity.class).id());
+        Assertions.assertEquals("source comment", 
manager.loadCatalog(source).comment());
+        Assertions.assertEquals("target comment", 
manager.loadCatalog(target).comment());
+        Assertions.assertFalse(
+            store.exists(NameIdentifier.of("lake", "intermediate"), 
EntityType.CATALOG));
+      }
+    }
+  }
+
+  @TestTemplate
+  void testRenameManagedTableToExistingName() throws Exception {
+    createParentEntities("lake", "catalog", "schema", AUDIT_INFO);
+    createAndInsertSchema("lake", "catalog", "other_schema");
+    TableEntity source = table(Namespace.of("lake", "catalog", "schema"), 
"source");
+    TableEntity target = table(source.namespace(), "target");
+    TableEntity crossSchemaTarget =
+        table(Namespace.of("lake", "catalog", "other_schema"), "target");
+    backend.insert(source, false);
+    backend.insert(target, false);
+    backend.insert(crossSchemaTarget, false);
+    ManagedTableOperations operations = managedTableOperations();
+    record RenameCase(TableEntity expectedTarget, TableChange... changes) {}
+    for (RenameCase renameCase :
+        new RenameCase[] {
+          new RenameCase(target, TableChange.rename("target")),
+          new RenameCase(target, TableChange.rename("intermediate"), 
TableChange.rename("target")),
+          new RenameCase(crossSchemaTarget, TableChange.rename("target", 
"other_schema")),
+          new RenameCase(
+              crossSchemaTarget,
+              TableChange.rename("intermediate", "other_schema"),
+              TableChange.rename("target")),
+          new RenameCase(
+              target,
+              TableChange.rename("intermediate", "other_schema"),
+              TableChange.rename("intermediate", "schema"),
+              TableChange.rename("target"))
+        }) {
+      TableAlreadyExistsException exception =
+          Assertions.assertThrows(
+              TableAlreadyExistsException.class,
+              () -> operations.alterTable(source.nameIdentifier(), 
renameCase.changes()));
+      Assertions.assertEquals(
+          "Table " + renameCase.expectedTarget().nameIdentifier() + " already 
exists",
+          exception.getMessage());
+      Assertions.assertInstanceOf(EntityAlreadyExistsException.class, 
exception.getCause());
+      Assertions.assertEquals(
+          source.id(),
+          store.get(source.nameIdentifier(), EntityType.TABLE, 
TableEntity.class).id());
+      Assertions.assertEquals(
+          target.id(),
+          store.get(target.nameIdentifier(), EntityType.TABLE, 
TableEntity.class).id());
+      Assertions.assertEquals(
+          crossSchemaTarget.id(),
+          store.get(crossSchemaTarget.nameIdentifier(), EntityType.TABLE, 
TableEntity.class).id());
+      Assertions.assertFalse(
+          store.exists(NameIdentifier.of(source.namespace(), "intermediate"), 
EntityType.TABLE));
+      Assertions.assertFalse(
+          store.exists(
+              NameIdentifier.of(crossSchemaTarget.namespace(), 
"intermediate"), EntityType.TABLE));
+    }
+  }
+
+  @TestTemplate
+  void testRenameManagedTableKeepsEarlierSchema() throws Exception {
+    createParentEntities("lake", "catalog", "schema", AUDIT_INFO);
+    createAndInsertSchema("lake", "catalog", "other_schema");
+    TableEntity source = table(Namespace.of("lake", "catalog", "schema"), 
"source");
+    TableEntity originalSchemaTarget = table(source.namespace(), "target");
+    backend.insert(source, false);
+    backend.insert(originalSchemaTarget, false);
+
+    managedTableOperations()
+        .alterTable(
+            source.nameIdentifier(),
+            TableChange.rename("intermediate", "other_schema"),
+            TableChange.rename("target"));
+
+    NameIdentifier expectedTarget = NameIdentifier.of("lake", "catalog", 
"other_schema", "target");
+    Assertions.assertEquals(
+        source.id(), store.get(expectedTarget, EntityType.TABLE, 
TableEntity.class).id());
+    Assertions.assertEquals(
+        originalSchemaTarget.id(),
+        store.get(originalSchemaTarget.nameIdentifier(), EntityType.TABLE, 
TableEntity.class).id());
+    Assertions.assertFalse(store.exists(source.nameIdentifier(), 
EntityType.TABLE));
+    Assertions.assertFalse(
+        store.exists(
+            NameIdentifier.of("lake", "catalog", "other_schema", 
"intermediate"),
+            EntityType.TABLE));
+  }
+
+  @TestTemplate
+  void testRenameManagedTableBackToOriginalSchema() throws Exception {
+    createParentEntities("lake", "catalog", "schema", AUDIT_INFO);
+    createAndInsertSchema("lake", "catalog", "other_schema");
+    TableEntity source = table(Namespace.of("lake", "catalog", "schema"), 
"source");
+    TableEntity crossSchemaTarget =
+        table(Namespace.of("lake", "catalog", "other_schema"), "target");
+    backend.insert(source, false);
+    backend.insert(crossSchemaTarget, false);
+
+    managedTableOperations()
+        .alterTable(
+            source.nameIdentifier(),
+            TableChange.rename("intermediate", "other_schema"),
+            TableChange.rename("intermediate", "schema"),
+            TableChange.rename("target"));
+
+    NameIdentifier expectedTarget = NameIdentifier.of(source.namespace(), 
"target");
+    Assertions.assertEquals(
+        source.id(), store.get(expectedTarget, EntityType.TABLE, 
TableEntity.class).id());
+    Assertions.assertEquals(
+        crossSchemaTarget.id(),
+        store.get(crossSchemaTarget.nameIdentifier(), EntityType.TABLE, 
TableEntity.class).id());
+    Assertions.assertFalse(store.exists(source.nameIdentifier(), 
EntityType.TABLE));
+    Assertions.assertFalse(
+        store.exists(NameIdentifier.of(source.namespace(), "intermediate"), 
EntityType.TABLE));
+    Assertions.assertFalse(
+        store.exists(
+            NameIdentifier.of("lake", "catalog", "other_schema", 
"intermediate"),
+            EntityType.TABLE));
+  }
+
+  private ManagedTableOperations managedTableOperations() {
+    return new ManagedTableOperations() {
+      @Override
+      protected EntityStore store() {
+        return store;
+      }
+
+      @Override
+      protected SupportsSchemas schemas() {
+        return Mockito.mock(SupportsSchemas.class);
+      }
+
+      @Override
+      protected IdGenerator idGenerator() {
+        return RandomIdGenerator.INSTANCE;
+      }
+    };
+  }
+
+  private TableEntity table(Namespace namespace, String name) {
+    return TableEntity.builder()
+        .withId(RandomIdGenerator.INSTANCE.nextId())
+        .withName(name)
+        .withNamespace(namespace)
+        .withProperties(Collections.emptyMap())
+        .withColumns(Collections.emptyList())
+        .withPartitioning(new Transform[0])
+        .withSortOrders(new SortOrder[0])
+        .withIndexes(Indexes.EMPTY_INDEXES)
+        .withDistribution(Distributions.NONE)
+        .withAuditInfo(AUDIT_INFO)
+        .build();
+  }
+}
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 57182308b2..37a2f19ea1 100644
--- a/core/src/test/java/org/apache/gravitino/tag/TestTagManager.java
+++ b/core/src/test/java/org/apache/gravitino/tag/TestTagManager.java
@@ -57,6 +57,7 @@ import org.apache.gravitino.Catalog;
 import org.apache.gravitino.Config;
 import org.apache.gravitino.Configs;
 import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityAlreadyExistsException;
 import org.apache.gravitino.EntityFieldLimits;
 import org.apache.gravitino.EntityStore;
 import org.apache.gravitino.EntityStoreFactory;
@@ -498,6 +499,37 @@ public class TestTagManager {
     Assertions.assertEquals("tag2", tagManager.getTag(METALAKE, 
"tag2").name());
   }
 
+  @Test
+  void testAlterTagMultipleRenamesToExistingTag() {
+    Tag source = tagManager.createTag(METALAKE, "tag1", "source", 
ImmutableMap.of("key", "source"));
+    Tag target = tagManager.createTag(METALAKE, "tag2", "target", 
ImmutableMap.of("key", "target"));
+
+    TagAlreadyExistsException exception =
+        Assertions.assertThrows(
+            TagAlreadyExistsException.class,
+            () ->
+                tagManager.alterTag(
+                    METALAKE,
+                    "tag1",
+                    TagChange.rename("intermediate"),
+                    TagChange.updateComment("changed"),
+                    TagChange.setProperty("key", "changed"),
+                    TagChange.rename("tag2")));
+
+    Assertions.assertEquals(
+        "Tag with name tag2 under metalake metalake_for_tag_test already 
exists",
+        exception.getMessage());
+    Assertions.assertInstanceOf(EntityAlreadyExistsException.class, 
exception.getCause());
+    for (Tag original : new Tag[] {source, target}) {
+      Tag unchanged = tagManager.getTag(METALAKE, original.name());
+      Assertions.assertEquals(original.name(), unchanged.name());
+      Assertions.assertEquals(original.comment(), unchanged.comment());
+      Assertions.assertEquals(original.properties(), unchanged.properties());
+    }
+    Assertions.assertThrows(
+        NoSuchTagException.class, () -> tagManager.getTag(METALAKE, 
"intermediate"));
+  }
+
   @Test
   public void testDeleteTag() {
     tagManager.createTag(METALAKE, "tag1", null, null);
diff --git a/core/src/test/java/org/apache/gravitino/utils/TestRenameUtils.java 
b/core/src/test/java/org/apache/gravitino/utils/TestRenameUtils.java
new file mode 100644
index 0000000000..8abc37c113
--- /dev/null
+++ b/core/src/test/java/org/apache/gravitino/utils/TestRenameUtils.java
@@ -0,0 +1,63 @@
+/*
+ * 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.utils;
+
+import org.apache.gravitino.tag.TagChange;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class TestRenameUtils {
+
+  @Test
+  void testEmptyChangesKeepOriginalName() {
+    Assertions.assertEquals(
+        "original",
+        RenameUtils.getLastRenameName(
+            "original",
+            new TagChange[0],
+            TagChange.RenameTag.class,
+            TagChange.RenameTag::getNewName));
+  }
+
+  @Test
+  void testNonRenameChangesKeepOriginalName() {
+    TagChange[] changes = {
+      TagChange.updateComment("comment"), TagChange.setProperty("key", "value")
+    };
+    Assertions.assertEquals(
+        "original",
+        RenameUtils.getLastRenameName(
+            "original", changes, TagChange.RenameTag.class, 
TagChange.RenameTag::getNewName));
+  }
+
+  @Test
+  void testLastRenameWinsWithOtherChanges() {
+    TagChange[] changes = {
+      TagChange.updateComment("comment"),
+      TagChange.rename("first"),
+      TagChange.setProperty("key", "value"),
+      TagChange.rename("last"),
+      TagChange.removeProperty("key")
+    };
+    Assertions.assertEquals(
+        "last",
+        RenameUtils.getLastRenameName(
+            "original", changes, TagChange.RenameTag.class, 
TagChange.RenameTag::getNewName));
+  }
+}
diff --git a/docs/open-api/models.yaml b/docs/open-api/models.yaml
index 08446ca984..7a82d411a1 100644
--- a/docs/open-api/models.yaml
+++ b/docs/open-api/models.yaml
@@ -133,6 +133,15 @@ paths:
               examples:
                 NoSuchModelException:
                   $ref: "#/components/examples/NoSuchModelException"
+        "409":
+          description: Conflict - The target model already exists
+          content:
+            application/vnd.gravitino.v1+json:
+              schema:
+                $ref: "./openapi.yaml#/components/schemas/ErrorModel"
+              examples:
+                ModelAlreadyExistsErrorResponse:
+                  $ref: "#/components/examples/ModelAlreadyExistsException"
         "5xx":
           $ref: "./openapi.yaml#/components/responses/ServerErrorResponse"
 
diff --git a/docs/open-api/policies.yaml b/docs/open-api/policies.yaml
index 8b6325ff49..7513b3bb72 100644
--- a/docs/open-api/policies.yaml
+++ b/docs/open-api/policies.yaml
@@ -160,6 +160,15 @@ paths:
                   $ref: 
"./metalakes.yaml#/components/examples/NoSuchMetalakeException"
                 NoSuchPolicyException:
                   $ref: "#/components/examples/NoSuchPolicyException"
+        "409":
+          description: Conflict - The target policy already exists
+          content:
+            application/vnd.gravitino.v1+json:
+              schema:
+                $ref: "./openapi.yaml#/components/schemas/ErrorModel"
+              examples:
+                PolicyAlreadyExistsErrorResponse:
+                  $ref: "#/components/examples/PolicyAlreadyExistsException"
         "5xx":
           $ref: "./openapi.yaml#/components/responses/ServerErrorResponse"
 
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestCatalogOperations.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestCatalogOperations.java
index 3d2c697502..cbd66e52dd 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestCatalogOperations.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestCatalogOperations.java
@@ -570,6 +570,27 @@ public class TestCatalogOperations extends 
BaseOperationsTest {
     assertNullRequestBodyRejected(resp);
   }
 
+  @Test
+  void testRenameCatalogToExistingName() {
+    CatalogAlreadyExistsException exception =
+        new CatalogAlreadyExistsException("Catalog existing already exists");
+    doThrow(exception).when(manager).alterCatalog(any(), any());
+    CatalogUpdatesRequest request =
+        new CatalogUpdatesRequest(
+            Collections.singletonList(new 
CatalogUpdateRequest.RenameCatalogRequest("existing")));
+    try (Response response =
+        target("/metalakes/metalake1/catalogs/catalog1")
+            .request(MediaType.APPLICATION_JSON_TYPE)
+            .accept("application/vnd.gravitino.v1+json")
+            .put(Entity.entity(request, MediaType.APPLICATION_JSON_TYPE))) {
+      Assertions.assertEquals(Response.Status.CONFLICT.getStatusCode(), 
response.getStatus());
+      ErrorResponse error = response.readEntity(ErrorResponse.class);
+      Assertions.assertEquals(ErrorConstants.ALREADY_EXISTS_CODE, 
error.getCode());
+      
Assertions.assertEquals(CatalogAlreadyExistsException.class.getSimpleName(), 
error.getType());
+      
Assertions.assertTrue(error.getMessage().contains(exception.getMessage()));
+    }
+  }
+
   @Test
   public void testAlterCatalog() {
     TestCatalog catalog = buildCatalog("metalake1", "catalog2");
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestFilesetOperations.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestFilesetOperations.java
index 544c02a26f..89c8699c52 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestFilesetOperations.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestFilesetOperations.java
@@ -34,6 +34,7 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import java.io.IOException;
 import java.time.Instant;
+import java.util.Collections;
 import java.util.Map;
 import java.util.stream.Stream;
 import javax.servlet.http.HttpServletRequest;
@@ -452,6 +453,27 @@ public class TestFilesetOperations extends 
BaseOperationsTest {
     assertNullRequestBodyRejected(resp);
   }
 
+  @Test
+  void testRenameFilesetToExistingName() {
+    FilesetAlreadyExistsException exception =
+        new FilesetAlreadyExistsException("Fileset existing already exists");
+    doThrow(exception).when(dispatcher).alterFileset(any(), 
any(FilesetChange.class));
+    FilesetUpdatesRequest request =
+        new FilesetUpdatesRequest(
+            Collections.singletonList(new 
FilesetUpdateRequest.RenameFilesetRequest("existing")));
+    try (Response response =
+        target(filesetPath(metalake, catalog, schema) + "fileset1")
+            .request(MediaType.APPLICATION_JSON_TYPE)
+            .accept("application/vnd.gravitino.v1+json")
+            .put(Entity.entity(request, MediaType.APPLICATION_JSON_TYPE))) {
+      Assertions.assertEquals(Response.Status.CONFLICT.getStatusCode(), 
response.getStatus());
+      ErrorResponse error = response.readEntity(ErrorResponse.class);
+      Assertions.assertEquals(ErrorConstants.ALREADY_EXISTS_CODE, 
error.getCode());
+      
Assertions.assertEquals(FilesetAlreadyExistsException.class.getSimpleName(), 
error.getType());
+      
Assertions.assertTrue(error.getMessage().contains(exception.getMessage()));
+    }
+  }
+
   @Test
   public void testRenameFileset() {
     FilesetUpdateRequest req = new 
FilesetUpdateRequest.RenameFilesetRequest("new name");
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetalakeOperations.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetalakeOperations.java
index a2cbd0b251..6eeb2b7669 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetalakeOperations.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetalakeOperations.java
@@ -37,6 +37,7 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import java.io.IOException;
 import java.time.Instant;
+import java.util.Collections;
 import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.GET;
@@ -59,6 +60,7 @@ import org.apache.gravitino.dto.responses.ErrorConstants;
 import org.apache.gravitino.dto.responses.ErrorResponse;
 import org.apache.gravitino.dto.responses.MetalakeListResponse;
 import org.apache.gravitino.dto.responses.MetalakeResponse;
+import org.apache.gravitino.exceptions.MetalakeAlreadyExistsException;
 import org.apache.gravitino.exceptions.NoSuchMetalakeException;
 import org.apache.gravitino.lock.LockManager;
 import org.apache.gravitino.meta.AuditInfo;
@@ -401,6 +403,28 @@ public class TestMetalakeOperations extends 
BaseOperationsTest {
             .contains("Failed to operate object [" + metalakeName + "] 
operation [LOAD]"));
   }
 
+  @Test
+  void testRenameMetalakeToExistingName() {
+    MetalakeAlreadyExistsException exception =
+        new MetalakeAlreadyExistsException("Metalake existing already exists");
+    doThrow(exception).when(metalakeManager).alterMetalake(any(), 
any(MetalakeChange.class));
+    MetalakeUpdatesRequest request =
+        new MetalakeUpdatesRequest(
+            Collections.singletonList(new 
MetalakeUpdateRequest.RenameMetalakeRequest("existing")));
+    try (Response response =
+        target("/metalakes/test")
+            .request(MediaType.APPLICATION_JSON_TYPE)
+            .accept("application/vnd.gravitino.v1+json")
+            .put(Entity.entity(request, MediaType.APPLICATION_JSON_TYPE))) {
+      Assertions.assertEquals(Response.Status.CONFLICT.getStatusCode(), 
response.getStatus());
+      ErrorResponse error = response.readEntity(ErrorResponse.class);
+      Assertions.assertEquals(ErrorConstants.ALREADY_EXISTS_CODE, 
error.getCode());
+      Assertions.assertEquals(
+          MetalakeAlreadyExistsException.class.getSimpleName(), 
error.getType());
+      
Assertions.assertTrue(error.getMessage().contains(exception.getMessage()));
+    }
+  }
+
   @Test
   public void testAlterMetalake() {
     String metalakeName = "test";
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestModelOperations.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestModelOperations.java
index e69ec2a0b2..3be28dc41d 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestModelOperations.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestModelOperations.java
@@ -20,6 +20,7 @@ package org.apache.gravitino.server.web.rest;
 
 import static org.apache.gravitino.Configs.CACHE_ENABLED;
 import static org.apache.gravitino.Configs.ENABLE_AUTHORIZATION;
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
@@ -1082,6 +1083,27 @@ public class TestModelOperations extends 
BaseOperationsTest {
     Assertions.assertEquals(RuntimeException.class.getSimpleName(), 
errorResp2.getType());
   }
 
+  @Test
+  void testRenameModelToExistingName() {
+    ModelAlreadyExistsException exception =
+        new ModelAlreadyExistsException("Model existing already exists");
+    doThrow(exception).when(modelDispatcher).alterModel(any(), 
any(ModelChange.class));
+    ModelUpdatesRequest request =
+        new ModelUpdatesRequest(
+            Collections.singletonList(new 
ModelUpdateRequest.RenameModelRequest("existing")));
+    try (Response response =
+        target(modelPath() + "/model1")
+            .request(MediaType.APPLICATION_JSON_TYPE)
+            .accept("application/vnd.gravitino.v1+json")
+            .put(Entity.entity(request, MediaType.APPLICATION_JSON_TYPE))) {
+      Assertions.assertEquals(Response.Status.CONFLICT.getStatusCode(), 
response.getStatus());
+      ErrorResponse error = response.readEntity(ErrorResponse.class);
+      Assertions.assertEquals(ErrorConstants.ALREADY_EXISTS_CODE, 
error.getCode());
+      
Assertions.assertEquals(ModelAlreadyExistsException.class.getSimpleName(), 
error.getType());
+      
Assertions.assertTrue(error.getMessage().contains(exception.getMessage()));
+    }
+  }
+
   @Test
   public void testRenameModel() {
     String oldName = "model1";
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestPolicyOperations.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestPolicyOperations.java
index 85e45239fe..72d25bf287 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestPolicyOperations.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestPolicyOperations.java
@@ -33,6 +33,7 @@ import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import java.io.IOException;
 import java.time.Instant;
+import java.util.Collections;
 import java.util.Optional;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.client.Entity;
@@ -527,6 +528,27 @@ public class TestPolicyOperations extends 
BaseOperationsTest {
     assertNullRequestBodyRejected(resp);
   }
 
+  @Test
+  void testRenamePolicyToExistingName() {
+    PolicyAlreadyExistsException exception =
+        new PolicyAlreadyExistsException("Policy existing already exists");
+    doThrow(exception).when(policyManager).alterPolicy(any(), any(), 
any(PolicyChange.class));
+    PolicyUpdatesRequest request =
+        new PolicyUpdatesRequest(
+            Collections.singletonList(new 
PolicyUpdateRequest.RenamePolicyRequest("existing")));
+    try (Response response =
+        target(policyPath(metalake) + "/policy1")
+            .request(MediaType.APPLICATION_JSON_TYPE)
+            .accept("application/vnd.gravitino.v1+json")
+            .put(Entity.entity(request, MediaType.APPLICATION_JSON_TYPE))) {
+      Assertions.assertEquals(Response.Status.CONFLICT.getStatusCode(), 
response.getStatus());
+      ErrorResponse error = response.readEntity(ErrorResponse.class);
+      Assertions.assertEquals(ErrorConstants.ALREADY_EXISTS_CODE, 
error.getCode());
+      
Assertions.assertEquals(PolicyAlreadyExistsException.class.getSimpleName(), 
error.getType());
+      
Assertions.assertTrue(error.getMessage().contains(exception.getMessage()));
+    }
+  }
+
   @Test
   public void testAlterPolicy() {
     ImmutableMap<String, Object> contentFields = 
ImmutableMap.of("target_file_size_bytes", 1000);
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestTableOperations.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestTableOperations.java
index 2c8d0fdff1..9864b208d5 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestTableOperations.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestTableOperations.java
@@ -34,6 +34,7 @@ import com.google.common.collect.ImmutableMap;
 import java.io.IOException;
 import java.time.Instant;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -615,6 +616,27 @@ public class TestTableOperations extends 
BaseOperationsTest {
     assertNullRequestBodyRejected(resp);
   }
 
+  @Test
+  void testRenameTableToExistingName() {
+    TableAlreadyExistsException exception =
+        new TableAlreadyExistsException("Table existing already exists");
+    doThrow(exception).when(dispatcher).alterTable(any(), 
any(TableChange.class));
+    TableUpdatesRequest request =
+        new TableUpdatesRequest(
+            Collections.singletonList(new 
TableUpdateRequest.RenameTableRequest("existing")));
+    try (Response response =
+        target(tablePath(metalake, catalog, schema) + "table1")
+            .request(MediaType.APPLICATION_JSON_TYPE)
+            .accept("application/vnd.gravitino.v1+json")
+            .put(Entity.entity(request, MediaType.APPLICATION_JSON_TYPE))) {
+      Assertions.assertEquals(Response.Status.CONFLICT.getStatusCode(), 
response.getStatus());
+      ErrorResponse error = response.readEntity(ErrorResponse.class);
+      Assertions.assertEquals(ErrorConstants.ALREADY_EXISTS_CODE, 
error.getCode());
+      
Assertions.assertEquals(TableAlreadyExistsException.class.getSimpleName(), 
error.getType());
+      
Assertions.assertTrue(error.getMessage().contains(exception.getMessage()));
+    }
+  }
+
   @Test
   public void testRenameTable() {
     TableUpdateRequest.RenameTableRequest req = new 
TableUpdateRequest.RenameTableRequest("table2");

Reply via email to