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 74ecaeb93 [#5602] feat(core): Add storage schema for model (part-1)
(#5689)
74ecaeb93 is described below
commit 74ecaeb933e931db67f471c580dfcaef5329be73
Author: Jerry Shao <[email protected]>
AuthorDate: Mon Dec 2 10:45:38 2024 +0800
[#5602] feat(core): Add storage schema for model (part-1) (#5689)
### What changes were proposed in this pull request?
This PR adds the 1st part of storage schema for model metadata.
### Why are the changes needed?
Fix: #5602
### Does this PR introduce _any_ user-facing change?
No.
### How was this patch tested?
Add various UTs to cover the code.
---
.../gravitino/storage/relational/JDBCBackend.java | 15 +-
.../storage/relational/mapper/ModelMetaMapper.java | 84 +++++++++
.../mapper/ModelMetaSQLProviderFactory.java | 100 ++++++++++
.../provider/base/ModelMetaBaseSQLProvider.java | 137 ++++++++++++++
.../postgresql/ModelMetaPostgreSQLProvider.java | 86 +++++++++
.../gravitino/storage/relational/po/ModelPO.java | 129 +++++++++++++
.../relational/po/ModelVersionAliasRelPO.java | 83 +++++++++
.../storage/relational/po/ModelVersionPO.java | 130 +++++++++++++
.../relational/service/CatalogMetaService.java | 7 +-
.../relational/service/MetadataObjectService.java | 18 ++
.../relational/service/MetalakeMetaService.java | 7 +-
.../relational/service/ModelMetaService.java | 189 +++++++++++++++++++
.../relational/service/SchemaMetaService.java | 20 +-
.../session/SqlSessionFactoryHelper.java | 2 +
.../storage/relational/utils/POConverters.java | 37 ++++
.../gravitino/storage/TestEntityStorage.java | 194 ++++++++++++++++++--
.../storage/relational/TestJDBCBackend.java | 89 +++++++++
.../relational/service/TestModelMetaService.java | 202 +++++++++++++++++++++
.../service/TestTableColumnMetaService.java | 36 +---
.../storage/relational/utils/TestPOConverters.java | 156 ++++++++++++++++
scripts/h2/schema-0.8.0-h2.sql | 47 +++++
scripts/h2/upgrade-0.7.0-to-0.8.0-h2.sql | 47 +++++
scripts/mysql/schema-0.8.0-mysql.sql | 47 +++++
scripts/mysql/upgrade-0.7.0-to-0.8.0-mysql.sql | 47 +++++
scripts/postgresql/schema-0.8.0-postgresql.sql | 84 +++++++++
.../upgrade-0.7.0-to-0.8.0-postgresql.sql | 84 +++++++++
26 files changed, 2026 insertions(+), 51 deletions(-)
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 f45778e97..961257808 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
@@ -42,6 +42,7 @@ import org.apache.gravitino.meta.BaseMetalake;
import org.apache.gravitino.meta.CatalogEntity;
import org.apache.gravitino.meta.FilesetEntity;
import org.apache.gravitino.meta.GroupEntity;
+import org.apache.gravitino.meta.ModelEntity;
import org.apache.gravitino.meta.RoleEntity;
import org.apache.gravitino.meta.SchemaEntity;
import org.apache.gravitino.meta.TableEntity;
@@ -54,6 +55,7 @@ import
org.apache.gravitino.storage.relational.service.CatalogMetaService;
import org.apache.gravitino.storage.relational.service.FilesetMetaService;
import org.apache.gravitino.storage.relational.service.GroupMetaService;
import org.apache.gravitino.storage.relational.service.MetalakeMetaService;
+import org.apache.gravitino.storage.relational.service.ModelMetaService;
import org.apache.gravitino.storage.relational.service.OwnerMetaService;
import org.apache.gravitino.storage.relational.service.RoleMetaService;
import org.apache.gravitino.storage.relational.service.SchemaMetaService;
@@ -111,6 +113,8 @@ public class JDBCBackend implements RelationalBackend {
return (List<E>)
RoleMetaService.getInstance().listRolesByNamespace(namespace);
case GROUP:
return (List<E>)
GroupMetaService.getInstance().listGroupsByNamespace(namespace, allFields);
+ case MODEL:
+ return (List<E>)
ModelMetaService.getInstance().listModelsByNamespace(namespace);
default:
throw new UnsupportedEntityTypeException(
"Unsupported entity type: %s for list operation", entityType);
@@ -150,6 +154,8 @@ public class JDBCBackend implements RelationalBackend {
GroupMetaService.getInstance().insertGroup((GroupEntity) e, overwritten);
} else if (e instanceof TagEntity) {
TagMetaService.getInstance().insertTag((TagEntity) e, overwritten);
+ } else if (e instanceof ModelEntity) {
+ ModelMetaService.getInstance().insertModel((ModelEntity) e, overwritten);
} else {
throw new UnsupportedEntityTypeException(
"Unsupported entity type: %s for insert operation", e.getClass());
@@ -212,6 +218,8 @@ public class JDBCBackend implements RelationalBackend {
return (E) RoleMetaService.getInstance().getRoleByIdentifier(ident);
case TAG:
return (E) TagMetaService.getInstance().getTagByIdentifier(ident);
+ case MODEL:
+ return (E) ModelMetaService.getInstance().getModelByIdentifier(ident);
default:
throw new UnsupportedEntityTypeException(
"Unsupported entity type: %s for get operation", entityType);
@@ -242,6 +250,8 @@ public class JDBCBackend implements RelationalBackend {
return RoleMetaService.getInstance().deleteRole(ident);
case TAG:
return TagMetaService.getInstance().deleteTag(ident);
+ case MODEL:
+ return ModelMetaService.getInstance().deleteModel(ident);
default:
throw new UnsupportedEntityTypeException(
"Unsupported entity type: %s for delete operation", entityType);
@@ -295,10 +305,13 @@ public class JDBCBackend implements RelationalBackend {
case COLUMN:
return TableColumnMetaService.getInstance()
.deleteColumnsByLegacyTimeline(legacyTimeline,
GARBAGE_COLLECTOR_SINGLE_DELETION_LIMIT);
- case AUDIT:
case MODEL:
+ return ModelMetaService.getInstance()
+ .deleteModelMetasByLegacyTimeline(
+ legacyTimeline, GARBAGE_COLLECTOR_SINGLE_DELETION_LIMIT);
case MODEL_VERSION:
// TODO (jerryshao): Implement hard delete logic for these entity
types.
+ case AUDIT:
return 0;
// TODO: Implement hard delete logic for these entity types.
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
new file mode 100644
index 000000000..5b3c4a93f
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/ModelMetaMapper.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.storage.relational.mapper;
+
+import java.util.List;
+import org.apache.gravitino.storage.relational.po.ModelPO;
+import org.apache.ibatis.annotations.DeleteProvider;
+import org.apache.ibatis.annotations.InsertProvider;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.SelectProvider;
+import org.apache.ibatis.annotations.UpdateProvider;
+
+public interface ModelMetaMapper {
+ String TABLE_NAME = "model_meta";
+
+ @InsertProvider(type = ModelMetaSQLProviderFactory.class, method =
"insertModelMeta")
+ void insertModelMeta(@Param("modelMeta") ModelPO modelPO);
+
+ @InsertProvider(
+ type = ModelMetaSQLProviderFactory.class,
+ method = "insertModelMetaOnDuplicateKeyUpdate")
+ void insertModelMetaOnDuplicateKeyUpdate(@Param("modelMeta") ModelPO
modelPO);
+
+ @SelectProvider(type = ModelMetaSQLProviderFactory.class, method =
"listModelPOsBySchemaId")
+ List<ModelPO> listModelPOsBySchemaId(@Param("schemaId") Long schemaId);
+
+ @SelectProvider(
+ type = ModelMetaSQLProviderFactory.class,
+ method = "selectModelMetaBySchemaIdAndModelName")
+ ModelPO selectModelMetaBySchemaIdAndModelName(
+ @Param("schemaId") Long schemaId, @Param("modelName") String modelName);
+
+ @SelectProvider(
+ type = ModelMetaSQLProviderFactory.class,
+ method = "selectModelIdBySchemaIdAndModelName")
+ Long selectModelIdBySchemaIdAndModelName(
+ @Param("schemaId") Long schemaId, @Param("modelName") String modelName);
+
+ @SelectProvider(type = ModelMetaSQLProviderFactory.class, method =
"selectModelMetaByModelId")
+ ModelPO selectModelMetaByModelId(@Param("modelId") Long modelId);
+
+ @UpdateProvider(
+ type = ModelMetaSQLProviderFactory.class,
+ method = "softDeleteModelMetaBySchemaIdAndModelName")
+ Integer softDeleteModelMetaBySchemaIdAndModelName(
+ @Param("schemaId") Long schemaId, @Param("modelName") String modelName);
+
+ @UpdateProvider(
+ type = ModelMetaSQLProviderFactory.class,
+ method = "softDeleteModelMetasByCatalogId")
+ Integer softDeleteModelMetasByCatalogId(@Param("catalogId") Long catalogId);
+
+ @UpdateProvider(
+ type = ModelMetaSQLProviderFactory.class,
+ method = "softDeleteModelMetasByMetalakeId")
+ Integer softDeleteModelMetasByMetalakeId(@Param("metalakeId") Long
metalakeId);
+
+ @UpdateProvider(
+ type = ModelMetaSQLProviderFactory.class,
+ method = "softDeleteModelMetasBySchemaId")
+ Integer softDeleteModelMetasBySchemaId(@Param("schemaId") Long schemaId);
+
+ @DeleteProvider(
+ type = ModelMetaSQLProviderFactory.class,
+ method = "deleteModelMetasByLegacyTimeline")
+ Integer deleteModelMetasByLegacyTimeline(
+ @Param("legacyTimeline") Long legacyTimeline, @Param("limit") int limit);
+}
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
new file mode 100644
index 000000000..74334ec6e
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/ModelMetaSQLProviderFactory.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.storage.relational.mapper;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import org.apache.gravitino.storage.relational.JDBCBackend.JDBCBackendType;
+import
org.apache.gravitino.storage.relational.mapper.provider.base.ModelMetaBaseSQLProvider;
+import
org.apache.gravitino.storage.relational.mapper.provider.postgresql.ModelMetaPostgreSQLProvider;
+import org.apache.gravitino.storage.relational.po.ModelPO;
+import org.apache.gravitino.storage.relational.session.SqlSessionFactoryHelper;
+import org.apache.ibatis.annotations.Param;
+
+public class ModelMetaSQLProviderFactory {
+
+ static class ModelMetaMySQLProvider extends ModelMetaBaseSQLProvider {}
+
+ static class ModelMetaH2Provider extends ModelMetaBaseSQLProvider {}
+
+ private static final Map<JDBCBackendType, ModelMetaBaseSQLProvider>
MODEL_META_SQL_PROVIDER_MAP =
+ ImmutableMap.of(
+ JDBCBackendType.MYSQL, new ModelMetaMySQLProvider(),
+ JDBCBackendType.H2, new ModelMetaH2Provider(),
+ JDBCBackendType.POSTGRESQL, new ModelMetaPostgreSQLProvider());
+
+ public static ModelMetaBaseSQLProvider getProvider() {
+ String databaseId =
+ SqlSessionFactoryHelper.getInstance()
+ .getSqlSessionFactory()
+ .getConfiguration()
+ .getDatabaseId();
+
+ JDBCBackendType jdbcBackendType = JDBCBackendType.fromString(databaseId);
+ return MODEL_META_SQL_PROVIDER_MAP.get(jdbcBackendType);
+ }
+
+ public static String insertModelMeta(@Param("modelMeta") ModelPO modelPO) {
+ return getProvider().insertModelMeta(modelPO);
+ }
+
+ public static String insertModelMetaOnDuplicateKeyUpdate(@Param("modelMeta")
ModelPO modelPO) {
+ return getProvider().insertModelMetaOnDuplicateKeyUpdate(modelPO);
+ }
+
+ public static String listModelPOsBySchemaId(@Param("schemaId") Long
schemaId) {
+ return getProvider().listModelPOsBySchemaId(schemaId);
+ }
+
+ public static String selectModelMetaBySchemaIdAndModelName(
+ @Param("schemaId") Long schemaId, @Param("modelName") String modelName) {
+ return getProvider().selectModelMetaBySchemaIdAndModelName(schemaId,
modelName);
+ }
+
+ public static String selectModelIdBySchemaIdAndModelName(
+ @Param("schemaId") Long schemaId, @Param("modelName") String modelName) {
+ return getProvider().selectModelIdBySchemaIdAndModelName(schemaId,
modelName);
+ }
+
+ public static String selectModelMetaByModelId(@Param("modelId") Long
modelId) {
+ return getProvider().selectModelMetaByModelId(modelId);
+ }
+
+ public static String softDeleteModelMetaBySchemaIdAndModelName(
+ @Param("schemaId") Long schemaId, @Param("modelName") String modelName) {
+ return getProvider().softDeleteModelMetaBySchemaIdAndModelName(schemaId,
modelName);
+ }
+
+ public static String softDeleteModelMetasByCatalogId(@Param("catalogId")
Long catalogId) {
+ return getProvider().softDeleteModelMetasByCatalogId(catalogId);
+ }
+
+ public static String softDeleteModelMetasByMetalakeId(@Param("metalakeId")
Long metalakeId) {
+ return getProvider().softDeleteModelMetasByMetalakeId(metalakeId);
+ }
+
+ public static String softDeleteModelMetasBySchemaId(@Param("schemaId") Long
schemaId) {
+ return getProvider().softDeleteModelMetasBySchemaId(schemaId);
+ }
+
+ public static String deleteModelMetasByLegacyTimeline(
+ @Param("legacyTimeline") Long legacyTimeline, @Param("limit") int limit)
{
+ return getProvider().deleteModelMetasByLegacyTimeline(legacyTimeline,
limit);
+ }
+}
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
new file mode 100644
index 000000000..cae5b2d9d
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/ModelMetaBaseSQLProvider.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.storage.relational.mapper.provider.base;
+
+import org.apache.gravitino.storage.relational.mapper.ModelMetaMapper;
+import org.apache.gravitino.storage.relational.po.ModelPO;
+import org.apache.ibatis.annotations.Param;
+
+public class ModelMetaBaseSQLProvider {
+
+ public String insertModelMeta(@Param("modelMeta") ModelPO modelPO) {
+ return "INSERT INTO "
+ + ModelMetaMapper.TABLE_NAME
+ + " (model_id, model_name, metalake_id, catalog_id, schema_id,"
+ + " model_comment, model_properties, model_latest_version, audit_info,
deleted_at)"
+ + " VALUES (#{modelMeta.modelId}, #{modelMeta.modelName},
#{modelMeta.metalakeId},"
+ + " #{modelMeta.catalogId}, #{modelMeta.schemaId},
#{modelMeta.modelComment},"
+ + " #{modelMeta.modelProperties}, #{modelMeta.modelLatestVersion},
#{modelMeta.auditInfo},"
+ + " #{modelMeta.deletedAt})";
+ }
+
+ public String insertModelMetaOnDuplicateKeyUpdate(@Param("modelMeta")
ModelPO modelPO) {
+ return "INSERT INTO "
+ + ModelMetaMapper.TABLE_NAME
+ + " (model_id, model_name, metalake_id, catalog_id, schema_id,"
+ + " model_comment, model_properties, model_latest_version, audit_info,
deleted_at)"
+ + " VALUES (#{modelMeta.modelId}, #{modelMeta.modelName},
#{modelMeta.metalakeId},"
+ + " #{modelMeta.catalogId}, #{modelMeta.schemaId},
#{modelMeta.modelComment},"
+ + " #{modelMeta.modelProperties}, #{modelMeta.modelLatestVersion},
#{modelMeta.auditInfo},"
+ + " #{modelMeta.deletedAt})"
+ + " ON DUPLICATE KEY UPDATE"
+ + " model_name = #{modelMeta.modelName},"
+ + " metalake_id = #{modelMeta.metalakeId},"
+ + " catalog_id = #{modelMeta.catalogId},"
+ + " schema_id = #{modelMeta.schemaId},"
+ + " model_comment = #{modelMeta.modelComment},"
+ + " model_properties = #{modelMeta.modelProperties},"
+ + " model_latest_version = #{modelMeta.modelLatestVersion},"
+ + " audit_info = #{modelMeta.auditInfo},"
+ + " deleted_at = #{modelMeta.deletedAt}";
+ }
+
+ public String listModelPOsBySchemaId(@Param("schemaId") Long schemaId) {
+ return "SELECT model_id AS modelId, model_name AS modelName, metalake_id
AS metalakeId,"
+ + " catalog_id AS catalogId, schema_id AS schemaId, model_comment AS
modelComment,"
+ + " model_properties AS modelProperties, model_latest_version AS"
+ + " modelLatestVersion, audit_info AS auditInfo, deleted_at AS
deletedAt"
+ + " FROM "
+ + ModelMetaMapper.TABLE_NAME
+ + " WHERE schema_id = #{schemaId} AND deleted_at = 0";
+ }
+
+ public String selectModelMetaBySchemaIdAndModelName(
+ @Param("schemaId") Long schemaId, @Param("modelName") String modelName) {
+ return "SELECT model_id AS modelId, model_name AS modelName, metalake_id
AS metalakeId,"
+ + " catalog_id AS catalogId, schema_id AS schemaId, model_comment AS
modelComment,"
+ + " model_properties AS modelProperties, model_latest_version AS"
+ + " modelLatestVersion, audit_info AS auditInfo, deleted_at AS
deletedAt"
+ + " FROM "
+ + ModelMetaMapper.TABLE_NAME
+ + " WHERE schema_id = #{schemaId} AND model_name = #{modelName} AND
deleted_at = 0";
+ }
+
+ public String selectModelIdBySchemaIdAndModelName(
+ @Param("schemaId") Long schemaId, @Param("modelName") String modelName) {
+ return "SELECT model_id"
+ + " FROM "
+ + ModelMetaMapper.TABLE_NAME
+ + " WHERE schema_id = #{schemaId} AND model_name = #{modelName} AND
deleted_at = 0";
+ }
+
+ public String selectModelMetaByModelId(@Param("modelId") Long modelId) {
+ return "SELECT model_id AS modelId, model_name AS modelName, metalake_id
AS metalakeId,"
+ + " catalog_id AS catalogId, schema_id AS schemaId, model_comment AS
modelComment,"
+ + " model_properties AS modelProperties, model_latest_version AS "
+ + " modelLatestVersion, audit_info AS auditInfo, deleted_at AS
deletedAt"
+ + " FROM "
+ + ModelMetaMapper.TABLE_NAME
+ + " WHERE model_id = #{modelId} AND deleted_at = 0";
+ }
+
+ public String softDeleteModelMetaBySchemaIdAndModelName(
+ @Param("schemaId") Long schemaId, @Param("modelName") String modelName) {
+ return "UPDATE "
+ + ModelMetaMapper.TABLE_NAME
+ + " SET deleted_at = (UNIX_TIMESTAMP() * 1000.0)"
+ + " + EXTRACT(MICROSECOND FROM CURRENT_TIMESTAMP(3)) / 1000"
+ + " WHERE schema_id = #{schemaId} AND model_name = #{modelName} AND
deleted_at = 0";
+ }
+
+ public String softDeleteModelMetasByCatalogId(@Param("catalogId") Long
catalogId) {
+ return "UPDATE "
+ + ModelMetaMapper.TABLE_NAME
+ + " SET deleted_at = (UNIX_TIMESTAMP() * 1000.0)"
+ + " + EXTRACT(MICROSECOND FROM CURRENT_TIMESTAMP(3)) / 1000"
+ + " WHERE catalog_id = #{catalogId} AND deleted_at = 0";
+ }
+
+ public String softDeleteModelMetasByMetalakeId(@Param("metalakeId") Long
metalakeId) {
+ return "UPDATE "
+ + ModelMetaMapper.TABLE_NAME
+ + " SET deleted_at = (UNIX_TIMESTAMP() * 1000.0)"
+ + " + EXTRACT(MICROSECOND FROM CURRENT_TIMESTAMP(3)) / 1000"
+ + " WHERE metalake_id = #{metalakeId} AND deleted_at = 0";
+ }
+
+ public String softDeleteModelMetasBySchemaId(@Param("schemaId") Long
schemaId) {
+ return "UPDATE "
+ + ModelMetaMapper.TABLE_NAME
+ + " SET deleted_at = (UNIX_TIMESTAMP() * 1000.0)"
+ + " + EXTRACT(MICROSECOND FROM CURRENT_TIMESTAMP(3)) / 1000"
+ + " WHERE schema_id = #{schemaId} AND deleted_at = 0";
+ }
+
+ public String deleteModelMetasByLegacyTimeline(
+ @Param("legacyTimeline") Long legacyTimeline, @Param("limit") int limit)
{
+ return "DELETE FROM "
+ + ModelMetaMapper.TABLE_NAME
+ + " WHERE deleted_at > 0 AND deleted_at < #{legacyTimeline} LIMIT
#{limit}";
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/ModelMetaPostgreSQLProvider.java
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/ModelMetaPostgreSQLProvider.java
new file mode 100644
index 000000000..8f62252aa
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/ModelMetaPostgreSQLProvider.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.storage.relational.mapper.provider.postgresql;
+
+import org.apache.gravitino.storage.relational.mapper.ModelMetaMapper;
+import
org.apache.gravitino.storage.relational.mapper.provider.base.ModelMetaBaseSQLProvider;
+import org.apache.gravitino.storage.relational.po.ModelPO;
+import org.apache.ibatis.annotations.Param;
+
+public class ModelMetaPostgreSQLProvider extends ModelMetaBaseSQLProvider {
+
+ @Override
+ public String insertModelMetaOnDuplicateKeyUpdate(@Param("modelMeta")
ModelPO modelPO) {
+ return "INSERT INTO "
+ + ModelMetaMapper.TABLE_NAME
+ + "(model_id, model_name, metalake_id, catalog_id, schema_id,"
+ + " model_comment, model_properties, model_latest_version, audit_info,
deleted_at)"
+ + " VALUES (#{modelMeta.modelId}, #{modelMeta.modelName},
#{modelMeta.metalakeId},"
+ + " #{modelMeta.catalogId}, #{modelMeta.schemaId},
#{modelMeta.modelComment},"
+ + " #{modelMeta.modelProperties}, #{modelMeta.modelLatestVersion},
#{modelMeta.auditInfo},"
+ + " #{modelMeta.deletedAt})"
+ + " ON CONFLICT (model_id) DO UPDATE SET"
+ + " model_name = #{modelMeta.modelName},"
+ + " metalake_id = #{modelMeta.metalakeId},"
+ + " catalog_id = #{modelMeta.catalogId},"
+ + " schema_id = #{modelMeta.schemaId},"
+ + " model_comment = #{modelMeta.modelComment},"
+ + " model_properties = #{modelMeta.modelProperties},"
+ + " model_latest_version = #{modelMeta.modelLatestVersion},"
+ + " audit_info = #{modelMeta.auditInfo},"
+ + " deleted_at = #{modelMeta.deletedAt}";
+ }
+
+ @Override
+ public String softDeleteModelMetaBySchemaIdAndModelName(
+ @Param("schemaId") Long schemaId, @Param("modelName") String modelName) {
+ return "UPDATE "
+ + ModelMetaMapper.TABLE_NAME
+ + " SET deleted_at = floor(extract(epoch from((current_timestamp -"
+ + " timestamp '1970-01-01 00:00:00')*1000)))"
+ + " WHERE schema_id = #{schemaId} AND model_name = #{modelName} AND
deleted_at = 0";
+ }
+
+ @Override
+ public String softDeleteModelMetasByCatalogId(@Param("catalogId") Long
catalogId) {
+ return "UPDATE "
+ + ModelMetaMapper.TABLE_NAME
+ + " SET deleted_at = floor(extract(epoch from((current_timestamp -"
+ + " timestamp '1970-01-01 00:00:00')*1000)))"
+ + " WHERE catalog_id = #{catalogId} AND deleted_at = 0";
+ }
+
+ @Override
+ public String softDeleteModelMetasByMetalakeId(@Param("metalakeId") Long
metalakeId) {
+ return "UPDATE "
+ + ModelMetaMapper.TABLE_NAME
+ + " SET deleted_at = floor(extract(epoch from((current_timestamp -"
+ + " timestamp '1970-01-01 00:00:00')*1000)))"
+ + " WHERE metalake_id = #{metalakeId} AND deleted_at = 0";
+ }
+
+ @Override
+ public String softDeleteModelMetasBySchemaId(@Param("schemaId") Long
schemaId) {
+ return "UPDATE "
+ + ModelMetaMapper.TABLE_NAME
+ + " SET deleted_at = floor(extract(epoch from((current_timestamp -"
+ + " timestamp '1970-01-01 00:00:00')*1000)))"
+ + " WHERE schema_id = #{schemaId} AND deleted_at = 0";
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/po/ModelPO.java
b/core/src/main/java/org/apache/gravitino/storage/relational/po/ModelPO.java
new file mode 100644
index 000000000..008b071d8
--- /dev/null
+++ b/core/src/main/java/org/apache/gravitino/storage/relational/po/ModelPO.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.storage.relational.po;
+
+import com.google.common.base.Preconditions;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+@EqualsAndHashCode
+@Getter
+public class ModelPO {
+
+ private Long modelId;
+
+ private String modelName;
+
+ private Long metalakeId;
+
+ private Long catalogId;
+
+ private Long schemaId;
+
+ private String modelComment;
+
+ private Integer modelLatestVersion;
+
+ private String modelProperties;
+
+ private String auditInfo;
+
+ private Long deletedAt;
+
+ private ModelPO() {}
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+
+ private final ModelPO modelPO;
+
+ private Builder() {
+ modelPO = new ModelPO();
+ }
+
+ public Builder withModelId(Long modelId) {
+ modelPO.modelId = modelId;
+ return this;
+ }
+
+ public Builder withModelName(String modelName) {
+ modelPO.modelName = modelName;
+ return this;
+ }
+
+ public Builder withMetalakeId(Long metalakeId) {
+ modelPO.metalakeId = metalakeId;
+ return this;
+ }
+
+ public Builder withCatalogId(Long catalogId) {
+ modelPO.catalogId = catalogId;
+ return this;
+ }
+
+ public Builder withSchemaId(Long schemaId) {
+ modelPO.schemaId = schemaId;
+ return this;
+ }
+
+ public Builder withModelComment(String modelComment) {
+ modelPO.modelComment = modelComment;
+ return this;
+ }
+
+ public Builder withModelLatestVersion(Integer modelLatestVersion) {
+ modelPO.modelLatestVersion = modelLatestVersion;
+ return this;
+ }
+
+ public Builder withModelProperties(String modelProperties) {
+ modelPO.modelProperties = modelProperties;
+ return this;
+ }
+
+ public Builder withAuditInfo(String auditInfo) {
+ modelPO.auditInfo = auditInfo;
+ return this;
+ }
+
+ public Builder withDeletedAt(Long deletedAt) {
+ modelPO.deletedAt = deletedAt;
+ return this;
+ }
+
+ public ModelPO build() {
+ Preconditions.checkArgument(modelPO.modelId != null, "Model id is
required");
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(modelPO.modelName), "Model name cannot be
empty");
+ Preconditions.checkArgument(modelPO.metalakeId != null, "Metalake id is
required");
+ Preconditions.checkArgument(modelPO.catalogId != null, "Catalog id is
required");
+ Preconditions.checkArgument(modelPO.schemaId != null, "Schema id is
required");
+ Preconditions.checkArgument(
+ modelPO.modelLatestVersion != null, "Model latest version is
required");
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(modelPO.auditInfo), "Audit info cannot be
empty");
+ Preconditions.checkArgument(modelPO.deletedAt != null, "Deleted at is
required");
+ return modelPO;
+ }
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/po/ModelVersionAliasRelPO.java
b/core/src/main/java/org/apache/gravitino/storage/relational/po/ModelVersionAliasRelPO.java
new file mode 100644
index 000000000..fc7896b25
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/po/ModelVersionAliasRelPO.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.storage.relational.po;
+
+import com.google.common.base.Preconditions;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+@EqualsAndHashCode
+@Getter
+public class ModelVersionAliasRelPO {
+
+ private Long modelId;
+
+ private Integer modelVersion;
+
+ private String modelAlias;
+
+ private Long deletedAt;
+
+ private ModelVersionAliasRelPO() {}
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+
+ private final ModelVersionAliasRelPO modelVersionAliasRelPO;
+
+ private Builder() {
+ modelVersionAliasRelPO = new ModelVersionAliasRelPO();
+ }
+
+ public Builder withModelId(Long modelId) {
+ modelVersionAliasRelPO.modelId = modelId;
+ return this;
+ }
+
+ public Builder withModelVersion(Integer modelVersion) {
+ modelVersionAliasRelPO.modelVersion = modelVersion;
+ return this;
+ }
+
+ public Builder withModelAlias(String modelAlias) {
+ modelVersionAliasRelPO.modelAlias = modelAlias;
+ return this;
+ }
+
+ public Builder withDeletedAt(Long deletedAt) {
+ modelVersionAliasRelPO.deletedAt = deletedAt;
+ return this;
+ }
+
+ public ModelVersionAliasRelPO build() {
+ Preconditions.checkArgument(modelVersionAliasRelPO.modelId != null,
"modelId is required");
+ Preconditions.checkArgument(
+ modelVersionAliasRelPO.modelVersion != null, "modelVersion is
required");
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(modelVersionAliasRelPO.modelAlias),
"modelAlias is required");
+ Preconditions.checkArgument(
+ modelVersionAliasRelPO.deletedAt != null, "deletedAt is required");
+ return modelVersionAliasRelPO;
+ }
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/po/ModelVersionPO.java
b/core/src/main/java/org/apache/gravitino/storage/relational/po/ModelVersionPO.java
new file mode 100644
index 000000000..ac5611e2d
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/po/ModelVersionPO.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.storage.relational.po;
+
+import com.google.common.base.Preconditions;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+@EqualsAndHashCode
+@Getter
+public class ModelVersionPO {
+
+ private Long modelId;
+
+ private Long metalakeId;
+
+ private Long catalogId;
+
+ private Long schemaId;
+
+ private Integer modelVersion;
+
+ private String modelVersionComment;
+
+ private String modelVersionProperties;
+
+ private String modelVersionUri;
+
+ private String auditInfo;
+
+ private Long deletedAt;
+
+ private ModelVersionPO() {}
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+
+ private final ModelVersionPO modelVersionPO;
+
+ private Builder() {
+ modelVersionPO = new ModelVersionPO();
+ }
+
+ public Builder withModelId(Long modelId) {
+ modelVersionPO.modelId = modelId;
+ return this;
+ }
+
+ public Builder withMetalakeId(Long metalakeId) {
+ modelVersionPO.metalakeId = metalakeId;
+ return this;
+ }
+
+ public Builder withCatalogId(Long catalogId) {
+ modelVersionPO.catalogId = catalogId;
+ return this;
+ }
+
+ public Builder withSchemaId(Long schemaId) {
+ modelVersionPO.schemaId = schemaId;
+ return this;
+ }
+
+ public Builder withModelVersion(Integer modelVersion) {
+ modelVersionPO.modelVersion = modelVersion;
+ return this;
+ }
+
+ public Builder withModelVersionComment(String modelVersionComment) {
+ modelVersionPO.modelVersionComment = modelVersionComment;
+ return this;
+ }
+
+ public Builder withModelVersionProperties(String modelVersionProperties) {
+ modelVersionPO.modelVersionProperties = modelVersionProperties;
+ return this;
+ }
+
+ public Builder withModelVersionUri(String modelVersionUri) {
+ modelVersionPO.modelVersionUri = modelVersionUri;
+ return this;
+ }
+
+ public Builder withAuditInfo(String auditInfo) {
+ modelVersionPO.auditInfo = auditInfo;
+ return this;
+ }
+
+ public Builder withDeletedAt(Long deletedAt) {
+ modelVersionPO.deletedAt = deletedAt;
+ return this;
+ }
+
+ public ModelVersionPO build() {
+ Preconditions.checkArgument(modelVersionPO.modelId != null, "Model id is
required");
+ Preconditions.checkArgument(modelVersionPO.metalakeId != null, "Metalake
id is required");
+ Preconditions.checkArgument(modelVersionPO.catalogId != null, "Catalog
id is required");
+ Preconditions.checkArgument(modelVersionPO.schemaId != null, "Schema id
is required");
+ Preconditions.checkArgument(modelVersionPO.modelVersion != null, "Model
version is required");
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(modelVersionPO.modelVersionUri),
+ "Model version uri cannot be empty");
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(modelVersionPO.auditInfo), "Audit info cannot
be empty");
+ Preconditions.checkArgument(modelVersionPO.deletedAt != null, "Deleted
at is required");
+
+ return modelVersionPO;
+ }
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/service/CatalogMetaService.java
b/core/src/main/java/org/apache/gravitino/storage/relational/service/CatalogMetaService.java
index 15f1d1a3c..0dcf0280c 100644
---
a/core/src/main/java/org/apache/gravitino/storage/relational/service/CatalogMetaService.java
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/service/CatalogMetaService.java
@@ -36,6 +36,7 @@ import org.apache.gravitino.meta.SchemaEntity;
import org.apache.gravitino.storage.relational.mapper.CatalogMetaMapper;
import org.apache.gravitino.storage.relational.mapper.FilesetMetaMapper;
import org.apache.gravitino.storage.relational.mapper.FilesetVersionMapper;
+import org.apache.gravitino.storage.relational.mapper.ModelMetaMapper;
import org.apache.gravitino.storage.relational.mapper.OwnerMetaMapper;
import org.apache.gravitino.storage.relational.mapper.SchemaMetaMapper;
import org.apache.gravitino.storage.relational.mapper.SecurableObjectMapper;
@@ -240,7 +241,11 @@ public class CatalogMetaService {
() ->
SessionUtils.doWithoutCommit(
TagMetadataObjectRelMapper.class,
- mapper ->
mapper.softDeleteTagMetadataObjectRelsByCatalogId(catalogId)));
+ mapper ->
mapper.softDeleteTagMetadataObjectRelsByCatalogId(catalogId)),
+ () ->
+ SessionUtils.doWithoutCommit(
+ ModelMetaMapper.class,
+ mapper ->
mapper.softDeleteModelMetasByCatalogId(catalogId)));
} else {
List<SchemaEntity> schemaEntities =
SchemaMetaService.getInstance()
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/service/MetadataObjectService.java
b/core/src/main/java/org/apache/gravitino/storage/relational/service/MetadataObjectService.java
index c32759af5..9834bafa0 100644
---
a/core/src/main/java/org/apache/gravitino/storage/relational/service/MetadataObjectService.java
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/service/MetadataObjectService.java
@@ -27,6 +27,7 @@ import org.apache.gravitino.storage.relational.po.CatalogPO;
import org.apache.gravitino.storage.relational.po.ColumnPO;
import org.apache.gravitino.storage.relational.po.FilesetPO;
import org.apache.gravitino.storage.relational.po.MetalakePO;
+import org.apache.gravitino.storage.relational.po.ModelPO;
import org.apache.gravitino.storage.relational.po.SchemaPO;
import org.apache.gravitino.storage.relational.po.TablePO;
import org.apache.gravitino.storage.relational.po.TopicPO;
@@ -70,6 +71,9 @@ public class MetadataObjectService {
return
FilesetMetaService.getInstance().getFilesetIdBySchemaIdAndName(schemaId,
names.get(2));
} else if (type == MetadataObject.Type.TOPIC) {
return
TopicMetaService.getInstance().getTopicIdBySchemaIdAndName(schemaId,
names.get(2));
+ } else if (type == MetadataObject.Type.MODEL) {
+ return ModelMetaService.getInstance()
+ .getModelIdBySchemaIdAndModelName(schemaId, names.get(2));
}
long tableId =
@@ -174,6 +178,20 @@ public class MetadataObjectService {
}
break;
+ case MODEL:
+ ModelPO modelPO =
ModelMetaService.getInstance().getModelPOById(objectId);
+ if (modelPO != null) {
+ fullName =
+ fullName != null
+ ? DOT_JOINER.join(modelPO.getModelName(), fullName)
+ : modelPO.getModelName();
+ objectId = modelPO.getSchemaId();
+ metadataType = MetadataObject.Type.SCHEMA;
+ } else {
+ return null;
+ }
+ break;
+
case COLUMN:
ColumnPO columnPO =
TableColumnMetaService.getInstance().getColumnPOById(objectId);
if (columnPO != null) {
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/service/MetalakeMetaService.java
b/core/src/main/java/org/apache/gravitino/storage/relational/service/MetalakeMetaService.java
index cf05af812..8fa94d4d7 100644
---
a/core/src/main/java/org/apache/gravitino/storage/relational/service/MetalakeMetaService.java
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/service/MetalakeMetaService.java
@@ -37,6 +37,7 @@ import
org.apache.gravitino.storage.relational.mapper.FilesetVersionMapper;
import org.apache.gravitino.storage.relational.mapper.GroupMetaMapper;
import org.apache.gravitino.storage.relational.mapper.GroupRoleRelMapper;
import org.apache.gravitino.storage.relational.mapper.MetalakeMetaMapper;
+import org.apache.gravitino.storage.relational.mapper.ModelMetaMapper;
import org.apache.gravitino.storage.relational.mapper.OwnerMetaMapper;
import org.apache.gravitino.storage.relational.mapper.RoleMetaMapper;
import org.apache.gravitino.storage.relational.mapper.SchemaMetaMapper;
@@ -243,7 +244,11 @@ public class MetalakeMetaService {
() ->
SessionUtils.doWithoutCommit(
OwnerMetaMapper.class,
- mapper ->
mapper.softDeleteOwnerRelByMetalakeId(metalakeId)));
+ mapper ->
mapper.softDeleteOwnerRelByMetalakeId(metalakeId)),
+ () ->
+ SessionUtils.doWithoutCommit(
+ ModelMetaMapper.class,
+ mapper ->
mapper.softDeleteModelMetasByMetalakeId(metalakeId)));
} else {
List<CatalogEntity> catalogEntities =
CatalogMetaService.getInstance()
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
new file mode 100644
index 000000000..2cb16bd07
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/service/ModelMetaService.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.storage.relational.service;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+import org.apache.gravitino.Entity;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.exceptions.NoSuchEntityException;
+import org.apache.gravitino.meta.ModelEntity;
+import org.apache.gravitino.storage.relational.mapper.ModelMetaMapper;
+import org.apache.gravitino.storage.relational.po.ModelPO;
+import org.apache.gravitino.storage.relational.utils.ExceptionUtils;
+import org.apache.gravitino.storage.relational.utils.POConverters;
+import org.apache.gravitino.storage.relational.utils.SessionUtils;
+import org.apache.gravitino.utils.NameIdentifierUtil;
+import org.apache.gravitino.utils.NamespaceUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ModelMetaService {
+
+ private static final Logger LOG =
LoggerFactory.getLogger(ModelMetaService.class);
+
+ private static final ModelMetaService INSTANCE = new ModelMetaService();
+
+ public static ModelMetaService getInstance() {
+ return INSTANCE;
+ }
+
+ private ModelMetaService() {}
+
+ public List<ModelEntity> listModelsByNamespace(Namespace ns) {
+ NamespaceUtil.checkModel(ns);
+
+ Long schemaId =
CommonMetaService.getInstance().getParentEntityIdByNamespace(ns);
+
+ List<ModelPO> modelPOs =
+ SessionUtils.getWithoutCommit(
+ ModelMetaMapper.class, mapper ->
mapper.listModelPOsBySchemaId(schemaId));
+
+ return modelPOs.stream().map(m -> POConverters.fromModelPO(m,
ns)).collect(Collectors.toList());
+ }
+
+ public ModelEntity getModelByIdentifier(NameIdentifier ident) {
+ NameIdentifierUtil.checkModel(ident);
+
+ Long schemaId =
CommonMetaService.getInstance().getParentEntityIdByNamespace(ident.namespace());
+
+ ModelPO modelPO =
+ SessionUtils.getWithoutCommit(
+ ModelMetaMapper.class,
+ mapper -> mapper.selectModelMetaBySchemaIdAndModelName(schemaId,
ident.name()));
+
+ if (modelPO == null) {
+ throw new NoSuchEntityException(
+ NoSuchEntityException.NO_SUCH_ENTITY_MESSAGE,
+ Entity.EntityType.MODEL.name().toLowerCase(Locale.ROOT),
+ ident.toString());
+ }
+
+ return POConverters.fromModelPO(modelPO, ident.namespace());
+ }
+
+ public void insertModel(ModelEntity modelEntity, boolean overwrite) throws
IOException {
+ NameIdentifierUtil.checkModel(modelEntity.nameIdentifier());
+
+ try {
+ ModelPO.Builder builder = ModelPO.builder();
+ fillModelPOBuilderParentEntityId(builder, modelEntity.namespace());
+
+ SessionUtils.doWithCommit(
+ ModelMetaMapper.class,
+ mapper -> {
+ ModelPO po = POConverters.initializeModelPO(modelEntity, builder);
+ if (overwrite) {
+ mapper.insertModelMetaOnDuplicateKeyUpdate(po);
+ } else {
+ mapper.insertModelMeta(po);
+ }
+ });
+ } catch (RuntimeException re) {
+ ExceptionUtils.checkSQLException(
+ re, Entity.EntityType.MODEL,
modelEntity.nameIdentifier().toString());
+ throw re;
+ }
+ }
+
+ public boolean deleteModel(NameIdentifier ident) {
+ NameIdentifierUtil.checkModel(ident);
+
+ Long schemaId;
+ try {
+ schemaId =
CommonMetaService.getInstance().getParentEntityIdByNamespace(ident.namespace());
+ } catch (NoSuchEntityException e) {
+ LOG.warn("Failed to delete model: {}", ident, e);
+ return false;
+ }
+
+ AtomicInteger modelDeletedCount = new AtomicInteger();
+ SessionUtils.doMultipleWithCommit(
+ () ->
+ modelDeletedCount.set(
+ SessionUtils.doWithoutCommitAndFetchResult(
+ ModelMetaMapper.class,
+ mapper ->
+
mapper.softDeleteModelMetaBySchemaIdAndModelName(schemaId, ident.name())))
+ // TODO(jerryshao): Add delete model version
+ );
+
+ return modelDeletedCount.get() > 0;
+ }
+
+ public int deleteModelMetasByLegacyTimeline(Long legacyTimeline, int limit) {
+ return SessionUtils.doWithCommitAndFetchResult(
+ ModelMetaMapper.class,
+ mapper -> mapper.deleteModelMetasByLegacyTimeline(legacyTimeline,
limit));
+ }
+
+ Long getModelIdBySchemaIdAndModelName(Long schemaId, String modelName) {
+ Long modelId =
+ SessionUtils.getWithoutCommit(
+ ModelMetaMapper.class,
+ mapper -> mapper.selectModelIdBySchemaIdAndModelName(schemaId,
modelName));
+
+ if (modelId == null) {
+ throw new NoSuchEntityException(
+ NoSuchEntityException.NO_SUCH_ENTITY_MESSAGE,
+ Entity.EntityType.MODEL.name().toLowerCase(Locale.ROOT),
+ modelName);
+ }
+
+ return modelId;
+ }
+
+ ModelPO getModelPOById(Long modelId) {
+ ModelPO modelPO =
+ SessionUtils.getWithoutCommit(
+ ModelMetaMapper.class, mapper ->
mapper.selectModelMetaByModelId(modelId));
+
+ if (modelPO == null) {
+ throw new NoSuchEntityException(
+ NoSuchEntityException.NO_SUCH_ENTITY_MESSAGE,
+ Entity.EntityType.MODEL.name().toLowerCase(Locale.ROOT),
+ modelId.toString());
+ }
+
+ return modelPO;
+ }
+
+ private void fillModelPOBuilderParentEntityId(ModelPO.Builder builder,
Namespace ns) {
+ NamespaceUtil.checkModel(ns);
+ String metalake = ns.level(0);
+ String catalog = ns.level(1);
+ String schema = ns.level(2);
+
+ Long metalakeId =
MetalakeMetaService.getInstance().getMetalakeIdByName(metalake);
+ builder.withMetalakeId(metalakeId);
+
+ Long catalogId =
+
CatalogMetaService.getInstance().getCatalogIdByMetalakeIdAndName(metalakeId,
catalog);
+ builder.withCatalogId(catalogId);
+
+ Long schemaId =
+
SchemaMetaService.getInstance().getSchemaIdByCatalogIdAndName(catalogId,
schema);
+ builder.withSchemaId(schemaId);
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/service/SchemaMetaService.java
b/core/src/main/java/org/apache/gravitino/storage/relational/service/SchemaMetaService.java
index 5d3fa1b4f..1229e3165 100644
---
a/core/src/main/java/org/apache/gravitino/storage/relational/service/SchemaMetaService.java
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/service/SchemaMetaService.java
@@ -31,10 +31,12 @@ import org.apache.gravitino.Namespace;
import org.apache.gravitino.exceptions.NoSuchEntityException;
import org.apache.gravitino.exceptions.NonEmptyEntityException;
import org.apache.gravitino.meta.FilesetEntity;
+import org.apache.gravitino.meta.ModelEntity;
import org.apache.gravitino.meta.SchemaEntity;
import org.apache.gravitino.meta.TableEntity;
import org.apache.gravitino.storage.relational.mapper.FilesetMetaMapper;
import org.apache.gravitino.storage.relational.mapper.FilesetVersionMapper;
+import org.apache.gravitino.storage.relational.mapper.ModelMetaMapper;
import org.apache.gravitino.storage.relational.mapper.OwnerMetaMapper;
import org.apache.gravitino.storage.relational.mapper.SchemaMetaMapper;
import org.apache.gravitino.storage.relational.mapper.SecurableObjectMapper;
@@ -226,7 +228,11 @@ public class SchemaMetaService {
() ->
SessionUtils.doWithoutCommit(
TagMetadataObjectRelMapper.class,
- mapper ->
mapper.softDeleteTagMetadataObjectRelsBySchemaId(schemaId)));
+ mapper ->
mapper.softDeleteTagMetadataObjectRelsBySchemaId(schemaId)),
+ () ->
+ SessionUtils.doWithoutCommit(
+ ModelMetaMapper.class,
+ mapper ->
mapper.softDeleteModelMetasBySchemaId(schemaId)));
} else {
List<TableEntity> tableEntities =
TableMetaService.getInstance()
@@ -250,6 +256,18 @@ public class SchemaMetaService {
throw new NonEmptyEntityException(
"Entity %s has sub-entities, you should remove sub-entities
first", identifier);
}
+ List<ModelEntity> modelEntities =
+ ModelMetaService.getInstance()
+ .listModelsByNamespace(
+ NamespaceUtil.ofModel(
+ identifier.namespace().level(0),
+ identifier.namespace().level(1),
+ schemaName));
+ if (!modelEntities.isEmpty()) {
+ throw new NonEmptyEntityException(
+ "Entity %s has sub-entities, you should remove sub-entities
first", identifier);
+ }
+
SessionUtils.doMultipleWithCommit(
() ->
SessionUtils.doWithoutCommit(
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/session/SqlSessionFactoryHelper.java
b/core/src/main/java/org/apache/gravitino/storage/relational/session/SqlSessionFactoryHelper.java
index 4fe53dba3..8bc7394d4 100644
---
a/core/src/main/java/org/apache/gravitino/storage/relational/session/SqlSessionFactoryHelper.java
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/session/SqlSessionFactoryHelper.java
@@ -33,6 +33,7 @@ import
org.apache.gravitino.storage.relational.mapper.FilesetVersionMapper;
import org.apache.gravitino.storage.relational.mapper.GroupMetaMapper;
import org.apache.gravitino.storage.relational.mapper.GroupRoleRelMapper;
import org.apache.gravitino.storage.relational.mapper.MetalakeMetaMapper;
+import org.apache.gravitino.storage.relational.mapper.ModelMetaMapper;
import org.apache.gravitino.storage.relational.mapper.OwnerMetaMapper;
import org.apache.gravitino.storage.relational.mapper.RoleMetaMapper;
import org.apache.gravitino.storage.relational.mapper.SchemaMetaMapper;
@@ -124,6 +125,7 @@ public class SqlSessionFactoryHelper {
configuration.addMapper(TagMetaMapper.class);
configuration.addMapper(TagMetadataObjectRelMapper.class);
configuration.addMapper(OwnerMetaMapper.class);
+ configuration.addMapper(ModelMetaMapper.class);
// Create the SqlSessionFactory object, it is a singleton object
if (sqlSessionFactory == null) {
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 4cccd0675..0bd0f4a74 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
@@ -45,6 +45,7 @@ import org.apache.gravitino.meta.CatalogEntity;
import org.apache.gravitino.meta.ColumnEntity;
import org.apache.gravitino.meta.FilesetEntity;
import org.apache.gravitino.meta.GroupEntity;
+import org.apache.gravitino.meta.ModelEntity;
import org.apache.gravitino.meta.RoleEntity;
import org.apache.gravitino.meta.SchemaEntity;
import org.apache.gravitino.meta.SchemaVersion;
@@ -64,6 +65,7 @@ import
org.apache.gravitino.storage.relational.po.FilesetVersionPO;
import org.apache.gravitino.storage.relational.po.GroupPO;
import org.apache.gravitino.storage.relational.po.GroupRoleRelPO;
import org.apache.gravitino.storage.relational.po.MetalakePO;
+import org.apache.gravitino.storage.relational.po.ModelPO;
import org.apache.gravitino.storage.relational.po.OwnerRelPO;
import org.apache.gravitino.storage.relational.po.RolePO;
import org.apache.gravitino.storage.relational.po.SchemaPO;
@@ -1287,4 +1289,39 @@ public class POConverters {
throw new RuntimeException("Failed to serialize json object:", e);
}
}
+
+ public static ModelEntity fromModelPO(ModelPO modelPO, Namespace namespace) {
+ try {
+ return ModelEntity.builder()
+ .withId(modelPO.getModelId())
+ .withName(modelPO.getModelName())
+ .withNamespace(namespace)
+ .withComment(modelPO.getModelComment())
+ .withLatestVersion(modelPO.getModelLatestVersion())
+ .withProperties(
+
JsonUtils.anyFieldMapper().readValue(modelPO.getModelProperties(), Map.class))
+ .withAuditInfo(
+ JsonUtils.anyFieldMapper().readValue(modelPO.getAuditInfo(),
AuditInfo.class))
+ .build();
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException("Failed to deserialize json object:", e);
+ }
+ }
+
+ public static ModelPO initializeModelPO(ModelEntity modelEntity,
ModelPO.Builder builder) {
+ try {
+ return builder
+ .withModelId(modelEntity.id())
+ .withModelName(modelEntity.name())
+ .withModelComment(modelEntity.comment())
+ .withModelLatestVersion(modelEntity.latestVersion())
+ .withModelProperties(
+
JsonUtils.anyFieldMapper().writeValueAsString(modelEntity.properties()))
+
.withAuditInfo(JsonUtils.anyFieldMapper().writeValueAsString(modelEntity.auditInfo()))
+ .withDeletedAt(DEFAULT_DELETED_AT)
+ .build();
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException("Failed to serialize json object:", e);
+ }
+ }
}
diff --git
a/core/src/test/java/org/apache/gravitino/storage/TestEntityStorage.java
b/core/src/test/java/org/apache/gravitino/storage/TestEntityStorage.java
index 01d919676..b0f516e50 100644
--- a/core/src/test/java/org/apache/gravitino/storage/TestEntityStorage.java
+++ b/core/src/test/java/org/apache/gravitino/storage/TestEntityStorage.java
@@ -73,6 +73,7 @@ import org.apache.gravitino.meta.CatalogEntity;
import org.apache.gravitino.meta.ColumnEntity;
import org.apache.gravitino.meta.FilesetEntity;
import org.apache.gravitino.meta.GroupEntity;
+import org.apache.gravitino.meta.ModelEntity;
import org.apache.gravitino.meta.RoleEntity;
import org.apache.gravitino.meta.SchemaEntity;
import org.apache.gravitino.meta.SchemaVersion;
@@ -81,6 +82,7 @@ import org.apache.gravitino.meta.TopicEntity;
import org.apache.gravitino.meta.UserEntity;
import org.apache.gravitino.rel.types.Type;
import org.apache.gravitino.rel.types.Types;
+import org.apache.gravitino.storage.relational.TestJDBCBackend;
import org.apache.gravitino.storage.relational.converters.H2ExceptionConverter;
import
org.apache.gravitino.storage.relational.converters.MySQLExceptionConverter;
import
org.apache.gravitino.storage.relational.converters.PostgreSQLExceptionConverter;
@@ -272,6 +274,15 @@ public class TestEntityStorage {
Namespace.of("metalake", "catalog", "schema1"),
"topic1",
auditInfo);
+ ModelEntity model1 =
+ TestJDBCBackend.createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ Namespace.of("metalake", "catalog", "schema1"),
+ "model1",
+ "model1",
+ 1,
+ null,
+ auditInfo);
UserEntity user1 =
createUser(RandomIdGenerator.INSTANCE.nextId(), "metalake", "user1",
auditInfo);
GroupEntity group1 =
@@ -287,6 +298,7 @@ public class TestEntityStorage {
store.put(table1);
store.put(fileset1);
store.put(topic1);
+ store.put(model1);
store.put(user1);
store.put(group1);
store.put(role1);
@@ -325,6 +337,12 @@ public class TestEntityStorage {
NameIdentifier.of("metalake", "catalog", "schema1",
"topic1"),
Entity.EntityType.TOPIC,
TopicEntity.class));
+ Assertions.assertDoesNotThrow(
+ () ->
+ store.get(
+ NameIdentifier.of("metalake", "catalog", "schema1",
"model1"),
+ Entity.EntityType.MODEL,
+ ModelEntity.class));
Assertions.assertDoesNotThrow(
() ->
@@ -386,6 +404,13 @@ public class TestEntityStorage {
NameIdentifier.of("metalake", "catalog", "schema1",
"topic1"),
Entity.EntityType.TOPIC,
TopicEntity.class));
+ Assertions.assertDoesNotThrow(
+ () ->
+ store.get(
+ NameIdentifier.of("metalake", "catalog", "schema1",
"model1"),
+ Entity.EntityType.MODEL,
+ ModelEntity.class));
+
Assertions.assertDoesNotThrow(
() ->
store.get(
@@ -595,6 +620,15 @@ public class TestEntityStorage {
TopicEntity topic1 =
createTopicEntity(
1L, Namespace.of("metalake", "catalog", "schema1"), "topic1",
auditInfo);
+ ModelEntity model1 =
+ TestJDBCBackend.createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ Namespace.of("metalake", "catalog", "schema1"),
+ "model1",
+ "model1",
+ 1,
+ null,
+ auditInfo);
SchemaEntity schema2 =
createSchemaEntity(2L, Namespace.of("metalake", "catalog"),
"schema2", auditInfo);
@@ -617,6 +651,16 @@ public class TestEntityStorage {
TopicEntity topic1InSchema2 =
createTopicEntity(
2L, Namespace.of("metalake", "catalog", "schema2"), "topic1",
auditInfo);
+ ModelEntity model1InSchema2 =
+ TestJDBCBackend.createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ Namespace.of("metalake", "catalog", "schema2"),
+ "model1",
+ "model1",
+ 1,
+ null,
+ auditInfo);
+
UserEntity user1 = createUser(1L, "metalake", "user1", auditInfo);
UserEntity user2 = createUser(2L, "metalake", "user2", auditInfo);
GroupEntity group1 = createGroup(1L, "metalake", "group1", auditInfo);
@@ -636,6 +680,8 @@ public class TestEntityStorage {
store.put(fileset1InSchema2);
store.put(topic1);
store.put(topic1InSchema2);
+ store.put(model1);
+ store.put(model1InSchema2);
store.put(user1);
store.put(user2);
store.put(group1);
@@ -656,6 +702,8 @@ public class TestEntityStorage {
fileset1InSchema2,
topic1,
topic1InSchema2,
+ model1,
+ model1InSchema2,
user1,
user2,
group1,
@@ -675,7 +723,9 @@ public class TestEntityStorage {
validateDeleteTopic(store, schema2, topic1, topic1InSchema2);
- validateDeleteSchema(store, schema1, table1, fileset1, topic1);
+ validateDeleteModel(store, schema2, model1, model1InSchema2);
+
+ validateDeleteSchema(store, schema1, table1, fileset1, topic1, model1);
validateDeleteCatalog(
store,
@@ -687,7 +737,9 @@ public class TestEntityStorage {
fileset1,
fileset1InSchema2,
topic1,
- topic1InSchema2);
+ topic1InSchema2,
+ model1,
+ model1InSchema2);
validateDeleteMetalake(store, metalake, catalogCopy, user2, group2,
role2);
@@ -774,6 +826,19 @@ public class TestEntityStorage {
topic1InSchema2.name(),
topic1InSchema2.auditInfo());
store.put(topic1InSchema2New);
+
+ // model
+ ModelEntity model1New =
+ TestJDBCBackend.createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ model1.namespace(),
+ model1.name(),
+ model1.comment(),
+ model1.latestVersion(),
+ model1.properties(),
+ model1.auditInfo());
+ store.put(model1New);
+
UserEntity userNew =
createUser(RandomIdGenerator.INSTANCE.nextId(), "metalake",
"userNew", auditInfo);
store.put(userNew);
@@ -790,7 +855,9 @@ public class TestEntityStorage {
validateDeleteTopicCascade(store, topic1New);
- validateDeleteSchemaCascade(store, schema1New, table1New, fileset1New,
topic1New);
+ validateDeleteModelCascade(store, model1New);
+
+ validateDeleteSchemaCascade(store, schema1New, table1New, fileset1New,
topic1New, model1New);
validateDeleteCatalogCascade(store, catalogNew, schema2New);
@@ -838,12 +905,23 @@ public class TestEntityStorage {
TopicEntity topicEntity1 =
createTopicEntity(RandomIdGenerator.INSTANCE.nextId(), namespace,
"sameName", auditInfo);
+ ModelEntity model1 =
+ TestJDBCBackend.createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ namespace,
+ "sameName",
+ "model1",
+ 1,
+ null,
+ auditInfo);
+
store.put(metalake1);
store.put(catalog1);
store.put(schema1);
store.put(table1);
store.put(filesetEntity1);
store.put(topicEntity1);
+ store.put(model1);
NameIdentifier identifier = NameIdentifier.of("metalake1", "catalog1",
"schema1", "sameName");
@@ -856,12 +934,16 @@ public class TestEntityStorage {
TopicEntity loadedTopicEntity =
store.get(identifier, Entity.EntityType.TOPIC, TopicEntity.class);
Assertions.assertEquals(topicEntity1.id(), loadedTopicEntity.id());
+ ModelEntity loadedModelEntity =
+ store.get(identifier, Entity.EntityType.MODEL, ModelEntity.class);
+ Assertions.assertEquals(model1.id(), loadedModelEntity.id());
// Remove table will not affect another
Assertions.assertTrue(store.delete(identifier, Entity.EntityType.TABLE));
Assertions.assertNotNull(
store.get(identifier, Entity.EntityType.FILESET,
FilesetEntity.class));
Assertions.assertNotNull(store.get(identifier, Entity.EntityType.TOPIC,
TopicEntity.class));
+ Assertions.assertNotNull(store.get(identifier, Entity.EntityType.MODEL,
ModelEntity.class));
// JDBC use id as the primary key, so we need to change the id of table1
if we want to store
// it again
@@ -873,6 +955,7 @@ public class TestEntityStorage {
store.delete(identifier, Entity.EntityType.FILESET);
Assertions.assertNotNull(store.get(identifier, Entity.EntityType.TABLE,
TableEntity.class));
Assertions.assertNotNull(store.get(identifier, Entity.EntityType.TOPIC,
TopicEntity.class));
+ Assertions.assertNotNull(store.get(identifier, Entity.EntityType.MODEL,
ModelEntity.class));
filesetEntity1 =
createFilesetEntity(
@@ -884,6 +967,7 @@ public class TestEntityStorage {
Assertions.assertNotNull(store.get(identifier, Entity.EntityType.TABLE,
TableEntity.class));
Assertions.assertNotNull(
store.get(identifier, Entity.EntityType.FILESET,
FilesetEntity.class));
+ Assertions.assertNotNull(store.get(identifier, Entity.EntityType.MODEL,
ModelEntity.class));
topicEntity1 =
createTopicEntity(RandomIdGenerator.INSTANCE.nextId(), namespace,
"sameName", auditInfo);
@@ -899,9 +983,12 @@ public class TestEntityStorage {
NameIdentifier changedNameIdentifier =
NameIdentifier.of("metalake1", "catalog1", "schema1",
"sameNameChanged");
- store.get(changedNameIdentifier, Entity.EntityType.TABLE,
TableEntity.class);
- store.get(identifier, Entity.EntityType.FILESET, FilesetEntity.class);
- store.get(identifier, Entity.EntityType.TOPIC, TopicEntity.class);
+ Assertions.assertNotNull(
+ store.get(changedNameIdentifier, Entity.EntityType.TABLE,
TableEntity.class));
+ Assertions.assertNotNull(
+ store.get(identifier, Entity.EntityType.FILESET,
FilesetEntity.class));
+ Assertions.assertNotNull(store.get(identifier, Entity.EntityType.TOPIC,
TopicEntity.class));
+ Assertions.assertNotNull(store.get(identifier, Entity.EntityType.MODEL,
ModelEntity.class));
table1 =
createTableEntity(RandomIdGenerator.INSTANCE.nextId(), namespace,
"sameName", auditInfo);
@@ -915,9 +1002,11 @@ public class TestEntityStorage {
Entity.EntityType.FILESET,
e -> createFilesetEntity(filesetId, namespace, "sameNameChanged",
e.auditInfo()));
- store.get(identifier, Entity.EntityType.TABLE, TableEntity.class);
- store.get(changedNameIdentifier, Entity.EntityType.FILESET,
FilesetEntity.class);
- store.get(identifier, Entity.EntityType.TOPIC, TopicEntity.class);
+ Assertions.assertNotNull(store.get(identifier, Entity.EntityType.TABLE,
TableEntity.class));
+ Assertions.assertNotNull(
+ store.get(changedNameIdentifier, Entity.EntityType.FILESET,
FilesetEntity.class));
+ Assertions.assertNotNull(store.get(identifier, Entity.EntityType.TOPIC,
TopicEntity.class));
+ Assertions.assertNotNull(store.get(identifier, Entity.EntityType.MODEL,
ModelEntity.class));
filesetEntity1 =
createFilesetEntity(
@@ -932,9 +1021,12 @@ public class TestEntityStorage {
Entity.EntityType.TOPIC,
e -> createTopicEntity(topicId, namespace, "sameNameChanged",
e.auditInfo()));
- store.get(identifier, Entity.EntityType.TABLE, TableEntity.class);
- store.get(identifier, Entity.EntityType.FILESET, FilesetEntity.class);
- store.get(changedNameIdentifier, Entity.EntityType.TOPIC,
TopicEntity.class);
+ Assertions.assertNotNull(store.get(identifier, Entity.EntityType.TABLE,
TableEntity.class));
+ Assertions.assertNotNull(
+ store.get(identifier, Entity.EntityType.FILESET,
FilesetEntity.class));
+ Assertions.assertNotNull(
+ store.get(changedNameIdentifier, Entity.EntityType.TOPIC,
TopicEntity.class));
+ Assertions.assertNotNull(store.get(identifier, Entity.EntityType.MODEL,
ModelEntity.class));
destroy(type);
}
@@ -1354,6 +1446,15 @@ public class TestEntityStorage {
Assertions.assertFalse(store.delete(topic1.nameIdentifier(),
Entity.EntityType.TOPIC));
}
+ private void validateDeleteModelCascade(EntityStore store, ModelEntity
model1)
+ throws IOException {
+ // Delete the topic 'metalake.catalog.schema1.topic1'
+ Assertions.assertTrue(store.delete(model1.nameIdentifier(),
EntityType.MODEL));
+ Assertions.assertFalse(store.exists(model1.nameIdentifier(),
EntityType.MODEL));
+ // Delete again should return false
+ Assertions.assertFalse(store.delete(model1.nameIdentifier(),
EntityType.MODEL));
+ }
+
private void validateDeleteFilesetCascade(EntityStore store, FilesetEntity
fileset1)
throws IOException {
// Delete the fileset 'metalake.catalog.schema1.fileset1'
@@ -1469,7 +1570,8 @@ public class TestEntityStorage {
SchemaEntity schema1,
TableEntity table1,
FilesetEntity fileset1,
- TopicEntity topic1)
+ TopicEntity topic1,
+ ModelEntity model1)
throws IOException {
TableEntity table1New =
createTableEntityWithColumns(
@@ -1494,6 +1596,17 @@ public class TestEntityStorage {
topic1.auditInfo());
store.put(topic1New);
+ ModelEntity model1New =
+ TestJDBCBackend.createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ model1.namespace(),
+ model1.name(),
+ model1.comment(),
+ model1.latestVersion(),
+ model1.properties(),
+ model1.auditInfo());
+ store.put(model1New);
+
Assertions.assertThrowsExactly(
NonEmptyEntityException.class,
() -> store.delete(schema1.nameIdentifier(),
Entity.EntityType.SCHEMA));
@@ -1527,6 +1640,10 @@ public class TestEntityStorage {
Assertions.assertThrows(
NoSuchEntityException.class,
() -> store.get(topic1.nameIdentifier(), Entity.EntityType.TOPIC,
TopicEntity.class));
+
+ Assertions.assertThrows(
+ NoSuchEntityException.class,
+ () -> store.get(model1.nameIdentifier(), Entity.EntityType.MODEL,
ModelEntity.class));
}
private void validateDeleteMetalake(
@@ -1567,7 +1684,9 @@ public class TestEntityStorage {
FilesetEntity fileset1,
FilesetEntity fileset1InSchema2,
TopicEntity topic1,
- TopicEntity topic1InSchema2)
+ TopicEntity topic1InSchema2,
+ ModelEntity model1,
+ ModelEntity model1InSchema2)
throws IOException {
// Now try to delete all schemas under catalog;
Assertions.assertThrowsExactly(
@@ -1577,6 +1696,8 @@ public class TestEntityStorage {
validateDeletedColumns(table1.id(), table1.type());
store.delete(fileset1.nameIdentifier(), Entity.EntityType.FILESET);
store.delete(topic1.nameIdentifier(), Entity.EntityType.TOPIC);
+ store.delete(model1.nameIdentifier(), Entity.EntityType.MODEL);
+
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
@@ -1588,6 +1709,7 @@ public class TestEntityStorage {
Assertions.assertFalse(
store.exists(fileset1InSchema2.nameIdentifier(),
Entity.EntityType.FILESET));
Assertions.assertFalse(store.exists(topic1InSchema2.nameIdentifier(),
Entity.EntityType.TOPIC));
+ Assertions.assertFalse(store.exists(model1InSchema2.nameIdentifier(),
Entity.EntityType.MODEL));
store.delete(schema2.nameIdentifier(), Entity.EntityType.SCHEMA);
store.delete(catalog.nameIdentifier(), Entity.EntityType.CATALOG);
@@ -1601,7 +1723,8 @@ public class TestEntityStorage {
SchemaEntity schema1,
TableEntity table1,
FilesetEntity fileset1,
- TopicEntity topic1)
+ TopicEntity topic1,
+ ModelEntity model1)
throws IOException {
// Delete the schema 'metalake.catalog.schema1' but failed, because it ha
sub-entities;
NonEmptyEntityException exception =
@@ -1614,26 +1737,30 @@ public class TestEntityStorage {
// has not been deleted yet;
Assertions.assertTrue(store.exists(schema1.nameIdentifier(),
Entity.EntityType.SCHEMA));
Assertions.assertTrue(store.exists(table1.nameIdentifier(),
Entity.EntityType.TABLE));
- ;
+
Assertions.assertTrue(store.exists(fileset1.nameIdentifier(),
Entity.EntityType.FILESET));
Assertions.assertTrue(store.exists(topic1.nameIdentifier(),
Entity.EntityType.TOPIC));
+ Assertions.assertTrue(store.exists(model1.nameIdentifier(),
Entity.EntityType.MODEL));
// Delete table1,fileset1 and schema1
Assertions.assertTrue(store.delete(table1.nameIdentifier(),
Entity.EntityType.TABLE));
validateDeletedColumns(table1.id(), table1.type());
Assertions.assertTrue(store.delete(fileset1.nameIdentifier(),
Entity.EntityType.FILESET));
Assertions.assertTrue(store.delete(topic1.nameIdentifier(),
Entity.EntityType.TOPIC));
+ Assertions.assertTrue(store.delete(model1.nameIdentifier(),
Entity.EntityType.MODEL));
Assertions.assertTrue(store.delete(schema1.nameIdentifier(),
Entity.EntityType.SCHEMA));
// Make sure table1, fileset1 in 'metalake.catalog.schema1' can't be
access;
Assertions.assertFalse(store.exists(table1.nameIdentifier(),
Entity.EntityType.TABLE));
Assertions.assertFalse(store.exists(fileset1.nameIdentifier(),
Entity.EntityType.FILESET));
Assertions.assertFalse(store.exists(topic1.nameIdentifier(),
Entity.EntityType.TOPIC));
+ Assertions.assertFalse(store.exists(model1.nameIdentifier(),
Entity.EntityType.MODEL));
Assertions.assertFalse(store.exists(schema1.nameIdentifier(),
Entity.EntityType.SCHEMA));
// Delete again should return false
Assertions.assertFalse(store.delete(table1.nameIdentifier(),
Entity.EntityType.TABLE));
Assertions.assertFalse(store.delete(fileset1.nameIdentifier(),
Entity.EntityType.FILESET));
Assertions.assertFalse(store.delete(topic1.nameIdentifier(),
Entity.EntityType.TOPIC));
+ Assertions.assertFalse(store.delete(model1.nameIdentifier(),
Entity.EntityType.MODEL));
Assertions.assertFalse(store.delete(schema1.nameIdentifier(),
Entity.EntityType.SCHEMA));
// Now we re-insert schema1, table1, fileset1 and topic1, and everything
should be OK
@@ -1666,6 +1793,17 @@ public class TestEntityStorage {
topic1.auditInfo());
store.put(topic1New);
+ ModelEntity model1New =
+ TestJDBCBackend.createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ model1.namespace(),
+ model1.name(),
+ model1.comment(),
+ model1.latestVersion(),
+ model1.properties(),
+ model1.auditInfo());
+ store.put(model1New);
+
Assertions.assertEquals(
schema1New,
store.get(schema1.nameIdentifier(), Entity.EntityType.SCHEMA,
SchemaEntity.class));
@@ -1676,6 +1814,8 @@ public class TestEntityStorage {
store.get(fileset1.nameIdentifier(), Entity.EntityType.FILESET,
FilesetEntity.class));
Assertions.assertEquals(
topic1New, store.get(topic1.nameIdentifier(), Entity.EntityType.TOPIC,
TopicEntity.class));
+ Assertions.assertEquals(
+ model1New, store.get(model1.nameIdentifier(), Entity.EntityType.MODEL,
ModelEntity.class));
}
private void validateDeleteUser(EntityStore store, UserEntity user1) throws
IOException {
@@ -1745,6 +1885,21 @@ public class TestEntityStorage {
Assertions.assertTrue(store.exists(table1InSchema2.nameIdentifier(),
Entity.EntityType.TABLE));
}
+ private void validateDeleteModel(
+ EntityStore store, SchemaEntity schema2, ModelEntity model1, ModelEntity
model1InSchema2)
+ throws IOException {
+ Assertions.assertTrue(store.delete(model1InSchema2.nameIdentifier(),
Entity.EntityType.MODEL));
+ Assertions.assertFalse(store.exists(model1InSchema2.nameIdentifier(),
Entity.EntityType.MODEL));
+ // delete again should return false
+ Assertions.assertFalse(store.delete(model1InSchema2.nameIdentifier(),
Entity.EntityType.MODEL));
+
+ Assertions.assertEquals(
+ model1, store.get(model1.nameIdentifier(), Entity.EntityType.MODEL,
ModelEntity.class));
+ // Make sure schema 'metalake.catalog.schema2' still exist;
+ Assertions.assertEquals(
+ schema2, store.get(schema2.nameIdentifier(), Entity.EntityType.SCHEMA,
SchemaEntity.class));
+ }
+
private static void validateAllEntityExist(
BaseMetalake metalake,
EntityStore store,
@@ -1758,6 +1913,8 @@ public class TestEntityStorage {
FilesetEntity fileset1InSchema2,
TopicEntity topic1,
TopicEntity topic1InSchema2,
+ ModelEntity model1,
+ ModelEntity model1InSchema2,
UserEntity user1,
UserEntity user2,
GroupEntity group1,
@@ -1796,6 +1953,11 @@ public class TestEntityStorage {
Assertions.assertEquals(
topic1InSchema2,
store.get(topic1InSchema2.nameIdentifier(), Entity.EntityType.TOPIC,
TopicEntity.class));
+ Assertions.assertEquals(
+ model1, store.get(model1.nameIdentifier(), Entity.EntityType.MODEL,
ModelEntity.class));
+ Assertions.assertEquals(
+ model1InSchema2,
+ store.get(model1InSchema2.nameIdentifier(), Entity.EntityType.MODEL,
ModelEntity.class));
Assertions.assertEquals(
user1, store.get(user1.nameIdentifier(), Entity.EntityType.USER,
UserEntity.class));
Assertions.assertEquals(
diff --git
a/core/src/test/java/org/apache/gravitino/storage/relational/TestJDBCBackend.java
b/core/src/test/java/org/apache/gravitino/storage/relational/TestJDBCBackend.java
index 01b1cac96..739edba96 100644
---
a/core/src/test/java/org/apache/gravitino/storage/relational/TestJDBCBackend.java
+++
b/core/src/test/java/org/apache/gravitino/storage/relational/TestJDBCBackend.java
@@ -27,6 +27,7 @@ import static
org.apache.gravitino.Configs.ENTITY_RELATIONAL_STORE;
import static org.apache.gravitino.Configs.ENTITY_STORE;
import static org.apache.gravitino.Configs.RELATIONAL_ENTITY_STORE;
import static org.apache.gravitino.SupportsRelationOperations.Type.OWNER_REL;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -67,6 +68,7 @@ import org.apache.gravitino.meta.BaseMetalake;
import org.apache.gravitino.meta.CatalogEntity;
import org.apache.gravitino.meta.FilesetEntity;
import org.apache.gravitino.meta.GroupEntity;
+import org.apache.gravitino.meta.ModelEntity;
import org.apache.gravitino.meta.RoleEntity;
import org.apache.gravitino.meta.SchemaEntity;
import org.apache.gravitino.meta.SchemaVersion;
@@ -297,6 +299,29 @@ public class TestJDBCBackend {
auditInfo);
backend.insert(topic, false);
assertThrows(EntityAlreadyExistsException.class, () ->
backend.insert(topicCopy, false));
+
+ ModelEntity model =
+ createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ NamespaceUtil.ofModel("metalake", "catalog", "schema"),
+ "model",
+ "model comment",
+ 1,
+ ImmutableMap.of("key", "value"),
+ auditInfo);
+ ModelEntity modelCopy =
+ createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ NamespaceUtil.ofModel("metalake", "catalog", "schema"),
+ "model",
+ "model comment",
+ 1,
+ ImmutableMap.of("key", "value"),
+ auditInfo);
+
+ assertDoesNotThrow(() -> backend.insert(model, false));
+ assertThrows(EntityAlreadyExistsException.class, () ->
backend.insert(modelCopy, false));
+ assertDoesNotThrow(() -> backend.insert(modelCopy, true));
}
@Test
@@ -575,6 +600,17 @@ public class TestJDBCBackend {
auditInfo);
backend.insert(topic, false);
+ ModelEntity model =
+ createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ NamespaceUtil.ofModel("metalake", "catalog", "schema"),
+ "model",
+ "model comment",
+ 1,
+ ImmutableMap.of("key", "value"),
+ auditInfo);
+ backend.insert(model, false);
+
// update fileset properties and version
FilesetEntity filesetV2 =
createFilesetEntity(
@@ -736,6 +772,9 @@ public class TestJDBCBackend {
List<TopicEntity> topics = backend.list(topic.namespace(),
Entity.EntityType.TOPIC, true);
assertTrue(topics.contains(topic));
+ List<ModelEntity> models = backend.list(model.namespace(),
Entity.EntityType.MODEL, true);
+ assertTrue(models.contains(model));
+
RoleEntity roleEntity = backend.get(role.nameIdentifier(),
Entity.EntityType.ROLE);
assertEquals(role, roleEntity);
assertEquals(1,
RoleMetaService.getInstance().listRolesByUserId(user.id()).size());
@@ -830,6 +869,7 @@ public class TestJDBCBackend {
assertFalse(backend.exists(table.nameIdentifier(),
Entity.EntityType.TABLE));
assertFalse(backend.exists(topic.nameIdentifier(),
Entity.EntityType.TOPIC));
+ assertFalse(backend.exists(model.nameIdentifier(),
Entity.EntityType.MODEL));
assertFalse(backend.exists(role.nameIdentifier(), Entity.EntityType.ROLE));
assertEquals(0,
RoleMetaService.getInstance().listRolesByUserId(user.id()).size());
@@ -861,6 +901,7 @@ public class TestJDBCBackend {
assertTrue(legacyRecordExistsInDB(schema.id(), Entity.EntityType.SCHEMA));
assertTrue(legacyRecordExistsInDB(table.id(), Entity.EntityType.TABLE));
assertTrue(legacyRecordExistsInDB(topic.id(), Entity.EntityType.TOPIC));
+ assertTrue(legacyRecordExistsInDB(model.id(), Entity.EntityType.MODEL));
assertTrue(legacyRecordExistsInDB(fileset.id(),
Entity.EntityType.FILESET));
assertTrue(legacyRecordExistsInDB(role.id(), Entity.EntityType.ROLE));
assertTrue(legacyRecordExistsInDB(user.id(), Entity.EntityType.USER));
@@ -883,6 +924,7 @@ public class TestJDBCBackend {
assertFalse(legacyRecordExistsInDB(table.id(), Entity.EntityType.TABLE));
assertFalse(legacyRecordExistsInDB(fileset.id(),
Entity.EntityType.FILESET));
assertFalse(legacyRecordExistsInDB(topic.id(), Entity.EntityType.TOPIC));
+ assertFalse(legacyRecordExistsInDB(model.id(), Entity.EntityType.MODEL));
assertFalse(legacyRecordExistsInDB(role.id(), Entity.EntityType.ROLE));
assertFalse(legacyRecordExistsInDB(user.id(), Entity.EntityType.USER));
assertFalse(legacyRecordExistsInDB(group.id(), Entity.EntityType.GROUP));
@@ -937,6 +979,10 @@ public class TestJDBCBackend {
tableName = "topic_meta";
idColumnName = "topic_id";
break;
+ case MODEL:
+ tableName = "model_meta";
+ idColumnName = "model_id";
+ break;
case ROLE:
tableName = "role_meta";
idColumnName = "role_id";
@@ -1326,4 +1372,47 @@ public class TestJDBCBackend {
.withSecurableObjects(securableObjects)
.build();
}
+
+ public static ModelEntity createModelEntity(
+ Long id,
+ Namespace namespace,
+ String name,
+ String comment,
+ Integer latestVersion,
+ Map<String, String> properties,
+ AuditInfo auditInfo) {
+ return ModelEntity.builder()
+ .withId(id)
+ .withName(name)
+ .withNamespace(namespace)
+ .withComment(comment)
+ .withLatestVersion(latestVersion)
+ .withProperties(properties)
+ .withAuditInfo(auditInfo)
+ .build();
+ }
+
+ protected void createParentEntities(
+ String metalakeName, String catalogName, String schemaName, AuditInfo
auditInfo)
+ throws IOException {
+ BaseMetalake metalake =
+ createBaseMakeLake(RandomIdGenerator.INSTANCE.nextId(), metalakeName,
auditInfo);
+ backend.insert(metalake, false);
+
+ CatalogEntity catalog =
+ createCatalog(
+ RandomIdGenerator.INSTANCE.nextId(),
+ Namespace.of(metalakeName),
+ catalogName,
+ auditInfo);
+ backend.insert(catalog, false);
+
+ SchemaEntity schema =
+ createSchemaEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ Namespace.of(metalakeName, catalog.name()),
+ schemaName,
+ auditInfo);
+ backend.insert(schema, false);
+ }
}
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
new file mode 100644
index 000000000..067edd5a2
--- /dev/null
+++
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestModelMetaService.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.storage.relational.service;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.time.Instant;
+import java.util.List;
+import java.util.Map;
+import org.apache.gravitino.EntityAlreadyExistsException;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.exceptions.NoSuchEntityException;
+import org.apache.gravitino.meta.AuditInfo;
+import org.apache.gravitino.meta.ModelEntity;
+import org.apache.gravitino.storage.RandomIdGenerator;
+import org.apache.gravitino.storage.relational.TestJDBCBackend;
+import org.apache.gravitino.storage.relational.po.ModelPO;
+import org.apache.gravitino.storage.relational.utils.POConverters;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestModelMetaService extends TestJDBCBackend {
+
+ private static final String METALAKE_NAME = "metalake_for_model_meta_test";
+
+ private static final String CATALOG_NAME = "catalog_for_model_meta_test";
+
+ private static final String SCHEMA_NAME = "schema_for_model_meta_test";
+
+ private static final Namespace MODEL_NS = Namespace.of(METALAKE_NAME,
CATALOG_NAME, SCHEMA_NAME);
+
+ private final AuditInfo auditInfo =
+
AuditInfo.builder().withCreator("test").withCreateTime(Instant.now()).build();
+
+ @Test
+ public void testInsertAndSelectModel() throws IOException {
+ createParentEntities(METALAKE_NAME, CATALOG_NAME, SCHEMA_NAME, auditInfo);
+ Map<String, String> properties = ImmutableMap.of("k1", "v1");
+
+ ModelEntity modelEntity =
+ createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ MODEL_NS,
+ "model1",
+ "model1 comment",
+ 0,
+ properties,
+ auditInfo);
+
+ Assertions.assertDoesNotThrow(
+ () -> ModelMetaService.getInstance().insertModel(modelEntity, false));
+
+ ModelEntity registeredModelEntity =
+
ModelMetaService.getInstance().getModelByIdentifier(modelEntity.nameIdentifier());
+ Assertions.assertEquals(modelEntity, registeredModelEntity);
+
+ // Test insert again without overwrite
+ Assertions.assertThrows(
+ EntityAlreadyExistsException.class,
+ () -> ModelMetaService.getInstance().insertModel(modelEntity, false));
+
+ // Test insert again with overwrite
+ ModelEntity modelEntity2 =
+ createModelEntity(
+ modelEntity.id(),
+ modelEntity.namespace(),
+ "model2",
+ null,
+ modelEntity.latestVersion(),
+ null,
+ auditInfo);
+ Assertions.assertDoesNotThrow(
+ () -> ModelMetaService.getInstance().insertModel(modelEntity2, true));
+ ModelEntity registeredModelEntity2 =
+
ModelMetaService.getInstance().getModelByIdentifier(modelEntity2.nameIdentifier());
+ Assertions.assertEquals(modelEntity2, registeredModelEntity2);
+
+ // Test get an in-existent model
+ Assertions.assertThrows(
+ NoSuchEntityException.class,
+ () ->
+ ModelMetaService.getInstance()
+ .getModelByIdentifier(NameIdentifier.of(MODEL_NS, "model3")));
+
+ // Test get model by id
+ ModelPO modelPO =
ModelMetaService.getInstance().getModelPOById(modelEntity.id());
+ Assertions.assertEquals(
+ modelEntity2, POConverters.fromModelPO(modelPO,
modelEntity.namespace()));
+
+ // Test get in-existent model by id
+ Assertions.assertThrows(
+ NoSuchEntityException.class, () ->
ModelMetaService.getInstance().getModelPOById(111L));
+
+ // Test get model id by name
+ Long schemaId =
CommonMetaService.getInstance().getParentEntityIdByNamespace(MODEL_NS);
+ Long modelId =
+
ModelMetaService.getInstance().getModelIdBySchemaIdAndModelName(schemaId,
"model2");
+ Assertions.assertEquals(modelEntity2.id(), modelId);
+
+ // Test get in-existent model id by name
+ Assertions.assertThrows(
+ NoSuchEntityException.class,
+ () ->
ModelMetaService.getInstance().getModelIdBySchemaIdAndModelName(schemaId,
"model3"));
+ }
+
+ @Test
+ public void testInsertAndListModels() throws IOException {
+ createParentEntities(METALAKE_NAME, CATALOG_NAME, SCHEMA_NAME, auditInfo);
+ Map<String, String> properties = ImmutableMap.of("k1", "v1");
+
+ ModelEntity modelEntity1 =
+ createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ MODEL_NS,
+ "model1",
+ "model1 comment",
+ 0,
+ properties,
+ auditInfo);
+ ModelEntity modelEntity2 =
+ createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ MODEL_NS,
+ "model2",
+ "model2 comment",
+ 0,
+ properties,
+ auditInfo);
+
+ Assertions.assertDoesNotThrow(
+ () -> ModelMetaService.getInstance().insertModel(modelEntity1, false));
+ Assertions.assertDoesNotThrow(
+ () -> ModelMetaService.getInstance().insertModel(modelEntity2, false));
+
+ List<ModelEntity> modelEntities =
+ ModelMetaService.getInstance().listModelsByNamespace(MODEL_NS);
+ Assertions.assertEquals(2, modelEntities.size());
+ Assertions.assertTrue(modelEntities.contains(modelEntity1));
+ Assertions.assertTrue(modelEntities.contains(modelEntity2));
+
+ // Test list models by in-existent namespace
+ Assertions.assertThrows(
+ NoSuchEntityException.class,
+ () ->
+ ModelMetaService.getInstance()
+ .listModelsByNamespace(Namespace.of(METALAKE_NAME,
CATALOG_NAME, "inexistent")));
+ }
+
+ @Test
+ public void testInsertAndDeleteModel() throws IOException {
+ createParentEntities(METALAKE_NAME, CATALOG_NAME, SCHEMA_NAME, auditInfo);
+ Map<String, String> properties = ImmutableMap.of("k1", "v1");
+
+ ModelEntity modelEntity =
+ createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ MODEL_NS,
+ "model1",
+ "model1 comment",
+ 0,
+ properties,
+ auditInfo);
+
+ Assertions.assertDoesNotThrow(
+ () -> ModelMetaService.getInstance().insertModel(modelEntity, false));
+
+
Assertions.assertTrue(ModelMetaService.getInstance().deleteModel(modelEntity.nameIdentifier()));
+ Assertions.assertThrows(
+ NoSuchEntityException.class,
+ () ->
ModelMetaService.getInstance().getModelByIdentifier(modelEntity.nameIdentifier()));
+
+ // Delete again should return false
+ Assertions.assertFalse(
+
ModelMetaService.getInstance().deleteModel(modelEntity.nameIdentifier()));
+
+ // Test delete in-existent model
+ Assertions.assertFalse(
+ ModelMetaService.getInstance().deleteModel(NameIdentifier.of(MODEL_NS,
"inexistent")));
+
+ // Test delete in-existent schema
+ Assertions.assertFalse(
+ ModelMetaService.getInstance()
+ .deleteModel(NameIdentifier.of(METALAKE_NAME, CATALOG_NAME,
"inexistent", "model1")));
+ }
+}
diff --git
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestTableColumnMetaService.java
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestTableColumnMetaService.java
index 30eb6bda6..a0e9f3427 100644
---
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestTableColumnMetaService.java
+++
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestTableColumnMetaService.java
@@ -28,10 +28,7 @@ import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.Namespace;
import org.apache.gravitino.exceptions.NoSuchEntityException;
import org.apache.gravitino.meta.AuditInfo;
-import org.apache.gravitino.meta.BaseMetalake;
-import org.apache.gravitino.meta.CatalogEntity;
import org.apache.gravitino.meta.ColumnEntity;
-import org.apache.gravitino.meta.SchemaEntity;
import org.apache.gravitino.meta.TableEntity;
import org.apache.gravitino.rel.expressions.literals.Literals;
import org.apache.gravitino.rel.types.Types;
@@ -53,7 +50,7 @@ public class TestTableColumnMetaService extends
TestJDBCBackend {
public void testInsertAndGetTableColumns() throws IOException {
String catalogName = "catalog1";
String schemaName = "schema1";
- createParentEntities(METALAKE_NAME, catalogName, schemaName);
+ createParentEntities(METALAKE_NAME, catalogName, schemaName, auditInfo);
// Create a table entity without columns
TableEntity createdTable =
@@ -154,7 +151,7 @@ public class TestTableColumnMetaService extends
TestJDBCBackend {
public void testUpdateTable() throws IOException {
String catalogName = "catalog1";
String schemaName = "schema1";
- createParentEntities(METALAKE_NAME, catalogName, schemaName);
+ createParentEntities(METALAKE_NAME, catalogName, schemaName, auditInfo);
// Create a table entity without columns
TableEntity createdTable =
@@ -331,7 +328,7 @@ public class TestTableColumnMetaService extends
TestJDBCBackend {
public void testCreateAndDeleteTable() throws IOException {
String catalogName = "catalog1";
String schemaName = "schema1";
- createParentEntities(METALAKE_NAME, catalogName, schemaName);
+ createParentEntities(METALAKE_NAME, catalogName, schemaName, auditInfo);
// Create a table entity with column
ColumnEntity column =
@@ -378,7 +375,7 @@ public class TestTableColumnMetaService extends
TestJDBCBackend {
public void testDeleteMetalake() throws IOException {
String catalogName = "catalog1";
String schemaName = "schema1";
- createParentEntities(METALAKE_NAME, catalogName, schemaName);
+ createParentEntities(METALAKE_NAME, catalogName, schemaName, auditInfo);
// Create a table entity with column
ColumnEntity column =
@@ -425,7 +422,7 @@ public class TestTableColumnMetaService extends
TestJDBCBackend {
public void testGetColumnIdAndPO() throws IOException {
String catalogName = "catalog1";
String schemaName = "schema1";
- createParentEntities(METALAKE_NAME, catalogName, schemaName);
+ createParentEntities(METALAKE_NAME, catalogName, schemaName, auditInfo);
// Create a table entity with column
ColumnEntity column =
@@ -550,27 +547,4 @@ public class TestTableColumnMetaService extends
TestJDBCBackend {
Assertions.assertEquals(expectedColumn.auditInfo(),
column.auditInfo());
});
}
-
- private void createParentEntities(String metalakeName, String catalogName,
String schemaName)
- throws IOException {
- BaseMetalake metalake =
- createBaseMakeLake(RandomIdGenerator.INSTANCE.nextId(), metalakeName,
auditInfo);
- backend.insert(metalake, false);
-
- CatalogEntity catalog =
- createCatalog(
- RandomIdGenerator.INSTANCE.nextId(),
- Namespace.of(metalakeName),
- catalogName,
- auditInfo);
- backend.insert(catalog, false);
-
- SchemaEntity schema =
- createSchemaEntity(
- RandomIdGenerator.INSTANCE.nextId(),
- Namespace.of(metalakeName, catalog.name()),
- schemaName,
- auditInfo);
- backend.insert(schema, false);
- }
}
diff --git
a/core/src/test/java/org/apache/gravitino/storage/relational/utils/TestPOConverters.java
b/core/src/test/java/org/apache/gravitino/storage/relational/utils/TestPOConverters.java
index 76a2c35d3..703b79e70 100644
---
a/core/src/test/java/org/apache/gravitino/storage/relational/utils/TestPOConverters.java
+++
b/core/src/test/java/org/apache/gravitino/storage/relational/utils/TestPOConverters.java
@@ -46,6 +46,7 @@ import org.apache.gravitino.meta.BaseMetalake;
import org.apache.gravitino.meta.CatalogEntity;
import org.apache.gravitino.meta.ColumnEntity;
import org.apache.gravitino.meta.FilesetEntity;
+import org.apache.gravitino.meta.ModelEntity;
import org.apache.gravitino.meta.SchemaEntity;
import org.apache.gravitino.meta.SchemaVersion;
import org.apache.gravitino.meta.TableEntity;
@@ -60,6 +61,7 @@ import org.apache.gravitino.storage.relational.po.ColumnPO;
import org.apache.gravitino.storage.relational.po.FilesetPO;
import org.apache.gravitino.storage.relational.po.FilesetVersionPO;
import org.apache.gravitino.storage.relational.po.MetalakePO;
+import org.apache.gravitino.storage.relational.po.ModelPO;
import org.apache.gravitino.storage.relational.po.OwnerRelPO;
import org.apache.gravitino.storage.relational.po.SchemaPO;
import org.apache.gravitino.storage.relational.po.TablePO;
@@ -832,6 +834,160 @@ public class TestPOConverters {
assertEquals(0, ownerRelPO.getDeletedAt());
}
+ @Test
+ public void testInitModelPO() throws JsonProcessingException {
+ AuditInfo auditInfo =
+
AuditInfo.builder().withCreator("creator").withCreateTime(FIX_INSTANT).build();
+ ModelEntity modelEntity =
+ ModelEntity.builder()
+ .withId(1L)
+ .withName("test")
+ .withNamespace(Namespace.of("test_metalake", "test_catalog",
"test_schema"))
+ .withComment("this is test")
+ .withProperties(ImmutableMap.of("key", "value"))
+ .withLatestVersion(1)
+ .withAuditInfo(auditInfo)
+ .build();
+
+ ModelPO.Builder builder =
+
ModelPO.builder().withMetalakeId(1L).withCatalogId(1L).withSchemaId(1L);
+ ModelPO modelPO = POConverters.initializeModelPO(modelEntity, builder);
+
+ assertEquals(1, modelPO.getModelId());
+ assertEquals("test", modelPO.getModelName());
+ assertEquals(1, modelPO.getMetalakeId());
+ assertEquals(1, modelPO.getCatalogId());
+ assertEquals(1, modelPO.getSchemaId());
+ assertEquals("this is test", modelPO.getModelComment());
+
+ Map<String, String> resultProperties =
+ JsonUtils.anyFieldMapper().readValue(modelPO.getModelProperties(),
Map.class);
+ assertEquals(ImmutableMap.of("key", "value"), resultProperties);
+
+ AuditInfo resultAuditInfo =
+ JsonUtils.anyFieldMapper().readValue(modelPO.getAuditInfo(),
AuditInfo.class);
+ assertEquals(auditInfo, resultAuditInfo);
+ assertEquals(1, modelPO.getModelLatestVersion());
+ assertEquals(0, modelPO.getDeletedAt());
+
+ // Test with null fields
+ ModelEntity modelEntityWithNull =
+ ModelEntity.builder()
+ .withId(1L)
+ .withName("test")
+ .withNamespace(Namespace.of("test_metalake", "test_catalog",
"test_schema"))
+ .withComment(null)
+ .withProperties(null)
+ .withLatestVersion(1)
+ .withAuditInfo(auditInfo)
+ .build();
+
+ ModelPO.Builder builderWithNull =
+
ModelPO.builder().withMetalakeId(1L).withCatalogId(1L).withSchemaId(1L);
+ ModelPO modelPOWithNull =
POConverters.initializeModelPO(modelEntityWithNull, builderWithNull);
+
+ assertNull(modelPOWithNull.getModelComment());
+ Map<String, String> resultPropertiesWithNull =
+
JsonUtils.anyFieldMapper().readValue(modelPOWithNull.getModelProperties(),
Map.class);
+ assertNull(resultPropertiesWithNull);
+ }
+
+ @Test
+ public void testFromModelPO() throws JsonProcessingException {
+ AuditInfo auditInfo =
+
AuditInfo.builder().withCreator("creator").withCreateTime(FIX_INSTANT).build();
+ Map<String, String> properties = ImmutableMap.of("key", "value");
+ Map<String, String> emptyProperties = Collections.emptyMap();
+ Namespace namespace = Namespace.of("test_metalake", "test_catalog",
"test_schema");
+
+ ModelPO modelPO =
+ ModelPO.builder()
+ .withModelId(1L)
+ .withModelName("test")
+ .withMetalakeId(1L)
+ .withCatalogId(1L)
+ .withSchemaId(1L)
+ .withModelComment("this is test")
+
.withModelProperties(JsonUtils.anyFieldMapper().writeValueAsString(properties))
+
.withAuditInfo(JsonUtils.anyFieldMapper().writeValueAsString(auditInfo))
+ .withModelLatestVersion(1)
+ .withDeletedAt(0L)
+ .build();
+
+ ModelEntity expectedModel =
+ ModelEntity.builder()
+ .withId(1L)
+ .withName("test")
+ .withNamespace(namespace)
+ .withComment("this is test")
+ .withProperties(properties)
+ .withLatestVersion(1)
+ .withAuditInfo(auditInfo)
+ .build();
+
+ ModelEntity convertedModel = POConverters.fromModelPO(modelPO, namespace);
+ assertEquals(expectedModel, convertedModel);
+
+ // test null fields
+ ModelPO modelPOWithNull =
+ ModelPO.builder()
+ .withModelId(1L)
+ .withModelName("test")
+ .withMetalakeId(1L)
+ .withCatalogId(1L)
+ .withSchemaId(1L)
+ .withModelComment(null)
+
.withModelProperties(JsonUtils.anyFieldMapper().writeValueAsString(null))
+
.withAuditInfo(JsonUtils.anyFieldMapper().writeValueAsString(auditInfo))
+ .withModelLatestVersion(1)
+ .withDeletedAt(0L)
+ .build();
+
+ ModelEntity expectedModelWithNull =
+ ModelEntity.builder()
+ .withId(1L)
+ .withName("test")
+ .withNamespace(namespace)
+ .withComment(null)
+ .withProperties(null)
+ .withLatestVersion(1)
+ .withAuditInfo(auditInfo)
+ .build();
+
+ ModelEntity convertedModelWithNull =
POConverters.fromModelPO(modelPOWithNull, namespace);
+ assertEquals(expectedModelWithNull, convertedModelWithNull);
+
+ // Test with empty properties
+ ModelPO modelPOWithEmptyProperties =
+ ModelPO.builder()
+ .withModelId(1L)
+ .withModelName("test")
+ .withMetalakeId(1L)
+ .withCatalogId(1L)
+ .withSchemaId(1L)
+ .withModelComment("this is test")
+
.withModelProperties(JsonUtils.anyFieldMapper().writeValueAsString(emptyProperties))
+
.withAuditInfo(JsonUtils.anyFieldMapper().writeValueAsString(auditInfo))
+ .withModelLatestVersion(1)
+ .withDeletedAt(0L)
+ .build();
+
+ ModelEntity expectedModelWithEmptyProperties =
+ ModelEntity.builder()
+ .withId(1L)
+ .withName("test")
+ .withNamespace(namespace)
+ .withComment("this is test")
+ .withProperties(emptyProperties)
+ .withLatestVersion(1)
+ .withAuditInfo(auditInfo)
+ .build();
+
+ ModelEntity convertedModelWithEmptyProperties =
+ POConverters.fromModelPO(modelPOWithEmptyProperties, namespace);
+ assertEquals(expectedModelWithEmptyProperties,
convertedModelWithEmptyProperties);
+ }
+
private static BaseMetalake createMetalake(Long id, String name, String
comment) {
AuditInfo auditInfo =
AuditInfo.builder().withCreator("creator").withCreateTime(FIX_INSTANT).build();
diff --git a/scripts/h2/schema-0.8.0-h2.sql b/scripts/h2/schema-0.8.0-h2.sql
index 495d46f80..541c60da9 100644
--- a/scripts/h2/schema-0.8.0-h2.sql
+++ b/scripts/h2/schema-0.8.0-h2.sql
@@ -289,3 +289,50 @@ CREATE TABLE IF NOT EXISTS `owner_meta` (
KEY `idx_oid` (`owner_id`),
KEY `idx_meid` (`metadata_object_id`)
) ENGINE=InnoDB;
+
+CREATE TABLE IF NOT EXISTS `model_meta` (
+ `model_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'model id',
+ `model_name` VARCHAR(128) NOT NULL COMMENT 'model name',
+ `metalake_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'metalake id',
+ `catalog_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'catalog id',
+ `schema_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'schema id',
+ `model_comment` TEXT DEFAULT NULL COMMENT 'model comment',
+ `model_properties` MEDIUMTEXT DEFAULT NULL COMMENT 'model properties',
+ `model_latest_version` INT UNSIGNED DEFAULT 0 COMMENT 'model latest
version',
+ `audit_info` MEDIUMTEXT NOT NULL COMMENT 'model audit info',
+ `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'model deleted
at',
+ PRIMARY KEY (`model_id`),
+ UNIQUE KEY `uk_sid_mn_del` (`schema_id`, `model_name`, `deleted_at`),
+ KEY `idx_mmid` (`metalake_id`),
+ KEY `idx_mcid` (`catalog_id`)
+) ENGINE=InnoDB;
+
+CREATE TABLE IF NOT EXISTS `model_version_info` (
+ `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment
id',
+ `metalake_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'metalake id',
+ `catalog_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'catalog id',
+ `schema_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'schema id',
+ `model_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'model id',
+ `version` INT UNSIGNED NOT NULL COMMENT 'model version',
+ `model_version_comment` TEXT DEFAULT NULL COMMENT 'model version comment',
+ `model_version_properties` MEDIUMTEXT DEFAULT NULL COMMENT 'model version
properties',
+ `model_version_uri` TEXT NOT NULL COMMENT 'model storage uri',
+ `audit_info` MEDIUMTEXT NOT NULL COMMENT 'model version audit info',
+ `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'model version
deleted at',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_mid_ver_del` (`model_id`, `version`, `deleted_at`),
+ KEY `idx_vmid` (`metalake_id`),
+ KEY `idx_vcid` (`catalog_id`),
+ KEY `idx_vsid` (`schema_id`)
+) ENGINE=InnoDB;
+
+CREATE TABLE IF NOT EXISTS `model_version_alias_rel` (
+ `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment
id',
+ `model_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'model id',
+ `model_version` INT UNSIGNED NOT NULL COMMENT 'model version',
+ `model_version_alias` VARCHAR(128) NOT NULL COMMENT 'model version alias',
+ `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'model version
alias deleted at',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_mi_mv_mva_del` (`model_id`, `model_version`,
`model_version_alias`, `deleted_at`),
+ KEY `idx_mva` (`model_version_alias`)
+) ENGINE=InnoDB;
diff --git a/scripts/h2/upgrade-0.7.0-to-0.8.0-h2.sql
b/scripts/h2/upgrade-0.7.0-to-0.8.0-h2.sql
index 89de7de2e..60c89a86e 100644
--- a/scripts/h2/upgrade-0.7.0-to-0.8.0-h2.sql
+++ b/scripts/h2/upgrade-0.7.0-to-0.8.0-h2.sql
@@ -18,3 +18,50 @@
--
ALTER TABLE `role_meta_securable_object` ALTER COLUMN `privilege_names`
TEXT(81920);
ALTER TABLE `role_meta_securable_object` ALTER COLUMN `privilege_conditions`
TEXT(81920);
+
+CREATE TABLE IF NOT EXISTS `model_meta` (
+ `model_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'model id',
+ `model_name` VARCHAR(128) NOT NULL COMMENT 'model name',
+ `metalake_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'metalake id',
+ `catalog_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'catalog id',
+ `schema_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'schema id',
+ `model_comment` TEXT DEFAULT NULL COMMENT 'model comment',
+ `model_properties` MEDIUMTEXT DEFAULT NULL COMMENT 'model properties',
+ `model_latest_version` INT UNSIGNED DEFAULT 0 COMMENT 'model latest
version',
+ `audit_info` MEDIUMTEXT NOT NULL COMMENT 'model audit info',
+ `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'model deleted
at',
+ PRIMARY KEY (`model_id`),
+ UNIQUE KEY `uk_sid_mn_del` (`schema_id`, `model_name`, `deleted_at`),
+ KEY `idx_mmid` (`metalake_id`),
+ KEY `idx_mcid` (`catalog_id`)
+) ENGINE=InnoDB;
+
+CREATE TABLE IF NOT EXISTS `model_version_info` (
+ `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment
id',
+ `metalake_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'metalake id',
+ `catalog_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'catalog id',
+ `schema_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'schema id',
+ `model_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'model id',
+ `version` INT UNSIGNED NOT NULL COMMENT 'model version',
+ `model_version_comment` TEXT DEFAULT NULL COMMENT 'model version comment',
+ `model_version_properties` MEDIUMTEXT DEFAULT NULL COMMENT 'model version
properties',
+ `model_version_uri` TEXT NOT NULL COMMENT 'model storage uri',
+ `audit_info` MEDIUMTEXT NOT NULL COMMENT 'model version audit info',
+ `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'model version
deleted at',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_mid_ver_del` (`model_id`, `version`, `deleted_at`),
+ KEY `idx_vmid` (`metalake_id`),
+ KEY `idx_vcid` (`catalog_id`),
+ KEY `idx_vsid` (`schema_id`)
+) ENGINE=InnoDB;
+
+CREATE TABLE IF NOT EXISTS `model_version_alias_rel` (
+ `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment
id',
+ `model_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'model id',
+ `model_version` INT UNSIGNED NOT NULL COMMENT 'model version',
+ `model_version_alias` VARCHAR(128) NOT NULL COMMENT 'model version alias',
+ `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'model version
alias deleted at',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_mi_mv_mva_del` (`model_id`, `model_version`,
`model_version_alias`, `deleted_at`),
+ KEY `idx_mva` (`model_version_alias`)
+) ENGINE=InnoDB;
diff --git a/scripts/mysql/schema-0.8.0-mysql.sql
b/scripts/mysql/schema-0.8.0-mysql.sql
index 2c8a46305..07b8e146c 100644
--- a/scripts/mysql/schema-0.8.0-mysql.sql
+++ b/scripts/mysql/schema-0.8.0-mysql.sql
@@ -280,3 +280,50 @@ CREATE TABLE IF NOT EXISTS `owner_meta` (
KEY `idx_oid` (`owner_id`),
KEY `idx_meid` (`metadata_object_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'owner
relation';
+
+CREATE TABLE IF NOT EXISTS `model_meta` (
+ `model_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'model id',
+ `model_name` VARCHAR(128) NOT NULL COMMENT 'model name',
+ `metalake_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'metalake id',
+ `catalog_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'catalog id',
+ `schema_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'schema id',
+ `model_comment` TEXT DEFAULT NULL COMMENT 'model comment',
+ `model_properties` MEDIUMTEXT DEFAULT NULL COMMENT 'model properties',
+ `model_latest_version` INT UNSIGNED DEFAULT 0 COMMENT 'model latest
version',
+ `audit_info` MEDIUMTEXT NOT NULL COMMENT 'model audit info',
+ `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'model deleted
at',
+ PRIMARY KEY (`model_id`),
+ UNIQUE KEY `uk_sid_mn_del` (`schema_id`, `model_name`, `deleted_at`),
+ KEY `idx_mid` (`metalake_id`),
+ KEY `idx_cid` (`catalog_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'model
metadata';
+
+CREATE TABLE IF NOT EXISTS `model_version_info` (
+ `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment
id',
+ `metalake_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'metalake id',
+ `catalog_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'catalog id',
+ `schema_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'schema id',
+ `model_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'model id',
+ `version` INT UNSIGNED NOT NULL COMMENT 'model version',
+ `model_version_comment` TEXT DEFAULT NULL COMMENT 'model version comment',
+ `model_version_properties` MEDIUMTEXT DEFAULT NULL COMMENT 'model version
properties',
+ `model_version_uri` TEXT NOT NULL COMMENT 'model storage uri',
+ `audit_info` MEDIUMTEXT NOT NULL COMMENT 'model version audit info',
+ `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'model version
deleted at',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_mid_ver_del` (`model_id`, `version`, `deleted_at`),
+ KEY `idx_mid` (`metalake_id`),
+ KEY `idx_cid` (`catalog_id`),
+ KEY `idx_sid` (`schema_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'model
version info';
+
+CREATE TABLE IF NOT EXISTS `model_version_alias_rel` (
+ `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment
id',
+ `model_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'model id',
+ `model_version` INT UNSIGNED NOT NULL COMMENT 'model version',
+ `model_version_alias` VARCHAR(128) NOT NULL COMMENT 'model version alias',
+ `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'model version
alias deleted at',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_mi_mv_mva_del` (`model_id`, `model_version`,
`model_version_alias`, `deleted_at`),
+ KEY `idx_mva` (`model_version_alias`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT
'model_version_alias_rel';
diff --git a/scripts/mysql/upgrade-0.7.0-to-0.8.0-mysql.sql
b/scripts/mysql/upgrade-0.7.0-to-0.8.0-mysql.sql
index fb591ae56..7858237c7 100644
--- a/scripts/mysql/upgrade-0.7.0-to-0.8.0-mysql.sql
+++ b/scripts/mysql/upgrade-0.7.0-to-0.8.0-mysql.sql
@@ -18,3 +18,50 @@
--
ALTER TABLE `role_meta_securable_object` MODIFY COLUMN `privilege_names`
TEXT(81920);
ALTER TABLE `role_meta_securable_object` MODIFY COLUMN `privilege_conditions`
TEXT(81920);
+
+CREATE TABLE IF NOT EXISTS `model_meta` (
+ `model_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'model id',
+ `model_name` VARCHAR(128) NOT NULL COMMENT 'model name',
+ `metalake_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'metalake id',
+ `catalog_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'catalog id',
+ `schema_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'schema id',
+ `model_comment` TEXT DEFAULT NULL COMMENT 'model comment',
+ `model_properties` MEDIUMTEXT DEFAULT NULL COMMENT 'model properties',
+ `model_latest_version` INT UNSIGNED DEFAULT 0 COMMENT 'model latest
version',
+ `audit_info` MEDIUMTEXT NOT NULL COMMENT 'model audit info',
+ `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'model deleted
at',
+ PRIMARY KEY (`model_id`),
+ UNIQUE KEY `uk_sid_mn_del` (`schema_id`, `model_name`, `deleted_at`),
+ KEY `idx_mid` (`metalake_id`),
+ KEY `idx_cid` (`catalog_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'model
metadata';
+
+CREATE TABLE IF NOT EXISTS `model_version_info` (
+ `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment
id',
+ `metalake_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'metalake id',
+ `catalog_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'catalog id',
+ `schema_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'schema id',
+ `model_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'model id',
+ `version` INT UNSIGNED NOT NULL COMMENT 'model version',
+ `model_version_comment` TEXT DEFAULT NULL COMMENT 'model version comment',
+ `model_version_properties` MEDIUMTEXT DEFAULT NULL COMMENT 'model version
properties',
+ `model_version_uri` TEXT NOT NULL COMMENT 'model storage uri',
+ `audit_info` MEDIUMTEXT NOT NULL COMMENT 'model version audit info',
+ `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'model version
deleted at',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_mid_ver_del` (`model_id`, `version`, `deleted_at`),
+ KEY `idx_mid` (`metalake_id`),
+ KEY `idx_cid` (`catalog_id`),
+ KEY `idx_sid` (`schema_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'model
version info';
+
+CREATE TABLE IF NOT EXISTS `model_version_alias_rel` (
+ `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment
id',
+ `model_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'model id',
+ `model_version` INT UNSIGNED NOT NULL COMMENT 'model version',
+ `model_version_alias` VARCHAR(128) NOT NULL COMMENT 'model version alias',
+ `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'model version
alias deleted at',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_mi_mv_mva_del` (`model_id`, `model_version`,
`model_version_alias`, `deleted_at`),
+ KEY `idx_mva` (`model_version_alias`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT
'model_version_alias_rel';
diff --git a/scripts/postgresql/schema-0.8.0-postgresql.sql
b/scripts/postgresql/schema-0.8.0-postgresql.sql
index 0f483a20f..fea045843 100644
--- a/scripts/postgresql/schema-0.8.0-postgresql.sql
+++ b/scripts/postgresql/schema-0.8.0-postgresql.sql
@@ -500,3 +500,87 @@ COMMENT ON COLUMN owner_meta.current_version IS 'owner
relation current version'
COMMENT ON COLUMN owner_meta.last_version IS 'owner relation last version';
COMMENT ON COLUMN owner_meta.deleted_at IS 'owner relation deleted at';
+
+CREATE TABLE IF NOT EXISTS model_meta (
+ model_id BIGINT NOT NULL,
+ model_name VARCHAR(128) NOT NULL,
+ metalake_id BIGINT NOT NULL,
+ catalog_id BIGINT NOT NULL,
+ schema_id BIGINT NOT NULL,
+ model_comment VARCHAR(65535) DEFAULT NULL,
+ model_properties TEXT DEFAULT NULL,
+ model_latest_version INT NOT NULL DEFAULT 0,
+ audit_info TEXT NOT NULL,
+ deleted_at BIGINT NOT NULL DEFAULT 0,
+ PRIMARY KEY (model_id),
+ UNIQUE (schema_id, model_name, deleted_at)
+ );
+
+CREATE INDEX IF NOT EXISTS idx_metalake_id ON model_meta (metalake_id);
+CREATE INDEX IF NOT EXISTS idx_catalog_id ON model_meta (catalog_id);
+COMMENT ON TABLE model_meta IS 'model metadata';
+
+COMMENT ON COLUMN model_meta.model_id IS 'model id';
+COMMENT ON COLUMN model_meta.model_name IS 'model name';
+COMMENT ON COLUMN model_meta.metalake_id IS 'metalake id';
+COMMENT ON COLUMN model_meta.catalog_id IS 'catalog id';
+COMMENT ON COLUMN model_meta.schema_id IS 'schema id';
+COMMENT ON COLUMN model_meta.model_comment IS 'model comment';
+COMMENT ON COLUMN model_meta.model_properties IS 'model properties';
+COMMENT ON COLUMN model_meta.model_latest_version IS 'model max version';
+COMMENT ON COLUMN model_meta.audit_info IS 'model audit info';
+COMMENT ON COLUMN model_meta.deleted_at IS 'model deleted at';
+
+
+CREATE TABLE IF NOT EXISTS model_version_info (
+ id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ metalake_id BIGINT NOT NULL,
+ catalog_id BIGINT NOT NULL,
+ schema_id BIGINT NOT NULL,
+ model_id BIGINT NOT NULL,
+ version INT NOT NULL,
+ model_version_comment VARCHAR(65535) DEFAULT NULL,
+ model_version_properties TEXT DEFAULT NULL,
+ model_version_uri TEXT NOT NULL,
+ audit_info TEXT NOT NULL,
+ deleted_at BIGINT NOT NULL DEFAULT 0,
+ PRIMARY KEY (id),
+ UNIQUE (model_id, version, deleted_at)
+ );
+
+CREATE INDEX IF NOT EXISTS idx_metalake_id ON model_version_info (metalake_id);
+CREATE INDEX IF NOT EXISTS idx_catalog_id ON model_version_info (catalog_id);
+CREATE INDEX IF NOT EXISTS idx_schema_id ON model_version_info (schema_id);
+COMMENT ON TABLE model_version_info IS 'model version information';
+
+COMMENT ON COLUMN model_version_info.id IS 'auto increment id';
+COMMENT ON COLUMN model_version_info.metalake_id IS 'metalake id';
+COMMENT ON COLUMN model_version_info.catalog_id IS 'catalog id';
+COMMENT ON COLUMN model_version_info.schema_id IS 'schema id';
+COMMENT ON COLUMN model_version_info.model_id IS 'model id';
+COMMENT ON COLUMN model_version_info.version IS 'model version';
+COMMENT ON COLUMN model_version_info.model_version_comment IS 'model version
comment';
+COMMENT ON COLUMN model_version_info.model_version_properties IS 'model
version properties';
+COMMENT ON COLUMN model_version_info.model_version_uri IS 'model storage uri';
+COMMENT ON COLUMN model_version_info.audit_info IS 'model version audit info';
+COMMENT ON COLUMN model_version_info.deleted_at IS 'model version deleted at';
+
+
+CREATE TABLE IF NOT EXISTS model_version_alias_rel (
+ id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ model_id BIGINT NOT NULL,
+ model_version INT NOT NULL,
+ model_version_alias VARCHAR(128) NOT NULL,
+ deleted_at BIGINT NOT NULL DEFAULT 0,
+ PRIMARY KEY (id),
+ UNIQUE (model_id, model_version, model_version_alias, deleted_at)
+ );
+
+CREATE INDEX IF NOT EXISTS idx_model_version_alias on model_version_alias_rel
(model_version_alias);
+COMMENT ON TABLE model_version_alias_rel IS 'model version alias relation';
+
+COMMENT ON COLUMN model_version_alias_rel.id IS 'auto increment id';
+COMMENT ON COLUMN model_version_alias_rel.model_id IS 'model id';
+COMMENT ON COLUMN model_version_alias_rel.model_version IS 'model version';
+COMMENT ON COLUMN model_version_alias_rel.model_version_alias IS 'model
version alias';
+COMMENT ON COLUMN model_version_alias_rel.deleted_at IS 'model version alias
deleted at';
diff --git a/scripts/postgresql/upgrade-0.7.0-to-0.8.0-postgresql.sql
b/scripts/postgresql/upgrade-0.7.0-to-0.8.0-postgresql.sql
index 574351ec4..a94c4ab22 100644
--- a/scripts/postgresql/upgrade-0.7.0-to-0.8.0-postgresql.sql
+++ b/scripts/postgresql/upgrade-0.7.0-to-0.8.0-postgresql.sql
@@ -18,3 +18,87 @@
--
ALTER TABLE role_meta_securable_object ALTER COLUMN privilege_names TYPE
VARCHAR(81920);
ALTER TABLE role_meta_securable_object ALTER COLUMN privilege_conditions TYPE
VARCHAR(81920);
+
+CREATE TABLE IF NOT EXISTS model_meta (
+ model_id BIGINT NOT NULL,
+ model_name VARCHAR(128) NOT NULL,
+ metalake_id BIGINT NOT NULL,
+ catalog_id BIGINT NOT NULL,
+ schema_id BIGINT NOT NULL,
+ model_comment VARCHAR(65535) DEFAULT NULL,
+ model_properties TEXT DEFAULT NULL,
+ model_latest_version INT NOT NULL DEFAULT 0,
+ audit_info TEXT NOT NULL,
+ deleted_at BIGINT NOT NULL DEFAULT 0,
+ PRIMARY KEY (model_id),
+ UNIQUE (schema_id, model_name, deleted_at)
+ );
+
+CREATE INDEX IF NOT EXISTS idx_metalake_id ON model_meta (metalake_id);
+CREATE INDEX IF NOT EXISTS idx_catalog_id ON model_meta (catalog_id);
+COMMENT ON TABLE model_meta IS 'model metadata';
+
+COMMENT ON COLUMN model_meta.model_id IS 'model id';
+COMMENT ON COLUMN model_meta.model_name IS 'model name';
+COMMENT ON COLUMN model_meta.metalake_id IS 'metalake id';
+COMMENT ON COLUMN model_meta.catalog_id IS 'catalog id';
+COMMENT ON COLUMN model_meta.schema_id IS 'schema id';
+COMMENT ON COLUMN model_meta.model_comment IS 'model comment';
+COMMENT ON COLUMN model_meta.model_properties IS 'model properties';
+COMMENT ON COLUMN model_meta.model_latest_version IS 'model max version';
+COMMENT ON COLUMN model_meta.audit_info IS 'model audit info';
+COMMENT ON COLUMN model_meta.deleted_at IS 'model deleted at';
+
+
+CREATE TABLE IF NOT EXISTS model_version_info (
+ id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ metalake_id BIGINT NOT NULL,
+ catalog_id BIGINT NOT NULL,
+ schema_id BIGINT NOT NULL,
+ model_id BIGINT NOT NULL,
+ version INT NOT NULL,
+ model_version_comment VARCHAR(65535) DEFAULT NULL,
+ model_version_properties TEXT DEFAULT NULL,
+ model_version_uri TEXT NOT NULL,
+ audit_info TEXT NOT NULL,
+ deleted_at BIGINT NOT NULL DEFAULT 0,
+ PRIMARY KEY (id),
+ UNIQUE (model_id, version, deleted_at)
+ );
+
+CREATE INDEX IF NOT EXISTS idx_metalake_id ON model_version_info (metalake_id);
+CREATE INDEX IF NOT EXISTS idx_catalog_id ON model_version_info (catalog_id);
+CREATE INDEX IF NOT EXISTS idx_schema_id ON model_version_info (schema_id);
+COMMENT ON TABLE model_version_info IS 'model version information';
+
+COMMENT ON COLUMN model_version_info.id IS 'auto increment id';
+COMMENT ON COLUMN model_version_info.metalake_id IS 'metalake id';
+COMMENT ON COLUMN model_version_info.catalog_id IS 'catalog id';
+COMMENT ON COLUMN model_version_info.schema_id IS 'schema id';
+COMMENT ON COLUMN model_version_info.model_id IS 'model id';
+COMMENT ON COLUMN model_version_info.version IS 'model version';
+COMMENT ON COLUMN model_version_info.model_version_comment IS 'model version
comment';
+COMMENT ON COLUMN model_version_info.model_version_properties IS 'model
version properties';
+COMMENT ON COLUMN model_version_info.model_version_uri IS 'model storage uri';
+COMMENT ON COLUMN model_version_info.audit_info IS 'model version audit info';
+COMMENT ON COLUMN model_version_info.deleted_at IS 'model version deleted at';
+
+
+CREATE TABLE IF NOT EXISTS model_version_alias_rel (
+ id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ model_id BIGINT NOT NULL,
+ model_version INT NOT NULL,
+ model_version_alias VARCHAR(128) NOT NULL,
+ deleted_at BIGINT NOT NULL DEFAULT 0,
+ PRIMARY KEY (id),
+ UNIQUE (model_id, model_version, model_version_alias, deleted_at)
+ );
+
+CREATE INDEX IF NOT EXISTS idx_model_version_alias on model_version_alias_rel
(model_version_alias);
+COMMENT ON TABLE model_version_alias_rel IS 'model version alias relation';
+
+COMMENT ON COLUMN model_version_alias_rel.id IS 'auto increment id';
+COMMENT ON COLUMN model_version_alias_rel.model_id IS 'model id';
+COMMENT ON COLUMN model_version_alias_rel.model_version IS 'model version';
+COMMENT ON COLUMN model_version_alias_rel.model_version_alias IS 'model
version alias';
+COMMENT ON COLUMN model_version_alias_rel.deleted_at IS 'model version alias
deleted at';