This is an automated email from the ASF dual-hosted git repository.
jshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 4c6384ea27 [#6234] feat(core): Support rename operations for model
alteration (#6765)
4c6384ea27 is described below
commit 4c6384ea27822f68bbf3c9f65f547c40126ae588
Author: Lord of Abyss <[email protected]>
AuthorDate: Mon Mar 31 01:39:46 2025 +0800
[#6234] feat(core): Support rename operations for model alteration (#6765)
### What changes were proposed in this pull request?
Add ModelChange API interface, Implement the rename logic in model
catalog and JDBC backend logic.
- [X] PR1: Add ModelChange API interface, Implement the rename logic in
model catalog and JDBC backend logic.
- [ ] PR2: Add REST endpoint to support model change.
- [ ] PR3: Add Java client and Python client for model rename.
- [ ] PR4: update CLI module and docs.
- [x] PR5: update model-related event.
### Why are the changes needed?
Fix: #6234
### Does this PR introduce _any_ user-facing change?
Yes, support alter model for rename.
### How was this patch tested?
local test.
---
.../org/apache/gravitino/model/ModelCatalog.java | 15 ++++
.../org/apache/gravitino/model/ModelChange.java | 100 +++++++++++++++++++++
.../apache/gravitino/model/TestModelChange.java | 63 +++++++++++++
.../catalog/model/ModelCatalogOperations.java | 68 ++++++++++++++
.../catalog/model/TestModelCatalogOperations.java | 33 +++++++
.../gravitino/catalog/hadoop/fs/Constants.java | 1 +
.../gravitino/client/GenericModelCatalog.java | 8 ++
.../catalog/ModelNormalizeDispatcher.java | 8 ++
.../catalog/ModelOperationDispatcher.java | 27 ++++++
.../gravitino/listener/ModelEventDispatcher.java | 23 +++++
.../listener/api/event/AlterModelEvent.java | 80 +++++++++++++++++
.../listener/api/event/AlterModelFailureEvent.java | 65 ++++++++++++++
.../listener/api/event/AlterModelPreEvent.java | 53 +++++++++++
.../listener/api/event/OperationType.java | 1 +
.../gravitino/storage/relational/JDBCBackend.java | 2 +
.../storage/relational/mapper/ModelMetaMapper.java | 4 +
.../mapper/ModelMetaSQLProviderFactory.java | 5 ++
.../provider/base/ModelMetaBaseSQLProvider.java | 25 ++++++
.../relational/service/ModelMetaService.java | 38 ++++++++
.../storage/relational/utils/POConverters.java | 19 ++++
.../catalog/TestModelOperationDispatcher.java | 24 +++++
.../gravitino/connector/TestCatalogOperations.java | 43 +++++++++
.../listener/api/event/TestModelEvent.java | 71 +++++++++++++++
.../relational/service/TestModelMetaService.java | 43 +++++++++
24 files changed, 819 insertions(+)
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 3fb39c18ae..8d24ae7efe 100644
--- a/api/src/main/java/org/apache/gravitino/model/ModelCatalog.java
+++ b/api/src/main/java/org/apache/gravitino/model/ModelCatalog.java
@@ -236,4 +236,19 @@ public interface ModelCatalog {
* @return True if the model version is deleted, false if the model version
does not exist.
*/
boolean deleteModelVersion(NameIdentifier ident, String alias);
+
+ /**
+ * Applies the {@link ModelChange changes} to a model in the catalog.
+ *
+ * <p>Implementations may reject the changes. If any change is rejected, no
changes should be
+ * applied to the model.
+ *
+ * @param ident the {@link NameIdentifier} instance of the model to alter
+ * @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 IllegalArgumentException If the change is rejected by the
implementation
+ */
+ Model alterModel(NameIdentifier ident, ModelChange... changes)
+ throws NoSuchModelException, IllegalArgumentException;
}
diff --git a/api/src/main/java/org/apache/gravitino/model/ModelChange.java
b/api/src/main/java/org/apache/gravitino/model/ModelChange.java
new file mode 100644
index 0000000000..5d40c78856
--- /dev/null
+++ b/api/src/main/java/org/apache/gravitino/model/ModelChange.java
@@ -0,0 +1,100 @@
+/*
+ * 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.model;
+
+import org.apache.gravitino.annotation.Evolving;
+
+/**
+ * A model change is a change to a model. It can be used to rename a model,
update the comment of a
+ * model, set a property and value pair for a model, or remove a property from
a model.
+ */
+@Evolving
+public interface ModelChange {
+ /**
+ * Create a ModelChange for renaming a model.
+ *
+ * @param newName The new model name.
+ * @return A ModelChange for the rename.
+ */
+ static ModelChange rename(String newName) {
+ return new ModelChange.RenameModel(newName);
+ }
+
+ /** A ModelChange to rename a model. */
+ final class RenameModel implements ModelChange {
+ private final String newName;
+
+ /**
+ * Constructs a new {@link RenameModel} instance with the specified new
name.
+ *
+ * @param newName The new name of the model.
+ */
+ public RenameModel(String newName) {
+ this.newName = newName;
+ }
+
+ /**
+ * Retrieves the new name for the model.
+ *
+ * @return The new name of the model.
+ */
+ public String newName() {
+ return newName;
+ }
+
+ /**
+ * Compares this {@code RenameModel} instance with another object for
equality. The comparison
+ * is based on the new name of the model.
+ *
+ * @param obj The object to compare with this instance.
+ * @return {@code true} if the given object represents the same model
renaming; {@code false}
+ * otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+ RenameModel other = (RenameModel) obj;
+ return newName.equals(other.newName);
+ }
+
+ /**
+ * Generates a hash code for this {@code RenameModel} instance. The hash
code is based on the
+ * new name of the model.
+ *
+ * @return A hash code value for this model renaming operation.
+ */
+ @Override
+ public int hashCode() {
+ return newName.hashCode();
+ }
+
+ /**
+ * Returns a string representation of the {@code RenameModel} instance.
This string format
+ * includes the class name followed by the property name to be renamed.
+ *
+ * @return A string summary of the property rename instance.
+ */
+ @Override
+ public String toString() {
+ return "RenameModel " + newName;
+ }
+ }
+}
diff --git a/api/src/test/java/org/apache/gravitino/model/TestModelChange.java
b/api/src/test/java/org/apache/gravitino/model/TestModelChange.java
new file mode 100644
index 0000000000..a906f8a75b
--- /dev/null
+++ b/api/src/test/java/org/apache/gravitino/model/TestModelChange.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.model;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestModelChange {
+
+ @Test
+ void testCreateModelChangeUseStaticMethod() {
+ String newName = "newName";
+ ModelChange modelChange = ModelChange.rename(newName);
+ Assertions.assertEquals(ModelChange.RenameModel.class,
modelChange.getClass());
+
+ ModelChange.RenameModel renameModel = (ModelChange.RenameModel)
modelChange;
+ Assertions.assertEquals(newName, renameModel.newName());
+ Assertions.assertEquals("RenameModel newName", renameModel.toString());
+ }
+
+ @Test
+ void testCreateModelChangeUseConstructor() {
+ String newName = "newName";
+ ModelChange.RenameModel renameModel = new ModelChange.RenameModel(newName);
+ Assertions.assertEquals(newName, renameModel.newName());
+ Assertions.assertEquals("RenameModel newName", renameModel.toString());
+ }
+
+ @Test
+ void testModelChangeEquals() {
+ String newName1 = "demo_model";
+ String newName2 = "test_model";
+ String newName3 = "demo_model";
+
+ ModelChange.RenameModel renameModel1 = new
ModelChange.RenameModel(newName1);
+ ModelChange.RenameModel renameModel2 = new
ModelChange.RenameModel(newName2);
+ ModelChange.RenameModel renameModel3 = new
ModelChange.RenameModel(newName3);
+
+ Assertions.assertEquals(renameModel1, renameModel3);
+ Assertions.assertNotEquals(renameModel1, renameModel2);
+ Assertions.assertEquals(renameModel1, renameModel3);
+
+ Assertions.assertNotEquals(renameModel1.hashCode(),
renameModel2.hashCode());
+ Assertions.assertEquals(renameModel1.hashCode(), renameModel3.hashCode());
+ }
+}
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 7683180f78..4a47880812 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
@@ -20,6 +20,7 @@ package org.apache.gravitino.catalog.model;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
import java.io.IOException;
import java.time.Instant;
import java.util.List;
@@ -46,6 +47,7 @@ import org.apache.gravitino.meta.ModelEntity;
import org.apache.gravitino.meta.ModelVersionEntity;
import org.apache.gravitino.model.Model;
import org.apache.gravitino.model.ModelCatalog;
+import org.apache.gravitino.model.ModelChange;
import org.apache.gravitino.model.ModelVersion;
import org.apache.gravitino.utils.NameIdentifierUtil;
import org.apache.gravitino.utils.NamespaceUtil;
@@ -259,6 +261,72 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
return internalDeleteModelVersion(modelVersionIdent);
}
+ /** {@inheritDoc} */
+ @Override
+ public Model alterModel(NameIdentifier ident, ModelChange... changes)
+ throws NoSuchModelException, IllegalArgumentException {
+ try {
+ if (!store.exists(ident, Entity.EntityType.MODEL)) {
+ throw new NoSuchModelException("Model %s does not exist", ident);
+ }
+ } catch (IOException ioe) {
+ throw new RuntimeException("Failed to alter model " + ident, ioe);
+ }
+
+ try {
+ ModelEntity updatedModelEntity =
+ store.update(
+ ident,
+ ModelEntity.class,
+ Entity.EntityType.MODEL,
+ e -> updateModelEntity(ident, e, changes));
+
+ return toModelImpl(updatedModelEntity);
+
+ } catch (IOException ioe) {
+ throw new RuntimeException("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 new RuntimeException("Model already exist " + ident.name(), eaee);
+ }
+ }
+
+ private ModelEntity updateModelEntity(
+ NameIdentifier ident, ModelEntity modelEntity, ModelChange... changes) {
+
+ Map<String, String> entityProperties =
+ modelEntity.properties() == null
+ ? Maps.newHashMap()
+ : Maps.newHashMap(modelEntity.properties());
+ String entityName = ident.name();
+ String entityComment = modelEntity.comment();
+ Long entityId = modelEntity.id();
+ AuditInfo entityAuditInfo = modelEntity.auditInfo();
+ Namespace entityNamespace = modelEntity.namespace();
+ Integer entityLatestVersion = modelEntity.latestVersion();
+
+ for (ModelChange change : changes) {
+ if (change instanceof ModelChange.RenameModel) {
+ entityName = ((ModelChange.RenameModel) change).newName();
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported model change: " + change.getClass().getSimpleName());
+ }
+ }
+
+ return ModelEntity.builder()
+ .withName(entityName)
+ .withId(entityId)
+ .withComment(entityComment)
+ .withAuditInfo(entityAuditInfo)
+ .withNamespace(entityNamespace)
+ .withProperties(entityProperties)
+ .withLatestVersion(entityLatestVersion)
+ .build();
+ }
+
private ModelImpl toModelImpl(ModelEntity model) {
return ModelImpl.builder()
.withName(model.name())
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 e8321e4ed2..83fcc1505f 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
@@ -67,6 +67,7 @@ import org.apache.gravitino.meta.BaseMetalake;
import org.apache.gravitino.meta.CatalogEntity;
import org.apache.gravitino.meta.SchemaVersion;
import org.apache.gravitino.model.Model;
+import org.apache.gravitino.model.ModelChange;
import org.apache.gravitino.model.ModelVersion;
import org.apache.gravitino.storage.IdGenerator;
import org.apache.gravitino.storage.RandomIdGenerator;
@@ -656,6 +657,38 @@ public class TestModelCatalogOperations {
Assertions.assertThrows(NoSuchModelException.class, () ->
ops.listModelVersions(modelIdent));
}
+ @Test
+ public void testRename() {
+ String schemaName = randomSchemaName();
+ createSchema(schemaName);
+
+ String modelName = "model";
+ String newModelName = "new_model";
+ String comment = "comment";
+
+ NameIdentifier modelIdent =
+ NameIdentifierUtil.ofModel(METALAKE_NAME, CATALOG_NAME, schemaName,
modelName);
+ StringIdentifier stringId = StringIdentifier.fromId(idGenerator.nextId());
+ Map<String, String> properties =
StringIdentifier.newPropertiesWithId(stringId, null);
+
+ Model registeredModel = ops.registerModel(modelIdent, comment, properties);
+ Assertions.assertEquals(modelName, registeredModel.name());
+ Assertions.assertEquals(comment, registeredModel.comment());
+ Assertions.assertEquals(properties, registeredModel.properties());
+
+ Model loadedModel = ops.getModel(modelIdent);
+ Assertions.assertEquals(modelName, loadedModel.name());
+ Assertions.assertEquals(comment, loadedModel.comment());
+ Assertions.assertEquals(properties, loadedModel.properties());
+
+ ModelChange change = ModelChange.rename(newModelName);
+ Model alteredModel = ops.alterModel(modelIdent, change);
+
+ Assertions.assertEquals(newModelName, alteredModel.name());
+ Assertions.assertEquals(comment, alteredModel.comment());
+ Assertions.assertEquals(properties, alteredModel.properties());
+ }
+
private String randomSchemaName() {
return "schema_" + UUID.randomUUID().toString().replace("-", "");
}
diff --git
a/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/Constants.java
b/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/Constants.java
index 5bbbbd591c..96a4c060bf 100644
---
a/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/Constants.java
+++
b/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/Constants.java
@@ -19,6 +19,7 @@
package org.apache.gravitino.catalog.hadoop.fs;
+/** Constants used by the Hadoop file system catalog. */
public class Constants {
// Name of the built-in local file system provider
diff --git
a/clients/client-java/src/main/java/org/apache/gravitino/client/GenericModelCatalog.java
b/clients/client-java/src/main/java/org/apache/gravitino/client/GenericModelCatalog.java
index 8658a97259..f579a79900 100644
---
a/clients/client-java/src/main/java/org/apache/gravitino/client/GenericModelCatalog.java
+++
b/clients/client-java/src/main/java/org/apache/gravitino/client/GenericModelCatalog.java
@@ -44,6 +44,7 @@ import
org.apache.gravitino.exceptions.NoSuchModelVersionException;
import org.apache.gravitino.exceptions.NoSuchSchemaException;
import org.apache.gravitino.model.Model;
import org.apache.gravitino.model.ModelCatalog;
+import org.apache.gravitino.model.ModelChange;
import org.apache.gravitino.model.ModelVersion;
import org.apache.gravitino.rest.RESTUtils;
@@ -249,6 +250,13 @@ class GenericModelCatalog extends BaseSchemaCatalog
implements ModelCatalog {
return resp.dropped();
}
+ @Override
+ public Model alterModel(NameIdentifier ident, ModelChange... changes)
+ throws NoSuchModelException, IllegalArgumentException {
+ // TODO: implement alterModel
+ return null;
+ }
+
/** @return A new builder instance for {@link GenericModelCatalog}. */
public static Builder builder() {
return new Builder();
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 10683f6e9c..c73816f9a2 100644
---
a/core/src/main/java/org/apache/gravitino/catalog/ModelNormalizeDispatcher.java
+++
b/core/src/main/java/org/apache/gravitino/catalog/ModelNormalizeDispatcher.java
@@ -33,6 +33,7 @@ import org.apache.gravitino.exceptions.NoSuchModelException;
import org.apache.gravitino.exceptions.NoSuchModelVersionException;
import org.apache.gravitino.exceptions.NoSuchSchemaException;
import org.apache.gravitino.model.Model;
+import org.apache.gravitino.model.ModelChange;
import org.apache.gravitino.model.ModelVersion;
public class ModelNormalizeDispatcher implements ModelDispatcher {
@@ -128,6 +129,13 @@ public class ModelNormalizeDispatcher implements
ModelDispatcher {
return dispatcher.deleteModelVersion(normalizeCaseSensitive(ident), alias);
}
+ /** {@inheritDoc} */
+ @Override
+ public Model alterModel(NameIdentifier ident, ModelChange... changes)
+ throws NoSuchModelException, IllegalArgumentException {
+ return dispatcher.alterModel(normalizeCaseSensitive(ident), changes);
+ }
+
private Namespace normalizeCaseSensitive(Namespace namespace) {
Capability capabilities =
getCapability(NameIdentifier.of(namespace.levels()), catalogManager);
return applyCaseSensitive(namespace, Capability.Scope.MODEL, capabilities);
diff --git
a/core/src/main/java/org/apache/gravitino/catalog/ModelOperationDispatcher.java
b/core/src/main/java/org/apache/gravitino/catalog/ModelOperationDispatcher.java
index 1c5291d51a..88376ee5fd 100644
---
a/core/src/main/java/org/apache/gravitino/catalog/ModelOperationDispatcher.java
+++
b/core/src/main/java/org/apache/gravitino/catalog/ModelOperationDispatcher.java
@@ -36,6 +36,7 @@ import org.apache.gravitino.exceptions.NoSuchSchemaException;
import org.apache.gravitino.lock.LockType;
import org.apache.gravitino.lock.TreeLockUtils;
import org.apache.gravitino.model.Model;
+import org.apache.gravitino.model.ModelChange;
import org.apache.gravitino.model.ModelVersion;
import org.apache.gravitino.storage.IdGenerator;
@@ -209,6 +210,32 @@ public class ModelOperationDispatcher extends
OperationDispatcher implements Mod
RuntimeException.class));
}
+ /** {@inheritDoc} */
+ @Override
+ public Model alterModel(NameIdentifier ident, ModelChange... changes)
+ throws NoSuchModelException, IllegalArgumentException {
+ validateAlterProperties(ident,
HasPropertyMetadata::modelPropertiesMetadata, changes);
+ NameIdentifier catalogIdent = getCatalogIdentifier(ident);
+
+ Model alteredModel =
+ TreeLockUtils.doWithTreeLock(
+ ident,
+ LockType.WRITE,
+ () ->
+ doWithCatalog(
+ catalogIdent,
+ c -> c.doWithModelOps(f -> f.alterModel(ident, changes)),
+ NoSuchModelException.class,
+ IllegalArgumentException.class));
+
+ return EntityCombinedModel.of(alteredModel)
+ .withHiddenProperties(
+ getHiddenPropertyNames(
+ catalogIdent,
+ HasPropertyMetadata::modelPropertiesMetadata,
+ alteredModel.properties()));
+ }
+
private ModelVersion internalGetModelVersion(
NameIdentifier ident, Supplier<ModelVersion> supplier) {
NameIdentifier catalogIdent = getCatalogIdentifier(ident);
diff --git
a/core/src/main/java/org/apache/gravitino/listener/ModelEventDispatcher.java
b/core/src/main/java/org/apache/gravitino/listener/ModelEventDispatcher.java
index 243657df1c..e5d91928fb 100644
--- a/core/src/main/java/org/apache/gravitino/listener/ModelEventDispatcher.java
+++ b/core/src/main/java/org/apache/gravitino/listener/ModelEventDispatcher.java
@@ -28,6 +28,9 @@ import
org.apache.gravitino.exceptions.ModelVersionAliasesAlreadyExistException;
import org.apache.gravitino.exceptions.NoSuchModelException;
import org.apache.gravitino.exceptions.NoSuchModelVersionException;
import org.apache.gravitino.exceptions.NoSuchSchemaException;
+import org.apache.gravitino.listener.api.event.AlterModelEvent;
+import org.apache.gravitino.listener.api.event.AlterModelFailureEvent;
+import org.apache.gravitino.listener.api.event.AlterModelPreEvent;
import org.apache.gravitino.listener.api.event.DeleteModelEvent;
import org.apache.gravitino.listener.api.event.DeleteModelFailureEvent;
import org.apache.gravitino.listener.api.event.DeleteModelPreEvent;
@@ -58,6 +61,7 @@ import
org.apache.gravitino.listener.api.event.RegisterModelPreEvent;
import org.apache.gravitino.listener.api.info.ModelInfo;
import org.apache.gravitino.listener.api.info.ModelVersionInfo;
import org.apache.gravitino.model.Model;
+import org.apache.gravitino.model.ModelChange;
import org.apache.gravitino.model.ModelVersion;
import org.apache.gravitino.utils.PrincipalUtils;
@@ -275,6 +279,25 @@ public class ModelEventDispatcher implements
ModelDispatcher {
}
}
+ /** {@inheritDoc} */
+ @Override
+ public Model alterModel(NameIdentifier ident, ModelChange... changes)
+ throws NoSuchModelException, IllegalArgumentException {
+ String user = PrincipalUtils.getCurrentUserName();
+
+ eventBus.dispatchEvent(new AlterModelPreEvent(user, ident, changes));
+ try {
+ Model modelObject = dispatcher.alterModel(ident, changes);
+ ModelInfo modelInfo = new ModelInfo(modelObject);
+ eventBus.dispatchEvent(new AlterModelEvent(user, ident, modelInfo,
changes));
+
+ return modelObject;
+ } catch (Exception e) {
+ eventBus.dispatchEvent(new AlterModelFailureEvent(user, ident, e,
changes));
+ throw e;
+ }
+ }
+
/** {@inheritDoc} */
@Override
public int[] listModelVersions(NameIdentifier ident) throws
NoSuchModelException {
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/AlterModelEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/AlterModelEvent.java
new file mode 100644
index 0000000000..5b3c80ae8e
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/AlterModelEvent.java
@@ -0,0 +1,80 @@
+/*
+ * 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.listener.api.event;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.listener.api.info.ModelInfo;
+import org.apache.gravitino.model.ModelChange;
+
+/** Represents an event fired when a model is successfully altered. */
+public class AlterModelEvent extends ModelEvent {
+ private final ModelInfo updatedModelInfo;
+ private final ModelChange[] modelChanges;
+
+ /**
+ * Constructs a new {@link AlterModelEvent} instance with specified user,
identifier,
+ * updatedModelInfo, and modelChanges.
+ *
+ * @param user The username of the individual responsible for initiating the
model alteration.
+ * @param identifier The unique identifier of the altered model, serving as
a clear reference
+ * point for the model in question.
+ * @param updatedModelInfo The post-alteration state of the model.
+ * @param modelChanges An array of {@link ModelChange} objects representing
the specific changes
+ * applied to the model during the alteration process.
+ */
+ public AlterModelEvent(
+ String user,
+ NameIdentifier identifier,
+ ModelInfo updatedModelInfo,
+ ModelChange[] modelChanges) {
+ super(user, identifier);
+ this.updatedModelInfo = updatedModelInfo;
+ this.modelChanges = modelChanges;
+ }
+
+ /**
+ * Retrieves the updated state of the model after the successful alteration.
+ *
+ * @return A {@link ModelInfo} instance encapsulating the details of the
altered model.
+ */
+ public ModelInfo updatedModelInfo() {
+ return updatedModelInfo;
+ }
+
+ /**
+ * Retrieves the specific changes that were made to the model during the
alteration process.
+ *
+ * @return An array of {@link ModelChange} objects detailing each
modification applied to the
+ * model.
+ */
+ public ModelChange[] modelChanges() {
+ return modelChanges;
+ }
+
+ /**
+ * Returns the type of operation.
+ *
+ * @return the operation type.
+ */
+ @Override
+ public OperationType operationType() {
+ return OperationType.ALTER_MODEL;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/AlterModelFailureEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/AlterModelFailureEvent.java
new file mode 100644
index 0000000000..2aeab89587
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/AlterModelFailureEvent.java
@@ -0,0 +1,65 @@
+/*
+ * 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.listener.api.event;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.model.ModelChange;
+
+/**
+ * Represents an event that is triggered when an attempt to alter a model
fails due to an exception.
+ */
+public class AlterModelFailureEvent extends FailureEvent {
+ private final ModelChange[] modelChanges;
+
+ /**
+ * Constructs a new {@link AlterModelFailureEvent} instance with the
specified user, identifier,
+ * exception, and model changes.
+ *
+ * @param user The user who initiated the model alteration operation.
+ * @param identifier The identifier of the model that was attempted to be
altered.
+ * @param exception The exception that was thrown during the model
alteration operation.
+ * @param modelChanges The changes that were attempted on the model.
+ */
+ public AlterModelFailureEvent(
+ String user, NameIdentifier identifier, Exception exception,
ModelChange[] modelChanges) {
+ super(user, identifier, exception);
+ this.modelChanges = modelChanges;
+ }
+
+ /**
+ * Retrieves the changes that were attempted on the model.
+ *
+ * @return An array of {@link ModelChange} objects detailing each
modification applied to the
+ * model.
+ */
+ public ModelChange[] modelChanges() {
+ return modelChanges;
+ }
+
+ /**
+ * Returns the type of operation.
+ *
+ * @return the operation type.
+ */
+ @Override
+ public OperationType operationType() {
+ return OperationType.ALTER_MODEL;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/AlterModelPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/AlterModelPreEvent.java
new file mode 100644
index 0000000000..1ad66e6369
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/AlterModelPreEvent.java
@@ -0,0 +1,53 @@
+/*
+ * 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.listener.api.event;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.model.ModelChange;
+
+/** Represents an event triggered before altering a model. */
+public class AlterModelPreEvent extends PreEvent {
+ private final ModelChange[] modelChanges;
+
+ public AlterModelPreEvent(String user, NameIdentifier identifier,
ModelChange[] modelChanges) {
+ super(user, identifier);
+ this.modelChanges = modelChanges;
+ }
+
+ /**
+ * Retrieves the specific changes that were made to the model during the
alteration process.
+ *
+ * @return An array of {@link ModelChange} objects detailing each
modification applied to the
+ * model.
+ */
+ public ModelChange[] modelChanges() {
+ return modelChanges;
+ }
+
+ /**
+ * Returns the type of operation.
+ *
+ * @return the operation type.
+ */
+ @Override
+ public OperationType operationType() {
+ return OperationType.ALTER_MODEL;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/OperationType.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/OperationType.java
index a7ff5da95b..0f7a0d2098 100644
---
a/core/src/main/java/org/apache/gravitino/listener/api/event/OperationType.java
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/OperationType.java
@@ -104,6 +104,7 @@ public enum OperationType {
DELETE_MODEL,
GET_MODEL,
LIST_MODEL,
+ ALTER_MODEL,
// Model Version
LINK_MODEL_VERSION,
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java
b/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java
index a089251297..3ce1c75f20 100644
---
a/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java
@@ -203,6 +203,8 @@ public class JDBCBackend implements RelationalBackend {
return (E) RoleMetaService.getInstance().updateRole(ident, updater);
case TAG:
return (E) TagMetaService.getInstance().updateTag(ident, updater);
+ case MODEL:
+ return (E) ModelMetaService.getInstance().updateModel(ident, updater);
default:
throw new UnsupportedEntityTypeException(
"Unsupported entity type: %s for update operation", entityType);
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/ModelMetaMapper.java
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/ModelMetaMapper.java
index b16c556c71..eba3a0a43c 100644
---
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/ModelMetaMapper.java
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/ModelMetaMapper.java
@@ -87,4 +87,8 @@ public interface ModelMetaMapper {
@UpdateProvider(type = ModelMetaSQLProviderFactory.class, method =
"updateModelLatestVersion")
Integer updateModelLatestVersion(@Param("modelId") Long modelId);
+
+ @UpdateProvider(type = ModelMetaSQLProviderFactory.class, method =
"updateModelMeta")
+ Integer updateModelMeta(
+ @Param("newModelMeta") ModelPO newModelPO, @Param("oldModelMeta")
ModelPO oldModelPO);
}
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/ModelMetaSQLProviderFactory.java
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/ModelMetaSQLProviderFactory.java
index 796009737b..e4f7fe8161 100644
---
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/ModelMetaSQLProviderFactory.java
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/ModelMetaSQLProviderFactory.java
@@ -106,4 +106,9 @@ public class ModelMetaSQLProviderFactory {
public static String updateModelLatestVersion(@Param("modelId") Long
modelId) {
return getProvider().updateModelLatestVersion(modelId);
}
+
+ public static String updateModelMeta(
+ @Param("newModelMeta") ModelPO newModelPO, @Param("oldModelMeta")
ModelPO oldModelPO) {
+ return getProvider().updateModelMeta(newModelPO, oldModelPO);
+ }
}
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/ModelMetaBaseSQLProvider.java
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/ModelMetaBaseSQLProvider.java
index aa0c42fe57..42a3a9d2f3 100644
---
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/ModelMetaBaseSQLProvider.java
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/ModelMetaBaseSQLProvider.java
@@ -159,4 +159,29 @@ public class ModelMetaBaseSQLProvider {
+ " SET model_latest_version = model_latest_version + 1"
+ " WHERE model_id = #{modelId} AND deleted_at = 0";
}
+
+ public String updateModelMeta(
+ @Param("newModelMeta") ModelPO newModelPO, @Param("oldModelMeta")
ModelPO oldModelPO) {
+ return "UPDATE "
+ + ModelMetaMapper.TABLE_NAME
+ + " SET model_name = #{newModelMeta.modelName},"
+ + " metalake_id = #{newModelMeta.metalakeId},"
+ + " catalog_id = #{newModelMeta.catalogId},"
+ + " schema_id = #{newModelMeta.schemaId},"
+ + " model_comment = #{newModelMeta.modelComment},"
+ + " model_properties = #{newModelMeta.modelProperties},"
+ + " model_latest_version = #{newModelMeta.modelLatestVersion},"
+ + " audit_info = #{newModelMeta.auditInfo},"
+ + " deleted_at = #{newModelMeta.deletedAt}"
+ + " WHERE model_id = #{oldModelMeta.modelId}"
+ + " AND model_name = #{oldModelMeta.modelName}"
+ + " AND metalake_id = #{oldModelMeta.metalakeId}"
+ + " AND catalog_id = #{oldModelMeta.catalogId}"
+ + " AND schema_id = #{oldModelMeta.schemaId}"
+ + " AND model_comment = #{oldModelMeta.modelComment}"
+ + " AND model_properties = #{oldModelMeta.modelProperties}"
+ + " AND model_latest_version = #{oldModelMeta.modelLatestVersion}"
+ + " AND audit_info = #{oldModelMeta.auditInfo}"
+ + " AND deleted_at = 0";
+ }
}
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/service/ModelMetaService.java
b/core/src/main/java/org/apache/gravitino/storage/relational/service/ModelMetaService.java
index 0197dfdd2d..7e32af2f4b 100644
---
a/core/src/main/java/org/apache/gravitino/storage/relational/service/ModelMetaService.java
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/service/ModelMetaService.java
@@ -19,12 +19,16 @@
package org.apache.gravitino.storage.relational.service;
+import com.google.common.base.Preconditions;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.gravitino.Entity;
+import org.apache.gravitino.HasIdentifier;
import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.Namespace;
import org.apache.gravitino.exceptions.NoSuchEntityException;
@@ -196,4 +200,38 @@ public class ModelMetaService {
}
return modelPO;
}
+
+ public <E extends Entity & HasIdentifier> ModelEntity updateModel(
+ NameIdentifier identifier, Function<E, E> updater) throws IOException {
+ NameIdentifierUtil.checkModel(identifier);
+
+ ModelPO oldModelPO = getModelPOByIdentifier(identifier);
+ ModelEntity oldModelEntity = POConverters.fromModelPO(oldModelPO,
identifier.namespace());
+ ModelEntity newEntity = (ModelEntity) updater.apply((E) oldModelEntity);
+ Preconditions.checkArgument(
+ Objects.equals(oldModelEntity.id(), newEntity.id()),
+ "The updated model entity id: %s should be same with the table entity
id before: %s",
+ newEntity.id(),
+ oldModelEntity.id());
+
+ Integer updateResult;
+ try {
+ updateResult =
+ SessionUtils.doWithCommitAndFetchResult(
+ ModelMetaMapper.class,
+ mapper ->
+ mapper.updateModelMeta(
+ POConverters.updateModelPO(oldModelPO, newEntity),
oldModelPO));
+ } catch (RuntimeException re) {
+ ExceptionUtils.checkSQLException(
+ re, Entity.EntityType.CATALOG,
newEntity.nameIdentifier().toString());
+ throw re;
+ }
+
+ if (updateResult > 0) {
+ return newEntity;
+ } else {
+ throw new IOException("Failed to update the entity: " + identifier);
+ }
+ }
}
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/utils/POConverters.java
b/core/src/main/java/org/apache/gravitino/storage/relational/utils/POConverters.java
index 12737cd075..731f1c23a2 100644
---
a/core/src/main/java/org/apache/gravitino/storage/relational/utils/POConverters.java
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/utils/POConverters.java
@@ -1356,6 +1356,25 @@ public class POConverters {
}
}
+ public static ModelPO updateModelPO(ModelPO oldModelPO, ModelEntity
newModel) {
+ try {
+ return ModelPO.builder()
+ .withModelId(newModel.id())
+ .withModelName(newModel.name())
+ .withMetalakeId(oldModelPO.getMetalakeId())
+ .withCatalogId(oldModelPO.getCatalogId())
+ .withSchemaId(oldModelPO.getSchemaId())
+ .withModelComment(newModel.comment())
+ .withModelLatestVersion(newModel.latestVersion())
+
.withModelProperties(JsonUtils.anyFieldMapper().writeValueAsString(newModel.properties()))
+
.withAuditInfo(JsonUtils.anyFieldMapper().writeValueAsString(newModel.auditInfo()))
+ .withDeletedAt(DEFAULT_DELETED_AT)
+ .build();
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException("Failed to serialize json object:", e);
+ }
+ }
+
public static ModelVersionPO initializeModelVersionPO(
ModelVersionEntity modelVersionEntity, ModelVersionPO.Builder builder) {
try {
diff --git
a/core/src/test/java/org/apache/gravitino/catalog/TestModelOperationDispatcher.java
b/core/src/test/java/org/apache/gravitino/catalog/TestModelOperationDispatcher.java
index 10bb85a1e1..b7aada3eef 100644
---
a/core/src/test/java/org/apache/gravitino/catalog/TestModelOperationDispatcher.java
+++
b/core/src/test/java/org/apache/gravitino/catalog/TestModelOperationDispatcher.java
@@ -39,6 +39,7 @@ import org.apache.gravitino.exceptions.NoSuchModelException;
import org.apache.gravitino.exceptions.NoSuchModelVersionException;
import org.apache.gravitino.lock.LockManager;
import org.apache.gravitino.model.Model;
+import org.apache.gravitino.model.ModelChange;
import org.apache.gravitino.model.ModelVersion;
import org.apache.gravitino.utils.NameIdentifierUtil;
import org.junit.jupiter.api.Assertions;
@@ -254,6 +255,29 @@ public class TestModelOperationDispatcher extends
TestOperationDispatcher {
() -> modelOperationDispatcher.getModelVersion(modelIdent, "alias2"));
}
+ @Test
+ public void testRenameModel() {
+ String schemaName = "test_rename_model_schema";
+ String newModelName = "new_model_name";
+ String modelComment = "model which tests rename";
+ NameIdentifier schemaIdent = NameIdentifier.of(metalake, catalog,
schemaName);
+ schemaOperationDispatcher.createSchema(
+ schemaIdent, "comment", ImmutableMap.of("k1", "v1", "k2", "v2"));
+
+ String modelName = "test_rename_model";
+ NameIdentifier modelIdent =
+ NameIdentifierUtil.ofModel(metalake, catalog, schemaName, modelName);
+ Map<String, String> props = ImmutableMap.of("k1", "v1", "k2", "v2");
+ Model model = modelOperationDispatcher.registerModel(modelIdent,
modelComment, props);
+
+ ModelChange[] changeComment = new ModelChange[]
{ModelChange.rename(newModelName)};
+ Model alteredModel = modelOperationDispatcher.alterModel(modelIdent,
changeComment);
+
+ Assertions.assertEquals(newModelName, alteredModel.name());
+ Assertions.assertEquals(modelComment, alteredModel.comment());
+ Assertions.assertEquals(model.properties(), alteredModel.properties());
+ }
+
private String randomSchemaName() {
return "schema_" + UUID.randomUUID().toString().replace("-", "");
}
diff --git
a/core/src/test/java/org/apache/gravitino/connector/TestCatalogOperations.java
b/core/src/test/java/org/apache/gravitino/connector/TestCatalogOperations.java
index f7775ef32e..c0a3f87588 100644
---
a/core/src/test/java/org/apache/gravitino/connector/TestCatalogOperations.java
+++
b/core/src/test/java/org/apache/gravitino/connector/TestCatalogOperations.java
@@ -74,6 +74,7 @@ import org.apache.gravitino.messaging.TopicChange;
import org.apache.gravitino.meta.AuditInfo;
import org.apache.gravitino.model.Model;
import org.apache.gravitino.model.ModelCatalog;
+import org.apache.gravitino.model.ModelChange;
import org.apache.gravitino.model.ModelVersion;
import org.apache.gravitino.rel.Column;
import org.apache.gravitino.rel.Table;
@@ -895,6 +896,48 @@ public class TestCatalogOperations
return true;
}
+ @Override
+ public Model alterModel(NameIdentifier ident, ModelChange... changes)
+ throws NoSuchModelException, IllegalArgumentException {
+ if (!models.containsKey(ident)) {
+ throw new NoSuchModelException("Model %s does not exist", ident);
+ }
+
+ AuditInfo updatedAuditInfo =
+ AuditInfo.builder()
+ .withCreator("test")
+ .withCreateTime(Instant.now())
+ .withLastModifier("test")
+ .withLastModifiedTime(Instant.now())
+ .build();
+
+ TestModel model = models.get(ident);
+ Map<String, String> newProps =
+ model.properties() != null ? Maps.newHashMap(model.properties()) :
Maps.newHashMap();
+
+ NameIdentifier newIdent = ident;
+ for (ModelChange change : changes) {
+ if (change instanceof ModelChange.RenameModel) {
+ String newName = ((ModelChange.RenameModel) change).newName();
+ newIdent = NameIdentifier.of(ident.namespace(), newName);
+ if (models.containsKey(newIdent)) {
+ throw new ModelAlreadyExistsException("Model %s already exists",
ident);
+ }
+ }
+ }
+ TestModel updatedModel =
+ TestModel.builder()
+ .withName(newIdent.name())
+ .withComment(model.comment())
+ .withProperties(new HashMap<>(newProps))
+ .withAuditInfo(updatedAuditInfo)
+ .withLatestVersion(model.latestVersion())
+ .build();
+
+ models.put(ident, updatedModel);
+ return updatedModel;
+ }
+
private boolean hasCallerContext() {
return CallerContext.CallerContextHolder.get() != null
&& CallerContext.CallerContextHolder.get().context() != null
diff --git
a/core/src/test/java/org/apache/gravitino/listener/api/event/TestModelEvent.java
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestModelEvent.java
index a23b82e31c..c86b83040f 100644
---
a/core/src/test/java/org/apache/gravitino/listener/api/event/TestModelEvent.java
+++
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestModelEvent.java
@@ -38,6 +38,7 @@ import org.apache.gravitino.listener.ModelEventDispatcher;
import org.apache.gravitino.listener.api.info.ModelInfo;
import org.apache.gravitino.listener.api.info.ModelVersionInfo;
import org.apache.gravitino.model.Model;
+import org.apache.gravitino.model.ModelChange;
import org.apache.gravitino.model.ModelVersion;
import org.apache.gravitino.utils.NameIdentifierUtil;
import org.junit.jupiter.api.Assertions;
@@ -52,6 +53,8 @@ public class TestModelEvent {
private DummyEventListener dummyEventListener;
private Model modelA;
private Model modelB;
+ private Model alterNameModel;
+ private ModelChange modelRenameChange;
private NameIdentifier existingIdentA;
private NameIdentifier existingIdentB;
private NameIdentifier notExistingIdent;
@@ -67,6 +70,8 @@ public class TestModelEvent {
this.modelA = getMockModel("modelA", "commentA");
this.modelB = getMockModel("modelB", "commentB");
+ this.alterNameModel = getMockModel("modelA_rename", "commentA");
+ this.modelRenameChange = getMockModelChange("modelA_rename");
this.firstModelVersion =
mockModelVersion("uriA", new String[] {"aliasProduction"},
"versionInfoA");
@@ -716,6 +721,63 @@ public class TestModelEvent {
Assertions.assertEquals(existingIdentA,
listModelVersionsFailureEvent.identifier());
}
+ @Test
+ void testAlterModelPreEvent() {
+ dispatcher.alterModel(existingIdentA, modelRenameChange);
+
+ // validate pre-event
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(AlterModelPreEvent.class, preEvent.getClass());
+ Assertions.assertEquals(OperationType.ALTER_MODEL,
preEvent.operationType());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+
+ AlterModelPreEvent alterModelPreEvent = (AlterModelPreEvent) preEvent;
+ Assertions.assertEquals(existingIdentA, alterModelPreEvent.identifier());
+ ModelChange[] changes = alterModelPreEvent.modelChanges();
+ Assertions.assertEquals(1, changes.length);
+ Assertions.assertEquals(modelRenameChange, changes[0]);
+ }
+
+ @Test
+ void testAlterModelEvent() {
+ dispatcher.alterModel(existingIdentA, modelRenameChange);
+
+ // validate post-event
+ Event postEvent = dummyEventListener.popPostEvent();
+ Assertions.assertEquals(AlterModelEvent.class, postEvent.getClass());
+ Assertions.assertEquals(OperationType.ALTER_MODEL,
postEvent.operationType());
+ Assertions.assertEquals(OperationStatus.SUCCESS,
postEvent.operationStatus());
+
+ AlterModelEvent alterModelEvent = (AlterModelEvent) postEvent;
+ ModelChange[] changes = alterModelEvent.modelChanges();
+ Assertions.assertEquals(1, changes.length);
+ Assertions.assertEquals(modelRenameChange, changes[0]);
+ ModelInfo modelInfo = alterModelEvent.updatedModelInfo();
+
+ checkModelInfo(modelInfo, alterNameModel);
+ }
+
+ @Test
+ void testAlterModelFailureEvent() {
+ Assertions.assertThrowsExactly(
+ GravitinoRuntimeException.class,
+ () -> failureDispatcher.alterModel(existingIdentA, modelRenameChange));
+
+ // validate failure event
+ Event event = dummyEventListener.popPostEvent();
+
+ Assertions.assertEquals(AlterModelFailureEvent.class, event.getClass());
+ Assertions.assertEquals(
+ GravitinoRuntimeException.class, ((AlterModelFailureEvent)
event).exception().getClass());
+ Assertions.assertEquals(OperationType.ALTER_MODEL, event.operationType());
+ Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+
+ AlterModelFailureEvent alterModelFailureEvent = (AlterModelFailureEvent)
event;
+ ModelChange[] changes = alterModelFailureEvent.modelChanges();
+ Assertions.assertEquals(1, changes.length);
+ Assertions.assertEquals(modelRenameChange, changes[0]);
+ }
+
private ModelDispatcher mockExceptionModelDispatcher() {
return mock(
ModelDispatcher.class,
@@ -756,6 +818,8 @@ public class TestModelEvent {
when(dispatcher.deleteModelVersion(existingIdentA, 3)).thenReturn(false);
when(dispatcher.listModelVersions(existingIdentA)).thenReturn(new int[]
{1, 2});
+ when(dispatcher.alterModel(existingIdentA, new ModelChange[]
{modelRenameChange}))
+ .thenReturn(alterNameModel);
return dispatcher;
}
@@ -869,4 +933,11 @@ public class TestModelEvent {
Assertions.assertEquals(expected.length, actual.length);
Assertions.assertArrayEquals(expected, actual);
}
+
+ private ModelChange getMockModelChange(String newName) {
+ ModelChange.RenameModel mockObject = mock(ModelChange.RenameModel.class);
+ when(mockObject.newName()).thenReturn(newName);
+
+ return mockObject;
+ }
}
diff --git
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestModelMetaService.java
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestModelMetaService.java
index 067edd5a26..5f4ac8c3cb 100644
---
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestModelMetaService.java
+++
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestModelMetaService.java
@@ -23,6 +23,7 @@ import java.io.IOException;
import java.time.Instant;
import java.util.List;
import java.util.Map;
+import java.util.function.Function;
import org.apache.gravitino.EntityAlreadyExistsException;
import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.Namespace;
@@ -199,4 +200,46 @@ public class TestModelMetaService extends TestJDBCBackend {
ModelMetaService.getInstance()
.deleteModel(NameIdentifier.of(METALAKE_NAME, CATALOG_NAME,
"inexistent", "model1")));
}
+
+ @Test
+ void testAlterModel() throws IOException {
+ createParentEntities(METALAKE_NAME, CATALOG_NAME, SCHEMA_NAME, auditInfo);
+ Map<String, String> properties = ImmutableMap.of("k1", "v1");
+ String newName = "new_model_name";
+
+ ModelEntity modelEntity =
+ createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ MODEL_NS,
+ "model1",
+ "model1 comment",
+ 0,
+ properties,
+ auditInfo);
+
+ Assertions.assertDoesNotThrow(
+ () -> ModelMetaService.getInstance().insertModel(modelEntity, false));
+
+ ModelEntity updatedModel =
+ ModelEntity.builder()
+ .withId(modelEntity.id())
+ .withName(newName)
+ .withNamespace(modelEntity.namespace())
+ .withLatestVersion(modelEntity.latestVersion())
+ .withAuditInfo(modelEntity.auditInfo())
+ .withComment(modelEntity.comment())
+ .build();
+
+ Function<ModelEntity, ModelEntity> renameUpdater = oldModel ->
updatedModel;
+ ModelEntity alteredModel =
+
ModelMetaService.getInstance().updateModel(modelEntity.nameIdentifier(),
renameUpdater);
+
+ Assertions.assertEquals(alteredModel, updatedModel);
+ // Test update an in-existent model
+ Assertions.assertThrows(
+ NoSuchEntityException.class,
+ () ->
+ ModelMetaService.getInstance()
+ .updateModel(NameIdentifier.of(MODEL_NS, "model3"),
renameUpdater));
+ }
}