This is an automated email from the ASF dual-hosted git repository.
fanng 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 77136b692 [#5601] feat(core): Add model entity and model dispatcher in
Gravitino (#5662)
77136b692 is described below
commit 77136b692fc73f2500552305f9fe775e3e008ef3
Author: Jerry Shao <[email protected]>
AuthorDate: Tue Nov 26 21:39:07 2024 +0800
[#5601] feat(core): Add model entity and model dispatcher in Gravitino
(#5662)
### What changes were proposed in this pull request?
1. Define and add the model namespace, name identifier, entity definition
in Gravitino.
2. Add the basic dispatcher framework in Gravitino.
### Why are the changes needed?
This is the second PR to support model management in Gravitino.
Fix: #5601
### Does this PR introduce _any_ user-facing change?
No.
### How was this patch tested?
Add UTs to verify part of the code.
---
.../src/main/java/org/apache/gravitino/Entity.java | 5 +
.../java/org/apache/gravitino/GravitinoEnv.java | 21 +++
.../gravitino/catalog/CapabilityHelpers.java | 3 +-
.../apache/gravitino/catalog/ModelDispatcher.java | 23 +++
.../catalog/ModelNormalizeDispatcher.java | 167 ++++++++++++++++++
.../catalog/ModelOperationDispatcher.java | 110 ++++++++++++
.../gravitino/connector/capability/Capability.java | 3 +-
.../org/apache/gravitino/meta/ModelEntity.java | 189 +++++++++++++++++++++
.../apache/gravitino/meta/ModelVersionEntity.java | 174 +++++++++++++++++++
.../gravitino/storage/relational/JDBCBackend.java | 5 +
.../apache/gravitino/utils/MetadataObjectUtil.java | 7 +
.../apache/gravitino/utils/NameIdentifierUtil.java | 58 +++++++
.../org/apache/gravitino/utils/NamespaceUtil.java | 52 ++++++
.../org/apache/gravitino/meta/TestModelEntity.java | 116 +++++++++++++
.../gravitino/meta/TestModelVersionEntity.java | 107 ++++++++++++
.../gravitino/utils/TestMetadataObjectUtil.java | 10 ++
.../gravitino/utils/TestNameIdentifierUtil.java | 28 +++
.../apache/gravitino/utils/TestNamespaceUtil.java | 17 ++
18 files changed, 1093 insertions(+), 2 deletions(-)
diff --git a/core/src/main/java/org/apache/gravitino/Entity.java
b/core/src/main/java/org/apache/gravitino/Entity.java
index 96ccc40ae..102be5036 100644
--- a/core/src/main/java/org/apache/gravitino/Entity.java
+++ b/core/src/main/java/org/apache/gravitino/Entity.java
@@ -69,6 +69,8 @@ public interface Entity extends Serializable {
GROUP("gr", 8),
ROLE("ro", 9),
TAG("ta", 10),
+ MODEL("mo", 11),
+ MODEL_VERSION("mv", 12),
AUDIT("au", 65534);
@@ -109,12 +111,15 @@ public interface Entity extends Serializable {
case TABLE:
case FILESET:
case TOPIC:
+ case MODEL:
case USER:
case GROUP:
case ROLE:
return ImmutableList.of(METALAKE, CATALOG, SCHEMA);
case COLUMN:
return ImmutableList.of(METALAKE, CATALOG, SCHEMA, TABLE);
+ case MODEL_VERSION:
+ return ImmutableList.of(METALAKE, CATALOG, SCHEMA, MODEL);
default:
throw new IllegalArgumentException("Unknown entity type: " +
entityType);
}
diff --git a/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
b/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
index 859b74674..1cad967a9 100644
--- a/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
+++ b/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
@@ -31,6 +31,9 @@ import
org.apache.gravitino.catalog.CatalogNormalizeDispatcher;
import org.apache.gravitino.catalog.FilesetDispatcher;
import org.apache.gravitino.catalog.FilesetNormalizeDispatcher;
import org.apache.gravitino.catalog.FilesetOperationDispatcher;
+import org.apache.gravitino.catalog.ModelDispatcher;
+import org.apache.gravitino.catalog.ModelNormalizeDispatcher;
+import org.apache.gravitino.catalog.ModelOperationDispatcher;
import org.apache.gravitino.catalog.PartitionDispatcher;
import org.apache.gravitino.catalog.PartitionNormalizeDispatcher;
import org.apache.gravitino.catalog.PartitionOperationDispatcher;
@@ -98,6 +101,8 @@ public class GravitinoEnv {
private TopicDispatcher topicDispatcher;
+ private ModelDispatcher modelDispatcher;
+
private MetalakeDispatcher metalakeDispatcher;
private AccessControlDispatcher accessControlDispatcher;
@@ -207,6 +212,15 @@ public class GravitinoEnv {
return tableDispatcher;
}
+ /**
+ * Get the ModelDispatcher associated with the Gravitino environment.
+ *
+ * @return The ModelDispatcher instance.
+ */
+ public ModelDispatcher modelDispatcher() {
+ return modelDispatcher;
+ }
+
/**
* Get the PartitionDispatcher associated with the Gravitino environment.
*
@@ -440,6 +454,13 @@ public class GravitinoEnv {
new TopicNormalizeDispatcher(topicHookDispatcher, catalogManager);
this.topicDispatcher = new TopicEventDispatcher(eventBus,
topicNormalizeDispatcher);
+ // TODO(jerryshao). Add Hook and event dispatcher support for Model.
+ ModelOperationDispatcher modelOperationDispatcher =
+ new ModelOperationDispatcher(catalogManager, entityStore, idGenerator);
+ ModelNormalizeDispatcher modelNormalizeDispatcher =
+ new ModelNormalizeDispatcher(modelOperationDispatcher, catalogManager);
+ this.modelDispatcher = modelNormalizeDispatcher;
+
// Create and initialize access control related modules
boolean enableAuthorization = config.get(Configs.ENABLE_AUTHORIZATION);
if (enableAuthorization) {
diff --git
a/core/src/main/java/org/apache/gravitino/catalog/CapabilityHelpers.java
b/core/src/main/java/org/apache/gravitino/catalog/CapabilityHelpers.java
index 266b4a401..172d4b89d 100644
--- a/core/src/main/java/org/apache/gravitino/catalog/CapabilityHelpers.java
+++ b/core/src/main/java/org/apache/gravitino/catalog/CapabilityHelpers.java
@@ -130,7 +130,8 @@ public class CapabilityHelpers {
String catalog = namespace.level(1);
if (identScope == Capability.Scope.TABLE
|| identScope == Capability.Scope.FILESET
- || identScope == Capability.Scope.TOPIC) {
+ || identScope == Capability.Scope.TOPIC
+ || identScope == Capability.Scope.MODEL) {
String schema = namespace.level(namespace.length() - 1);
schema = applyCaseSensitiveOnName(Capability.Scope.SCHEMA, schema,
capabilities);
return Namespace.of(metalake, catalog, schema);
diff --git
a/core/src/main/java/org/apache/gravitino/catalog/ModelDispatcher.java
b/core/src/main/java/org/apache/gravitino/catalog/ModelDispatcher.java
new file mode 100644
index 000000000..067fd6ba2
--- /dev/null
+++ b/core/src/main/java/org/apache/gravitino/catalog/ModelDispatcher.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.catalog;
+
+import org.apache.gravitino.model.ModelCatalog;
+
+public interface ModelDispatcher extends ModelCatalog {}
diff --git
a/core/src/main/java/org/apache/gravitino/catalog/ModelNormalizeDispatcher.java
b/core/src/main/java/org/apache/gravitino/catalog/ModelNormalizeDispatcher.java
new file mode 100644
index 000000000..ea4933c3c
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/catalog/ModelNormalizeDispatcher.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.catalog;
+
+import static org.apache.gravitino.catalog.CapabilityHelpers.applyCapabilities;
+import static
org.apache.gravitino.catalog.CapabilityHelpers.applyCaseSensitive;
+import static org.apache.gravitino.catalog.CapabilityHelpers.getCapability;
+
+import java.util.Map;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.connector.capability.Capability;
+import org.apache.gravitino.exceptions.ModelAlreadyExistsException;
+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.model.Model;
+import org.apache.gravitino.model.ModelVersion;
+
+public class ModelNormalizeDispatcher implements ModelDispatcher {
+ private final CatalogManager catalogManager;
+ private final ModelDispatcher dispatcher;
+
+ public ModelNormalizeDispatcher(ModelDispatcher dispatcher, CatalogManager
catalogManager) {
+ this.dispatcher = dispatcher;
+ this.catalogManager = catalogManager;
+ }
+
+ @Override
+ public NameIdentifier[] listModels(Namespace namespace) throws
NoSuchSchemaException {
+ // The constraints of the name spec may be more strict than underlying
catalog,
+ // and for compatibility reasons, we only apply case-sensitive
capabilities here.
+ Namespace caseSensitiveNs = normalizeCaseSensitive(namespace);
+ NameIdentifier[] identifiers = dispatcher.listModels(caseSensitiveNs);
+ return normalizeCaseSensitive(identifiers);
+ }
+
+ @Override
+ public Model getModel(NameIdentifier ident) throws NoSuchModelException {
+ // The constraints of the name spec may be more strict than underlying
catalog,
+ // and for compatibility reasons, we only apply case-sensitive
capabilities here.
+ return dispatcher.getModel(normalizeCaseSensitive(ident));
+ }
+
+ @Override
+ public boolean modelExists(NameIdentifier ident) {
+ // The constraints of the name spec may be more strict than underlying
catalog,
+ // and for compatibility reasons, we only apply case-sensitive
capabilities here.
+ return dispatcher.modelExists(normalizeCaseSensitive(ident));
+ }
+
+ @Override
+ public Model registerModel(NameIdentifier ident, String comment, Map<String,
String> properties)
+ throws ModelAlreadyExistsException {
+ return dispatcher.registerModel(normalizeNameIdentifier(ident), comment,
properties);
+ }
+
+ @Override
+ public Model registerModel(
+ NameIdentifier ident,
+ String uri,
+ String[] aliases,
+ String comment,
+ Map<String, String> properties)
+ throws ModelAlreadyExistsException,
ModelVersionAliasesAlreadyExistException {
+ return dispatcher.registerModel(
+ normalizeNameIdentifier(ident), uri, aliases, comment, properties);
+ }
+
+ @Override
+ public boolean deleteModel(NameIdentifier ident) {
+ // The constraints of the name spec may be more strict than underlying
catalog,
+ // and for compatibility reasons, we only apply case-sensitive
capabilities here.
+ return dispatcher.deleteModel(normalizeCaseSensitive(ident));
+ }
+
+ @Override
+ public int[] listModelVersions(NameIdentifier ident) throws
NoSuchModelException {
+ return dispatcher.listModelVersions(normalizeCaseSensitive(ident));
+ }
+
+ @Override
+ public ModelVersion getModelVersion(NameIdentifier ident, int version)
+ throws NoSuchModelVersionException {
+ return dispatcher.getModelVersion(normalizeCaseSensitive(ident), version);
+ }
+
+ @Override
+ public ModelVersion getModelVersion(NameIdentifier ident, String alias)
+ throws NoSuchModelVersionException {
+ return dispatcher.getModelVersion(normalizeCaseSensitive(ident), alias);
+ }
+
+ @Override
+ public boolean modelVersionExists(NameIdentifier ident, int version) {
+ return dispatcher.modelVersionExists(normalizeCaseSensitive(ident),
version);
+ }
+
+ @Override
+ public boolean modelVersionExists(NameIdentifier ident, String alias) {
+ return dispatcher.modelVersionExists(normalizeCaseSensitive(ident), alias);
+ }
+
+ @Override
+ public ModelVersion linkModelVersion(
+ NameIdentifier ident,
+ String uri,
+ String[] aliases,
+ String comment,
+ Map<String, String> properties)
+ throws NoSuchModelException, ModelVersionAliasesAlreadyExistException {
+ return dispatcher.linkModelVersion(
+ normalizeCaseSensitive(ident), uri, aliases, comment, properties);
+ }
+
+ @Override
+ public boolean deleteModelVersion(NameIdentifier ident, int version) {
+ return dispatcher.deleteModelVersion(normalizeCaseSensitive(ident),
version);
+ }
+
+ @Override
+ public boolean deleteModelVersion(NameIdentifier ident, String alias) {
+ return dispatcher.deleteModelVersion(normalizeCaseSensitive(ident), alias);
+ }
+
+ private Namespace normalizeCaseSensitive(Namespace namespace) {
+ Capability capabilities =
getCapability(NameIdentifier.of(namespace.levels()), catalogManager);
+ return applyCaseSensitive(namespace, Capability.Scope.MODEL, capabilities);
+ }
+
+ private NameIdentifier normalizeCaseSensitive(NameIdentifier ident) {
+ Capability capabilities = getCapability(ident, catalogManager);
+ return applyCaseSensitive(ident, Capability.Scope.MODEL, capabilities);
+ }
+
+ private NameIdentifier[] normalizeCaseSensitive(NameIdentifier[] idents) {
+ if (ArrayUtils.isEmpty(idents)) {
+ return idents;
+ }
+
+ Capability capabilities = getCapability(idents[0], catalogManager);
+ return applyCaseSensitive(idents, Capability.Scope.MODEL, capabilities);
+ }
+
+ private NameIdentifier normalizeNameIdentifier(NameIdentifier ident) {
+ Capability capability = getCapability(ident, catalogManager);
+ return applyCapabilities(ident, Capability.Scope.MODEL, capability);
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/catalog/ModelOperationDispatcher.java
b/core/src/main/java/org/apache/gravitino/catalog/ModelOperationDispatcher.java
new file mode 100644
index 000000000..e8739673d
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/catalog/ModelOperationDispatcher.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.catalog;
+
+import java.util.Map;
+import org.apache.gravitino.EntityStore;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.exceptions.ModelAlreadyExistsException;
+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.model.Model;
+import org.apache.gravitino.model.ModelVersion;
+import org.apache.gravitino.storage.IdGenerator;
+
+public class ModelOperationDispatcher extends OperationDispatcher implements
ModelDispatcher {
+
+ public ModelOperationDispatcher(
+ CatalogManager catalogManager, EntityStore store, IdGenerator
idGenerator) {
+ super(catalogManager, store, idGenerator);
+ }
+
+ @Override
+ public NameIdentifier[] listModels(Namespace namespace) throws
NoSuchSchemaException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public Model getModel(NameIdentifier ident) throws NoSuchModelException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public Model registerModel(NameIdentifier ident, String comment, Map<String,
String> properties)
+ throws ModelAlreadyExistsException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public Model registerModel(
+ NameIdentifier ident,
+ String uri,
+ String[] aliases,
+ String comment,
+ Map<String, String> properties)
+ throws ModelAlreadyExistsException,
ModelVersionAliasesAlreadyExistException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public boolean deleteModel(NameIdentifier ident) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public int[] listModelVersions(NameIdentifier ident) throws
NoSuchModelException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public ModelVersion getModelVersion(NameIdentifier ident, int version)
+ throws NoSuchModelVersionException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public ModelVersion getModelVersion(NameIdentifier ident, String alias)
+ throws NoSuchModelVersionException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public ModelVersion linkModelVersion(
+ NameIdentifier ident,
+ String uri,
+ String[] aliases,
+ String comment,
+ Map<String, String> properties)
+ throws NoSuchModelException, ModelVersionAliasesAlreadyExistException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public boolean deleteModelVersion(NameIdentifier ident, int version) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public boolean deleteModelVersion(NameIdentifier ident, String alias) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/connector/capability/Capability.java
b/core/src/main/java/org/apache/gravitino/connector/capability/Capability.java
index 54d3ac543..37d517f2d 100644
---
a/core/src/main/java/org/apache/gravitino/connector/capability/Capability.java
+++
b/core/src/main/java/org/apache/gravitino/connector/capability/Capability.java
@@ -39,7 +39,8 @@ public interface Capability {
COLUMN,
FILESET,
TOPIC,
- PARTITION
+ PARTITION,
+ MODEL
}
/**
diff --git a/core/src/main/java/org/apache/gravitino/meta/ModelEntity.java
b/core/src/main/java/org/apache/gravitino/meta/ModelEntity.java
new file mode 100644
index 000000000..130c5b36a
--- /dev/null
+++ b/core/src/main/java/org/apache/gravitino/meta/ModelEntity.java
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.meta;
+
+import com.google.common.collect.Maps;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import lombok.ToString;
+import org.apache.gravitino.Auditable;
+import org.apache.gravitino.Entity;
+import org.apache.gravitino.Field;
+import org.apache.gravitino.HasIdentifier;
+import org.apache.gravitino.Namespace;
+
+@ToString
+public class ModelEntity implements Entity, Auditable, HasIdentifier {
+
+ public static final Field ID =
+ Field.required("id", Long.class, "The unique id of the model entity.");
+ public static final Field NAME =
+ Field.required("name", String.class, "The name of the model entity.");
+ public static final Field COMMENT =
+ Field.optional("comment", String.class, "The comment or description of
the model entity.");
+ public static final Field LATEST_VERSION =
+ Field.required("latest_version", Integer.class, "The latest version of
the model entity.");
+ public static final Field PROPERTIES =
+ Field.optional("properties", Map.class, "The properties of the model
entity.");
+ public static final Field AUDIT_INFO =
+ Field.required("audit_info", AuditInfo.class, "The audit details of the
model entity.");
+
+ private Long id;
+
+ private String name;
+
+ private Namespace namespace;
+
+ private String comment;
+
+ private Integer latestVersion;
+
+ private AuditInfo auditInfo;
+
+ private Map<String, String> properties;
+
+ private ModelEntity() {}
+
+ @Override
+ public Map<Field, Object> fields() {
+ Map<Field, Object> fields = Maps.newHashMap();
+ fields.put(ID, id);
+ fields.put(NAME, name);
+ fields.put(COMMENT, comment);
+ fields.put(LATEST_VERSION, latestVersion);
+ fields.put(PROPERTIES, properties);
+ fields.put(AUDIT_INFO, auditInfo);
+
+ return Collections.unmodifiableMap(fields);
+ }
+
+ @Override
+ public String name() {
+ return name;
+ }
+
+ @Override
+ public Long id() {
+ return id;
+ }
+
+ @Override
+ public Namespace namespace() {
+ return namespace;
+ }
+
+ public String comment() {
+ return comment;
+ }
+
+ public Integer latestVersion() {
+ return latestVersion;
+ }
+
+ public Map<String, String> properties() {
+ return properties;
+ }
+
+ @Override
+ public AuditInfo auditInfo() {
+ return auditInfo;
+ }
+
+ @Override
+ public EntityType type() {
+ return EntityType.MODEL;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof ModelEntity)) {
+ return false;
+ }
+
+ ModelEntity that = (ModelEntity) o;
+ return Objects.equals(id, that.id)
+ && Objects.equals(name, that.name)
+ && Objects.equals(comment, that.comment)
+ && Objects.equals(latestVersion, that.latestVersion)
+ && Objects.equals(properties, that.properties)
+ && Objects.equals(auditInfo, that.auditInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, name, comment, latestVersion, properties,
auditInfo);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private final ModelEntity model;
+
+ private Builder() {
+ model = new ModelEntity();
+ }
+
+ public Builder withId(Long id) {
+ model.id = id;
+ return this;
+ }
+
+ public Builder withName(String name) {
+ model.name = name;
+ return this;
+ }
+
+ public Builder withNamespace(Namespace namespace) {
+ model.namespace = namespace;
+ return this;
+ }
+
+ public Builder withComment(String comment) {
+ model.comment = comment;
+ return this;
+ }
+
+ public Builder withLatestVersion(Integer latestVersion) {
+ model.latestVersion = latestVersion;
+ return this;
+ }
+
+ public Builder withProperties(Map<String, String> properties) {
+ model.properties = properties;
+ return this;
+ }
+
+ public Builder withAuditInfo(AuditInfo auditInfo) {
+ model.auditInfo = auditInfo;
+ return this;
+ }
+
+ public ModelEntity build() {
+ model.validate();
+ return model;
+ }
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/meta/ModelVersionEntity.java
b/core/src/main/java/org/apache/gravitino/meta/ModelVersionEntity.java
new file mode 100644
index 000000000..6b9f44a52
--- /dev/null
+++ b/core/src/main/java/org/apache/gravitino/meta/ModelVersionEntity.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.meta;
+
+import com.google.common.collect.Maps;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import lombok.ToString;
+import org.apache.gravitino.Auditable;
+import org.apache.gravitino.Entity;
+import org.apache.gravitino.Field;
+
+@ToString
+public class ModelVersionEntity implements Entity, Auditable {
+
+ public static final Field VERSION =
+ Field.required("version", Integer.class, "The version of the model
entity.");
+ public static final Field COMMENT =
+ Field.optional("comment", String.class, "The comment or description of
the model entity.");
+ public static final Field ALIASES =
+ Field.optional("aliases", List.class, "The aliases of the model
entity.");
+ public static final Field URL =
+ Field.required("uri", String.class, "The URI of the model entity.");
+ public static final Field PROPERTIES =
+ Field.optional("properties", Map.class, "The properties of the model
entity.");
+ public static final Field AUDIT_INFO =
+ Field.required("audit_info", AuditInfo.class, "The audit details of the
model entity.");
+
+ private Integer version;
+
+ private String comment;
+
+ private List<String> aliases;
+
+ private String uri;
+
+ private AuditInfo auditInfo;
+
+ private Map<String, String> properties;
+
+ private ModelVersionEntity() {}
+
+ @Override
+ public Map<Field, Object> fields() {
+ Map<Field, Object> fields = Maps.newHashMap();
+ fields.put(VERSION, version);
+ fields.put(COMMENT, comment);
+ fields.put(ALIASES, aliases);
+ fields.put(URL, uri);
+ fields.put(PROPERTIES, properties);
+ fields.put(AUDIT_INFO, auditInfo);
+
+ return Collections.unmodifiableMap(fields);
+ }
+
+ public Integer version() {
+ return version;
+ }
+
+ public String comment() {
+ return comment;
+ }
+
+ public List<String> aliases() {
+ return aliases;
+ }
+
+ public String uri() {
+ return uri;
+ }
+
+ public Map<String, String> properties() {
+ return properties;
+ }
+
+ @Override
+ public AuditInfo auditInfo() {
+ return auditInfo;
+ }
+
+ @Override
+ public EntityType type() {
+ return EntityType.MODEL_VERSION;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof ModelVersionEntity)) {
+ return false;
+ }
+
+ ModelVersionEntity that = (ModelVersionEntity) o;
+ return Objects.equals(version, that.version)
+ && Objects.equals(comment, that.comment)
+ && Objects.equals(aliases, that.aliases)
+ && Objects.equals(uri, that.uri)
+ && Objects.equals(properties, that.properties)
+ && Objects.equals(auditInfo, that.auditInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(version, comment, aliases, uri, properties, auditInfo);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private final ModelVersionEntity model;
+
+ private Builder() {
+ model = new ModelVersionEntity();
+ }
+
+ public Builder withVersion(int version) {
+ model.version = version;
+ return this;
+ }
+
+ public Builder withComment(String comment) {
+ model.comment = comment;
+ return this;
+ }
+
+ public Builder withAliases(List<String> aliases) {
+ model.aliases = aliases;
+ return this;
+ }
+
+ public Builder withUri(String uri) {
+ model.uri = uri;
+ return this;
+ }
+
+ public Builder withProperties(Map<String, String> properties) {
+ model.properties = properties;
+ return this;
+ }
+
+ public Builder withAuditInfo(AuditInfo auditInfo) {
+ model.auditInfo = auditInfo;
+ return this;
+ }
+
+ public ModelVersionEntity build() {
+ model.validate();
+ return model;
+ }
+ }
+}
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 1534a7397..f45778e97 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
@@ -296,6 +296,9 @@ public class JDBCBackend implements RelationalBackend {
return TableColumnMetaService.getInstance()
.deleteColumnsByLegacyTimeline(legacyTimeline,
GARBAGE_COLLECTOR_SINGLE_DELETION_LIMIT);
case AUDIT:
+ case MODEL:
+ case MODEL_VERSION:
+ // TODO (jerryshao): Implement hard delete logic for these entity
types.
return 0;
// TODO: Implement hard delete logic for these entity types.
@@ -320,6 +323,8 @@ public class JDBCBackend implements RelationalBackend {
case AUDIT:
case ROLE:
case TAG:
+ case MODEL:
+ case MODEL_VERSION:
// These entity types have not implemented multi-versions, so we can
skip.
return 0;
diff --git
a/core/src/main/java/org/apache/gravitino/utils/MetadataObjectUtil.java
b/core/src/main/java/org/apache/gravitino/utils/MetadataObjectUtil.java
index da9f4129a..44b9d30a0 100644
--- a/core/src/main/java/org/apache/gravitino/utils/MetadataObjectUtil.java
+++ b/core/src/main/java/org/apache/gravitino/utils/MetadataObjectUtil.java
@@ -49,6 +49,7 @@ public class MetadataObjectUtil {
.put(MetadataObject.Type.FILESET, Entity.EntityType.FILESET)
.put(MetadataObject.Type.COLUMN, Entity.EntityType.COLUMN)
.put(MetadataObject.Type.ROLE, Entity.EntityType.ROLE)
+ .put(MetadataObject.Type.MODEL, Entity.EntityType.MODEL)
.build();
private MetadataObjectUtil() {}
@@ -94,6 +95,7 @@ public class MetadataObjectUtil {
case TOPIC:
case FILESET:
case COLUMN:
+ case MODEL:
String fullName = DOT.join(metalakeName, metadataObject.fullName());
return NameIdentifier.parse(fullName);
default:
@@ -158,6 +160,11 @@ public class MetadataObjectUtil {
check(env.topicDispatcher().topicExists(identifier),
exceptionToThrowSupplier);
break;
+ case MODEL:
+ NameIdentifierUtil.checkModel(identifier);
+ check(env.modelDispatcher().modelExists(identifier),
exceptionToThrowSupplier);
+ break;
+
case ROLE:
AuthorizationUtils.checkRole(identifier);
try {
diff --git
a/core/src/main/java/org/apache/gravitino/utils/NameIdentifierUtil.java
b/core/src/main/java/org/apache/gravitino/utils/NameIdentifierUtil.java
index 550fef967..a1cb3ead6 100644
--- a/core/src/main/java/org/apache/gravitino/utils/NameIdentifierUtil.java
+++ b/core/src/main/java/org/apache/gravitino/utils/NameIdentifierUtil.java
@@ -139,6 +139,37 @@ public class NameIdentifierUtil {
return NameIdentifier.of(metalake, catalog, schema, topic);
}
+ /**
+ * Create the model {@link NameIdentifier} with the given metalake, catalog,
schema and model
+ * name.
+ *
+ * @param metalake The metalake name
+ * @param catalog The catalog name
+ * @param schema The schema name
+ * @param model The model name
+ * @return The created model {@link NameIdentifier}
+ */
+ public static NameIdentifier ofModel(
+ String metalake, String catalog, String schema, String model) {
+ return NameIdentifier.of(metalake, catalog, schema, model);
+ }
+
+ /**
+ * Create the model version {@link NameIdentifier} with the given metalake,
catalog, schema, model
+ * and version.
+ *
+ * @param metalake The metalake name
+ * @param catalog The catalog name
+ * @param schema The schema name
+ * @param model The model name
+ * @param version The model version
+ * @return The created model version {@link NameIdentifier}
+ */
+ public static NameIdentifier ofModelVersion(
+ String metalake, String catalog, String schema, String model, int
version) {
+ return NameIdentifier.of(metalake, catalog, schema, model,
String.valueOf(version));
+ }
+
/**
* Try to get the catalog {@link NameIdentifier} from the given {@link
NameIdentifier}.
*
@@ -245,6 +276,28 @@ public class NameIdentifierUtil {
NamespaceUtil.checkTopic(ident.namespace());
}
+ /**
+ * Check the given {@link NameIdentifier} is a model identifier. Throw an
{@link
+ * IllegalNameIdentifierException} if it's not.
+ *
+ * @param ident The model {@link NameIdentifier} to check.
+ */
+ public static void checkModel(NameIdentifier ident) {
+ NameIdentifier.check(ident != null, "Model identifier must not be null");
+ NamespaceUtil.checkModel(ident.namespace());
+ }
+
+ /**
+ * Check the given {@link NameIdentifier} is a model version identifier.
Throw an {@link
+ * IllegalNameIdentifierException} if it's not.
+ *
+ * @param ident The model version {@link NameIdentifier} to check.
+ */
+ public static void checkModelVersion(NameIdentifier ident) {
+ NameIdentifier.check(ident != null, "Model version identifier must not be
null");
+ NamespaceUtil.checkModelVersion(ident.namespace());
+ }
+
/**
* Check the given condition is true. Throw an {@link
IllegalNamespaceException} if it's not.
*
@@ -309,6 +362,11 @@ public class NameIdentifierUtil {
String topicParent = dot.join(ident.namespace().level(1),
ident.namespace().level(2));
return MetadataObjects.of(topicParent, ident.name(),
MetadataObject.Type.TOPIC);
+ case MODEL:
+ checkModel(ident);
+ String modelParent = dot.join(ident.namespace().level(1),
ident.namespace().level(2));
+ return MetadataObjects.of(modelParent, ident.name(),
MetadataObject.Type.MODEL);
+
case ROLE:
AuthorizationUtils.checkRole(ident);
return MetadataObjects.of(null, ident.name(),
MetadataObject.Type.ROLE);
diff --git a/core/src/main/java/org/apache/gravitino/utils/NamespaceUtil.java
b/core/src/main/java/org/apache/gravitino/utils/NamespaceUtil.java
index c24015bb3..03ad8dc2e 100644
--- a/core/src/main/java/org/apache/gravitino/utils/NamespaceUtil.java
+++ b/core/src/main/java/org/apache/gravitino/utils/NamespaceUtil.java
@@ -107,6 +107,32 @@ public class NamespaceUtil {
return Namespace.of(metalake, catalog, schema);
}
+ /**
+ * Create a namespace for model.
+ *
+ * @param metalake The metalake name
+ * @param catalog The catalog name
+ * @param schema The schema name
+ * @return A namespace for model
+ */
+ public static Namespace ofModel(String metalake, String catalog, String
schema) {
+ return Namespace.of(metalake, catalog, schema);
+ }
+
+ /**
+ * Create a namespace for model version.
+ *
+ * @param metalake The metalake name
+ * @param catalog The catalog name
+ * @param schema The schema name
+ * @param model The model name
+ * @return A namespace for model version
+ */
+ public static Namespace ofModelVersion(
+ String metalake, String catalog, String schema, String model) {
+ return Namespace.of(metalake, catalog, schema, model);
+ }
+
/**
* Check if the given metalake namespace is legal, throw an {@link
IllegalNamespaceException} if
* it's illegal.
@@ -198,6 +224,32 @@ public class NamespaceUtil {
namespace);
}
+ /**
+ * Check if the given model namespace is legal, throw an {@link
IllegalNamespaceException} if it's
+ * illegal.
+ *
+ * @param namespace The model namespace
+ */
+ public static void checkModel(Namespace namespace) {
+ check(
+ namespace != null && namespace.length() == 3,
+ "Model namespace must be non-null and have 3 levels, the input
namespace is %s",
+ namespace);
+ }
+
+ /**
+ * Check if the given model version namespace is legal, throw an {@link
IllegalNamespaceException}
+ * if it's illegal.
+ *
+ * @param namespace The model version namespace
+ */
+ public static void checkModelVersion(Namespace namespace) {
+ check(
+ namespace != null && namespace.length() == 4,
+ "Model version namespace must be non-null and have 4 levels, the input
namespace is %s",
+ namespace);
+ }
+
/**
* Check the given condition is true. Throw an {@link
IllegalNamespaceException} if it's not.
*
diff --git a/core/src/test/java/org/apache/gravitino/meta/TestModelEntity.java
b/core/src/test/java/org/apache/gravitino/meta/TestModelEntity.java
new file mode 100644
index 000000000..d3b48090b
--- /dev/null
+++ b/core/src/test/java/org/apache/gravitino/meta/TestModelEntity.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.meta;
+
+import com.google.common.collect.ImmutableMap;
+import java.time.Instant;
+import java.util.Map;
+import org.apache.gravitino.Namespace;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestModelEntity {
+
+ @Test
+ public void testModelEntityFields() {
+ AuditInfo auditInfo =
+
AuditInfo.builder().withCreator("test").withCreateTime(Instant.now()).build();
+ Map<String, String> properties = ImmutableMap.of("k1", "v1");
+
+ ModelEntity modelEntity =
+ ModelEntity.builder()
+ .withId(1L)
+ .withName("test")
+ .withComment("test comment")
+ .withLatestVersion(1)
+ .withNamespace(Namespace.of("m1", "c1", "s1"))
+ .withProperties(properties)
+ .withAuditInfo(auditInfo)
+ .build();
+
+ Assertions.assertEquals(1L, modelEntity.id());
+ Assertions.assertEquals("test", modelEntity.name());
+ Assertions.assertEquals("test comment", modelEntity.comment());
+ Assertions.assertEquals(1, modelEntity.latestVersion());
+ Assertions.assertEquals(Namespace.of("m1", "c1", "s1"),
modelEntity.namespace());
+ Assertions.assertEquals(properties, modelEntity.properties());
+ Assertions.assertEquals(auditInfo, modelEntity.auditInfo());
+
+ ModelEntity modelEntity2 =
+ ModelEntity.builder()
+ .withId(1L)
+ .withName("test")
+ .withLatestVersion(1)
+ .withNamespace(Namespace.of("m1", "c1", "s1"))
+ .withProperties(properties)
+ .withAuditInfo(auditInfo)
+ .build();
+ Assertions.assertNull(modelEntity2.comment());
+
+ ModelEntity modelEntity3 =
+ ModelEntity.builder()
+ .withId(1L)
+ .withName("test")
+ .withComment("test comment")
+ .withLatestVersion(1)
+ .withNamespace(Namespace.of("m1", "c1", "s1"))
+ .withAuditInfo(auditInfo)
+ .build();
+ Assertions.assertNull(modelEntity3.properties());
+ }
+
+ @Test
+ public void testWithoutRequiredFields() {
+ Assertions.assertThrows(IllegalArgumentException.class, () ->
ModelEntity.builder().build());
+
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () -> {
+ ModelEntity.builder()
+ .withId(1L)
+ .withNamespace(Namespace.of("m1", "c1", "s1"))
+ .withAuditInfo(
+
AuditInfo.builder().withCreator("test").withCreateTime(Instant.now()).build())
+ .build();
+ });
+
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () -> {
+ ModelEntity.builder()
+ .withId(1L)
+ .withName("test")
+ .withNamespace(Namespace.of("m1", "c1", "s1"))
+ .withAuditInfo(
+
AuditInfo.builder().withCreator("test").withCreateTime(Instant.now()).build())
+ .build();
+ });
+
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () -> {
+ ModelEntity.builder()
+ .withId(1L)
+ .withName("test")
+ .withNamespace(Namespace.of("m1", "c1", "s1"))
+ .withLatestVersion(1)
+ .build();
+ });
+ }
+}
diff --git
a/core/src/test/java/org/apache/gravitino/meta/TestModelVersionEntity.java
b/core/src/test/java/org/apache/gravitino/meta/TestModelVersionEntity.java
new file mode 100644
index 000000000..5b62ea946
--- /dev/null
+++ b/core/src/test/java/org/apache/gravitino/meta/TestModelVersionEntity.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.meta;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import java.time.Instant;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestModelVersionEntity {
+
+ @Test
+ public void testModelVersionEntityFields() {
+ AuditInfo auditInfo =
+
AuditInfo.builder().withCreator("test").withCreateTime(Instant.now()).build();
+ Map<String, String> properties = ImmutableMap.of("k1", "v1");
+ List<String> aliases = Lists.newArrayList("alias1", "alias2");
+
+ ModelVersionEntity modelVersionEntity =
+ ModelVersionEntity.builder()
+ .withVersion(1)
+ .withComment("test comment")
+ .withAliases(aliases)
+ .withProperties(properties)
+ .withUri("test_uri")
+ .withAuditInfo(auditInfo)
+ .build();
+
+ Assertions.assertEquals(1, modelVersionEntity.version());
+ Assertions.assertEquals("test comment", modelVersionEntity.comment());
+ Assertions.assertEquals(aliases, modelVersionEntity.aliases());
+ Assertions.assertEquals(properties, modelVersionEntity.properties());
+ Assertions.assertEquals("test_uri", modelVersionEntity.uri());
+ Assertions.assertEquals(auditInfo, modelVersionEntity.auditInfo());
+
+ ModelVersionEntity modelVersionEntity2 =
+ ModelVersionEntity.builder()
+ .withVersion(1)
+ .withAliases(aliases)
+ .withProperties(properties)
+ .withUri("test_uri")
+ .withAuditInfo(auditInfo)
+ .build();
+ Assertions.assertNull(modelVersionEntity2.comment());
+
+ ModelVersionEntity modelVersionEntity3 =
+ ModelVersionEntity.builder()
+ .withVersion(1)
+ .withComment("test comment")
+ .withAliases(aliases)
+ .withUri("test_uri")
+ .withAuditInfo(auditInfo)
+ .build();
+ Assertions.assertNull(modelVersionEntity3.properties());
+
+ ModelVersionEntity modelVersionEntity4 =
+ ModelVersionEntity.builder()
+ .withVersion(1)
+ .withComment("test comment")
+ .withProperties(properties)
+ .withUri("test_uri")
+ .withAuditInfo(auditInfo)
+ .build();
+ Assertions.assertNull(modelVersionEntity4.aliases());
+ }
+
+ @Test
+ public void testWithoutRequiredFields() {
+ Assertions.assertThrows(
+ IllegalArgumentException.class, () ->
ModelVersionEntity.builder().build());
+
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () -> {
+ ModelVersionEntity.builder()
+ .withVersion(1)
+ .withAuditInfo(
+
AuditInfo.builder().withCreator("test").withCreateTime(Instant.now()).build())
+ .build();
+ });
+
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () -> {
+
ModelVersionEntity.builder().withVersion(1).withUri("test_uri").build();
+ });
+ }
+}
diff --git
a/core/src/test/java/org/apache/gravitino/utils/TestMetadataObjectUtil.java
b/core/src/test/java/org/apache/gravitino/utils/TestMetadataObjectUtil.java
index c5a281866..9911ddbfa 100644
--- a/core/src/test/java/org/apache/gravitino/utils/TestMetadataObjectUtil.java
+++ b/core/src/test/java/org/apache/gravitino/utils/TestMetadataObjectUtil.java
@@ -68,6 +68,11 @@ public class TestMetadataObjectUtil {
Entity.EntityType.COLUMN,
MetadataObjectUtil.toEntityType(
MetadataObjects.of("catalog.schema.table", "column",
MetadataObject.Type.COLUMN)));
+
+ Assertions.assertEquals(
+ Entity.EntityType.MODEL,
+ MetadataObjectUtil.toEntityType(
+ MetadataObjects.of("catalog.schema", "model",
MetadataObject.Type.MODEL)));
}
@Test
@@ -113,6 +118,11 @@ public class TestMetadataObjectUtil {
"metalake",
MetadataObjects.of("catalog.schema", "fileset",
MetadataObject.Type.FILESET)));
+ Assertions.assertEquals(
+ NameIdentifier.of("metalake", "catalog", "schema", "model"),
+ MetadataObjectUtil.toEntityIdent(
+ "metalake", MetadataObjects.of("catalog.schema", "model",
MetadataObject.Type.MODEL)));
+
Assertions.assertEquals(
NameIdentifier.of("metalake", "catalog", "schema", "table", "column"),
MetadataObjectUtil.toEntityIdent(
diff --git
a/core/src/test/java/org/apache/gravitino/utils/TestNameIdentifierUtil.java
b/core/src/test/java/org/apache/gravitino/utils/TestNameIdentifierUtil.java
index 2eca30351..e5db1eb10 100644
--- a/core/src/test/java/org/apache/gravitino/utils/TestNameIdentifierUtil.java
+++ b/core/src/test/java/org/apache/gravitino/utils/TestNameIdentifierUtil.java
@@ -61,6 +61,21 @@ public class TestNameIdentifierUtil {
Throwable excep3 =
assertThrows(IllegalNamespaceException.class, () ->
NameIdentifierUtil.checkTable(abc));
assertTrue(excep3.getMessage().contains("Table namespace must be non-null
and have 3 levels"));
+
+ // test model
+ assertThrows(IllegalNameIdentifierException.class, () ->
NameIdentifierUtil.checkModel(null));
+ Throwable excep4 =
+ assertThrows(IllegalNamespaceException.class, () ->
NameIdentifierUtil.checkModel(abc));
+ assertTrue(excep4.getMessage().contains("Model namespace must be non-null
and have 3 levels"));
+
+ // test model version
+ assertThrows(
+ IllegalNameIdentifierException.class, () ->
NameIdentifierUtil.checkModelVersion(null));
+ Throwable excep5 =
+ assertThrows(
+ IllegalNamespaceException.class, () ->
NameIdentifierUtil.checkModelVersion(abcd));
+ assertTrue(
+ excep5.getMessage().contains("Model version namespace must be non-null
and have 4 levels"));
}
@Test
@@ -111,6 +126,12 @@ public class TestNameIdentifierUtil {
assertEquals(
columnObject, NameIdentifierUtil.toMetadataObject(column,
Entity.EntityType.COLUMN));
+ // test model
+ NameIdentifier model = NameIdentifier.of("metalake1", "catalog1",
"schema1", "model1");
+ MetadataObject modelObject =
+ MetadataObjects.parse("catalog1.schema1.model1",
MetadataObject.Type.MODEL);
+ assertEquals(modelObject, NameIdentifierUtil.toMetadataObject(model,
Entity.EntityType.MODEL));
+
// test null
Throwable e1 =
assertThrows(
@@ -123,5 +144,12 @@ public class TestNameIdentifierUtil {
IllegalArgumentException.class,
() -> NameIdentifierUtil.toMetadataObject(fileset,
Entity.EntityType.TAG));
assertTrue(e2.getMessage().contains("Entity type TAG is not supported"));
+
+ // test model version
+ Throwable e3 =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> NameIdentifierUtil.toMetadataObject(model,
Entity.EntityType.MODEL_VERSION));
+ assertTrue(e3.getMessage().contains("Entity type MODEL_VERSION is not
supported"));
}
}
diff --git
a/core/src/test/java/org/apache/gravitino/utils/TestNamespaceUtil.java
b/core/src/test/java/org/apache/gravitino/utils/TestNamespaceUtil.java
index 4286d3f90..71ba8e4c7 100644
--- a/core/src/test/java/org/apache/gravitino/utils/TestNamespaceUtil.java
+++ b/core/src/test/java/org/apache/gravitino/utils/TestNamespaceUtil.java
@@ -64,5 +64,22 @@ public class TestNamespaceUtil {
IllegalNamespaceException.class, () ->
NamespaceUtil.checkTable(abcd));
Assertions.assertTrue(
excep3.getMessage().contains("Table namespace must be non-null and
have 3 levels"));
+
+ // Test model
+ Assertions.assertThrows(IllegalNamespaceException.class, () ->
NamespaceUtil.checkModel(null));
+ Throwable excep4 =
+ Assertions.assertThrows(
+ IllegalNamespaceException.class, () ->
NamespaceUtil.checkModel(abcd));
+ Assertions.assertTrue(
+ excep4.getMessage().contains("Model namespace must be non-null and
have 3 levels"));
+
+ // Test model version
+ Assertions.assertThrows(
+ IllegalNamespaceException.class, () ->
NamespaceUtil.checkModelVersion(null));
+ Throwable excep5 =
+ Assertions.assertThrows(
+ IllegalNamespaceException.class, () ->
NamespaceUtil.checkModelVersion(ab));
+ Assertions.assertTrue(
+ excep5.getMessage().contains("Model version namespace must be non-null
and have 4 levels"));
}
}