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

diqiu50 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 9d8111643d [#14138] refactor(core): Split Semantic Model definition 
storage (#14139)
9d8111643d is described below

commit 9d8111643d0084d1f2f87c45ced50e76d399e874
Author: mchades <[email protected]>
AuthorDate: Tue Oct 27 11:24:52 2026 +0800

    [#14138] refactor(core): Split Semantic Model definition storage (#14139)
    
    ### What changes were proposed in this pull request?
    
    Store the five definition fields in separate JSON columns within each
    Semantic Model version snapshot. Update serialization, reconstruction,
    SQL mappings, overwrite handling, and the H2/MySQL/PostgreSQL schema and
    upgrade scripts.
    
    ### Why are the changes needed?
    
    Prepare schema-level metric discovery to read metrics without loading
    unrelated definition fields. This implements the first storage step of
    #14038, under #14036.
    
    Fixes #14138.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No public API changes.
    
    ### How was this patch tested?
    
    113 tests passed: 11 PO unit tests and 34 tests on each of H2, MySQL,
    and PostgreSQL, covering definition round-trips, absent/empty fields,
    malformed JSON, snapshots, overwrite, rollback, lifecycle behavior, and
    SQL upgrades.
    
    Ran coreUnitTest and the three core database test tasks with focused
    Semantic Model and TestSQLScripts filters, using -PskipITs
    -PskipDockerTests=false. Spotless and git diff --check passed.
---
 .../relational/mapper/SemanticModelMetaMapper.java |   9 +-
 .../base/SemanticModelMetaBaseSQLProvider.java     |   9 +-
 .../SemanticModelVersionInfoBaseSQLProvider.java   |  10 +-
 .../storage/relational/po/SemanticModelPO.java     |  56 +++++-
 .../relational/po/SemanticModelVersionInfoPO.java  |  22 ++-
 .../service/SemanticModelMetaService.java          |   6 +-
 .../relational/TestSemanticModelJDBCBackend.java   | 147 +++++++++++++++-
 .../storage/relational/po/TestSemanticModelPO.java | 191 +++++++++++++++++++++
 .../service/TestSemanticModelMetaService.java      |  14 +-
 design-docs/gravitino-semantic-model-design.md     |  21 ++-
 scripts/h2/schema-2.0.0-h2.sql                     |   6 +-
 scripts/h2/upgrade-1.3.0-to-2.0.0-h2.sql           |   6 +-
 scripts/mysql/schema-2.0.0-mysql.sql               |   6 +-
 scripts/mysql/upgrade-1.3.0-to-2.0.0-mysql.sql     |   6 +-
 scripts/postgresql/schema-2.0.0-postgresql.sql     |  12 +-
 .../upgrade-1.3.0-to-2.0.0-postgresql.sql          |  12 +-
 16 files changed, 491 insertions(+), 42 deletions(-)

diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/SemanticModelMetaMapper.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/SemanticModelMetaMapper.java
index cb393b4b06..dc59fb0c1c 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/SemanticModelMetaMapper.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/SemanticModelMetaMapper.java
@@ -53,7 +53,11 @@ public interface SemanticModelMetaMapper {
         @Result(property = "version", column = "version"),
         @Result(property = "semanticModelName", column = 
"version_semantic_model_name"),
         @Result(property = "semanticModelComment", column = 
"semantic_model_comment"),
-        @Result(property = "semanticModelDefinition", column = 
"semantic_model_definition"),
+        @Result(property = "datasets", column = "datasets"),
+        @Result(property = "relationships", column = "relationships"),
+        @Result(property = "metrics", column = "metrics"),
+        @Result(property = "aiContext", column = "ai_context"),
+        @Result(property = "customExtensions", column = "custom_extensions"),
         @Result(property = "properties", column = "properties"),
         @Result(property = "auditInfo", column = "version_audit_info"),
         @Result(property = "deletedAt", column = "version_deleted_at")
@@ -80,7 +84,8 @@ public interface SemanticModelMetaMapper {
             column =
                 "{id,version_metalake_id,version_catalog_id,version_schema_id,"
                     + 
"version_semantic_model_id,version,version_semantic_model_name,"
-                    + 
"semantic_model_comment,semantic_model_definition,properties,"
+                    + 
"semantic_model_comment,datasets,relationships,metrics,ai_context,"
+                    + "custom_extensions,properties,"
                     + "version_audit_info,version_deleted_at}",
             one = @One(resultMap = "mapToSemanticModelVersionInfoPO"))
       })
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/SemanticModelMetaBaseSQLProvider.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/SemanticModelMetaBaseSQLProvider.java
index 66b57f8e59..0f114f3900 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/SemanticModelMetaBaseSQLProvider.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/SemanticModelMetaBaseSQLProvider.java
@@ -40,7 +40,8 @@ public class SemanticModelMetaBaseSQLProvider {
           + " smvi.schema_id as version_schema_id,"
           + " smvi.semantic_model_id as version_semantic_model_id, 
smvi.version,"
           + " smvi.semantic_model_name as version_semantic_model_name,"
-          + " smvi.semantic_model_comment, smvi.semantic_model_definition, 
smvi.properties,"
+          + " smvi.semantic_model_comment, smvi.datasets, smvi.relationships, 
smvi.metrics,"
+          + " smvi.ai_context, smvi.custom_extensions, smvi.properties,"
           + " smvi.audit_info as version_audit_info,"
           + " smvi.deleted_at as version_deleted_at";
 
@@ -238,7 +239,11 @@ public class SemanticModelMetaBaseSQLProvider {
             smvi.version,
             smvi.semantic_model_name as version_semantic_model_name,
             smvi.semantic_model_comment,
-            smvi.semantic_model_definition,
+            smvi.datasets,
+            smvi.relationships,
+            smvi.metrics,
+            smvi.ai_context,
+            smvi.custom_extensions,
             smvi.properties,
             smvi.audit_info as version_audit_info,
             smvi.deleted_at as version_deleted_at
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/SemanticModelVersionInfoBaseSQLProvider.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/SemanticModelVersionInfoBaseSQLProvider.java
index c83286691b..d4502eb3f2 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/SemanticModelVersionInfoBaseSQLProvider.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/SemanticModelVersionInfoBaseSQLProvider.java
@@ -32,14 +32,17 @@ public class SemanticModelVersionInfoBaseSQLProvider {
     return "INSERT INTO "
         + SemanticModelVersionInfoMapper.TABLE_NAME
         + " (metalake_id, catalog_id, schema_id, semantic_model_id, version,"
-        + " semantic_model_name, semantic_model_comment, 
semantic_model_definition,"
+        + " semantic_model_name, semantic_model_comment, datasets, 
relationships, metrics,"
+        + " ai_context, custom_extensions,"
         + " properties, audit_info, deleted_at)"
         + " VALUES (#{semanticModelVersionInfo.metalakeId},"
         + " #{semanticModelVersionInfo.catalogId}, 
#{semanticModelVersionInfo.schemaId},"
         + " #{semanticModelVersionInfo.semanticModelId}, 
#{semanticModelVersionInfo.version},"
         + " #{semanticModelVersionInfo.semanticModelName},"
         + " #{semanticModelVersionInfo.semanticModelComment},"
-        + " #{semanticModelVersionInfo.semanticModelDefinition},"
+        + " #{semanticModelVersionInfo.datasets}, 
#{semanticModelVersionInfo.relationships},"
+        + " #{semanticModelVersionInfo.metrics}, 
#{semanticModelVersionInfo.aiContext},"
+        + " #{semanticModelVersionInfo.customExtensions},"
         + " #{semanticModelVersionInfo.properties}, 
#{semanticModelVersionInfo.auditInfo},"
         + " #{semanticModelVersionInfo.deletedAt})";
   }
@@ -51,7 +54,8 @@ public class SemanticModelVersionInfoBaseSQLProvider {
         + " schema_id as schemaId, semantic_model_id as semanticModelId, 
version as version,"
         + " semantic_model_name as semanticModelName,"
         + " semantic_model_comment as semanticModelComment,"
-        + " semantic_model_definition as semanticModelDefinition, properties 
as properties,"
+        + " datasets as datasets, relationships as relationships, metrics as 
metrics,"
+        + " ai_context as aiContext, custom_extensions as customExtensions, 
properties as properties,"
         + " audit_info as auditInfo, deleted_at as deletedAt FROM "
         + SemanticModelVersionInfoMapper.TABLE_NAME
         + " WHERE semantic_model_id = #{semanticModelId}"
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/po/SemanticModelPO.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/po/SemanticModelPO.java
index 34e95faaab..c50c6ab4d6 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/po/SemanticModelPO.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/po/SemanticModelPO.java
@@ -24,12 +24,18 @@ import com.fasterxml.jackson.core.JsonProcessingException;
 import com.google.common.base.Preconditions;
 import java.util.Collections;
 import java.util.Map;
+import javax.annotation.Nullable;
 import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.ToString;
 import org.apache.gravitino.Entity;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
+import org.apache.gravitino.dto.semantic.AIContextDTO;
+import org.apache.gravitino.dto.semantic.CustomExtensionDTO;
+import org.apache.gravitino.dto.semantic.DatasetDTO;
+import org.apache.gravitino.dto.semantic.MetricDTO;
+import org.apache.gravitino.dto.semantic.RelationshipDTO;
 import org.apache.gravitino.dto.semantic.SemanticModelDefinitionDTO;
 import org.apache.gravitino.json.JsonUtils;
 import org.apache.gravitino.meta.AuditInfo;
@@ -111,8 +117,21 @@ public class SemanticModelPO {
     try {
       SemanticModelVersionInfoPO versionPO = 
semanticModelPO.getSemanticModelVersionInfoPO();
       SemanticModelDefinitionDTO definitionDTO =
-          JsonUtils.anyFieldMapper()
-              .readValue(versionPO.semanticModelDefinition(), 
SemanticModelDefinitionDTO.class);
+          SemanticModelDefinitionDTO.builder()
+              .withDatasets(deserializeField(versionPO.datasets(), 
DatasetDTO[].class, "datasets"))
+              .withRelationships(
+                  deserializeOptionalField(
+                      versionPO.relationships(), RelationshipDTO[].class, 
"relationships"))
+              .withMetrics(
+                  deserializeOptionalField(versionPO.metrics(), 
MetricDTO[].class, "metrics"))
+              .withAiContext(
+                  deserializeOptionalField(versionPO.aiContext(), 
AIContextDTO.class, "ai_context"))
+              .withCustomExtensions(
+                  deserializeOptionalField(
+                      versionPO.customExtensions(),
+                      CustomExtensionDTO[].class,
+                      "custom_extensions"))
+              .build();
       Map<String, String> properties =
           versionPO.properties() == null
               ? Collections.emptyMap()
@@ -163,10 +182,8 @@ public class SemanticModelPO {
       NamespacedEntityId namespacedEntityId,
       Integer version) {
     try {
-      String definitionJson =
-          JsonUtils.anyFieldMapper()
-              .writeValueAsString(
-                  
SemanticModelDefinitionDTO.fromDefinition(semanticModelEntity.definition()));
+      SemanticModelDefinitionDTO definitionDTO =
+          
SemanticModelDefinitionDTO.fromDefinition(semanticModelEntity.definition());
       String propertiesJson =
           semanticModelEntity.properties().isEmpty()
               ? null
@@ -180,7 +197,11 @@ public class SemanticModelPO {
           .withVersion(version)
           .withSemanticModelName(semanticModelEntity.name())
           .withSemanticModelComment(semanticModelEntity.comment())
-          .withSemanticModelDefinition(definitionJson)
+          
.withDatasets(JsonUtils.anyFieldMapper().writeValueAsString(definitionDTO.getDatasets()))
+          
.withRelationships(serializeOptionalField(definitionDTO.getRelationships()))
+          .withMetrics(serializeOptionalField(definitionDTO.getMetrics()))
+          .withAiContext(serializeOptionalField(definitionDTO.getAiContext()))
+          
.withCustomExtensions(serializeOptionalField(definitionDTO.getCustomExtensions()))
           .withProperties(propertiesJson)
           .withAuditInfo(
               
JsonUtils.anyFieldMapper().writeValueAsString(semanticModelEntity.auditInfo()))
@@ -225,4 +246,25 @@ public class SemanticModelPO {
       throw new RuntimeException("Failed to serialize Semantic Model audit 
info", e);
     }
   }
+
+  @Nullable
+  private static String serializeOptionalField(@Nullable Object value)
+      throws JsonProcessingException {
+    return value == null ? null : 
JsonUtils.anyFieldMapper().writeValueAsString(value);
+  }
+
+  private static <T> T deserializeField(String json, Class<T> type, String 
columnName) {
+    try {
+      return JsonUtils.anyFieldMapper().readValue(json, type);
+    } catch (JsonProcessingException e) {
+      throw new RuntimeException(
+          "Failed to deserialize Semantic Model column '" + columnName + "'", 
e);
+    }
+  }
+
+  @Nullable
+  private static <T> T deserializeOptionalField(
+      @Nullable String json, Class<T> type, String columnName) {
+    return json == null ? null : deserializeField(json, type, columnName);
+  }
 }
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/po/SemanticModelVersionInfoPO.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/po/SemanticModelVersionInfoPO.java
index 03a5491c2a..ba87dde18b 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/po/SemanticModelVersionInfoPO.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/po/SemanticModelVersionInfoPO.java
@@ -19,6 +19,7 @@
 package org.apache.gravitino.storage.relational.po;
 
 import com.google.common.base.Preconditions;
+import javax.annotation.Nullable;
 import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.ToString;
@@ -40,7 +41,11 @@ public class SemanticModelVersionInfoPO {
   private Integer version;
   private String semanticModelName;
   private String semanticModelComment;
-  private String semanticModelDefinition;
+  private String datasets;
+  @Nullable private String relationships;
+  @Nullable private String metrics;
+  @Nullable private String aiContext;
+  @Nullable private String customExtensions;
   private String properties;
   private String auditInfo;
   private Long deletedAt;
@@ -58,7 +63,11 @@ public class SemanticModelVersionInfoPO {
       Integer version,
       String semanticModelName,
       String semanticModelComment,
-      String semanticModelDefinition,
+      String datasets,
+      @Nullable String relationships,
+      @Nullable String metrics,
+      @Nullable String aiContext,
+      @Nullable String customExtensions,
       String properties,
       String auditInfo,
       Long deletedAt) {
@@ -70,8 +79,7 @@ public class SemanticModelVersionInfoPO {
     Preconditions.checkArgument(
         StringUtils.isNotBlank(semanticModelName), "Semantic Model name cannot 
be empty");
     Preconditions.checkArgument(
-        StringUtils.isNotBlank(semanticModelDefinition),
-        "Semantic Model definition cannot be empty");
+        StringUtils.isNotBlank(datasets), "Semantic Model datasets cannot be 
empty");
     Preconditions.checkArgument(StringUtils.isNotBlank(auditInfo), "Audit info 
cannot be empty");
     Preconditions.checkArgument(deletedAt != null, "Deleted at is required");
 
@@ -83,7 +91,11 @@ public class SemanticModelVersionInfoPO {
     this.version = version;
     this.semanticModelName = semanticModelName;
     this.semanticModelComment = semanticModelComment;
-    this.semanticModelDefinition = semanticModelDefinition;
+    this.datasets = datasets;
+    this.relationships = relationships;
+    this.metrics = metrics;
+    this.aiContext = aiContext;
+    this.customExtensions = customExtensions;
     this.properties = properties;
     this.auditInfo = auditInfo;
     this.deletedAt = deletedAt;
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/SemanticModelMetaService.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/SemanticModelMetaService.java
index 244f8b91d2..ce8d32246e 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/SemanticModelMetaService.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/SemanticModelMetaService.java
@@ -443,7 +443,11 @@ public class SemanticModelMetaService {
         .withVersion(nextVersion)
         .withSemanticModelName(source.semanticModelName())
         .withSemanticModelComment(source.semanticModelComment())
-        .withSemanticModelDefinition(source.semanticModelDefinition())
+        .withDatasets(source.datasets())
+        .withRelationships(source.relationships())
+        .withMetrics(source.metrics())
+        .withAiContext(source.aiContext())
+        .withCustomExtensions(source.customExtensions())
         .withProperties(source.properties())
         .withAuditInfo(source.auditInfo())
         .withDeletedAt(source.deletedAt())
diff --git 
a/core/src/test/java/org/apache/gravitino/storage/relational/TestSemanticModelJDBCBackend.java
 
b/core/src/test/java/org/apache/gravitino/storage/relational/TestSemanticModelJDBCBackend.java
index 0ba494234e..c939e0dd50 100644
--- 
a/core/src/test/java/org/apache/gravitino/storage/relational/TestSemanticModelJDBCBackend.java
+++ 
b/core/src/test/java/org/apache/gravitino/storage/relational/TestSemanticModelJDBCBackend.java
@@ -25,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.collect.ImmutableMap;
 import java.io.IOException;
 import java.math.BigDecimal;
@@ -53,8 +54,10 @@ import org.apache.gravitino.authorization.AuthorizationUtils;
 import org.apache.gravitino.authorization.Privilege;
 import org.apache.gravitino.authorization.Privileges;
 import org.apache.gravitino.authorization.SecurableObjects;
+import org.apache.gravitino.dto.semantic.SemanticModelDefinitionDTO;
 import org.apache.gravitino.exceptions.NoSuchEntityException;
 import org.apache.gravitino.exceptions.NonEmptyEntityException;
+import org.apache.gravitino.json.JsonUtils;
 import org.apache.gravitino.meta.NamespacedEntityId;
 import org.apache.gravitino.meta.RoleEntity;
 import org.apache.gravitino.meta.SemanticModelEntity;
@@ -76,6 +79,7 @@ import 
org.apache.gravitino.storage.relational.mapper.EntityChangeLogMapper;
 import org.apache.gravitino.storage.relational.mapper.SemanticModelMetaMapper;
 import 
org.apache.gravitino.storage.relational.mapper.SemanticModelVersionInfoMapper;
 import org.apache.gravitino.storage.relational.po.SemanticModelPO;
+import org.apache.gravitino.storage.relational.po.SemanticModelVersionInfoPO;
 import 
org.apache.gravitino.storage.relational.service.OrphanedMetadataObjectRelationService;
 import org.apache.gravitino.storage.relational.service.OwnerMetaService;
 import org.apache.gravitino.storage.relational.service.POStorageReadRouting;
@@ -198,7 +202,7 @@ public class TestSemanticModelJDBCBackend extends 
TestJDBCBackend {
   }
 
   @TestTemplate
-  public void testCreateAndLoadRoundTrip() throws IOException {
+  public void testCreateAndLoadRoundTrip() throws IOException, SQLException {
     Namespace namespace = createParents("round_trip");
     SemanticModelEntity absent =
         semanticModel(
@@ -235,6 +239,78 @@ public class TestSemanticModelJDBCBackend extends 
TestJDBCBackend {
         
loadedAbsent.definition().aiContext().object().additionalProperties().get("threshold"));
     assertNull(loadedAbsent.definition().aiContext().object().examples());
     assertEquals(0, 
loadedAbsent.definition().aiContext().object().synonyms().length);
+    assertStoredDefinition(absent, 1);
+    assertStoredDefinition(empty, 1);
+  }
+
+  @TestTemplate
+  public void testDefinitionColumnsAcrossReplacementsAndOverwrite()
+      throws IOException, SQLException {
+    Namespace namespace = createParents("definition_columns");
+    SemanticModelEntity base =
+        semanticModel(
+            RandomIdGenerator.INSTANCE.nextId(), namespace, "sales_model", 
false, Map.of());
+    SemanticModelDefinition fullDefinition =
+        JsonUtils.anyFieldMapper()
+            .readValue(
+                """
+                {
+                  "datasets": [
+                    {"name": "orders",
+                     "source": {"namespace": ["sales", "mart"], "name": 
"orders"}},
+                    {"name": "customers",
+                     "source": {"namespace": ["sales", "mart"], "name": 
"customers"}}
+                  ],
+                  "relationships": [
+                    {"name": "orders_customers", "from": "orders", "to": 
"customers",
+                     "fromColumns": ["customer_id"], "toColumns": ["id"]}
+                  ],
+                  "metrics": [
+                    {"name": "z_count", "expression": {
+                      "dialects": [{"dialect": "ANSI_SQL", "expression": 
"COUNT(*)"}]}},
+                    {"name": "a_revenue", "expression": {
+                      "dialects": [{"dialect": "ANSI_SQL", "expression": 
"SUM(orders.amount)"}]}}
+                  ],
+                  "aiContext": "Use certified definitions",
+                  "customExtensions": [
+                    {"vendorName": "z_vendor", "data": "first"},
+                    {"vendorName": "a_vendor", "data": "second"}
+                  ]
+                }
+                """,
+                SemanticModelDefinitionDTO.class)
+            .toDefinition();
+    SemanticModelEntity original = withDefinition(base, fullDefinition);
+    SemanticModelEntity empty =
+        withDefinition(
+            base,
+            SemanticModelDefinition.builder()
+                .withDatasets(fullDefinition.datasets())
+                .withAIContext(AIContext.of(""))
+                .withRelationships(new Relationship[0])
+                .withMetrics(new Metric[0])
+                .withCustomExtensions(new CustomExtension[0])
+                .build());
+    SemanticModelEntity absent =
+        withDefinition(
+            base,
+            
SemanticModelDefinition.builder().withDatasets(base.definition().datasets()).build());
+
+    backend.insert(original, false);
+    assertEquals(original, backend.get(original.nameIdentifier(), 
original.type()));
+    backend.update(original.nameIdentifier(), original.type(), ignored -> 
empty);
+    assertEquals(empty, backend.get(original.nameIdentifier(), 
original.type()));
+    backend.update(original.nameIdentifier(), original.type(), ignored -> 
absent);
+    assertEquals(absent, backend.get(original.nameIdentifier(), 
original.type()));
+    backend.insert(original, true);
+    assertEquals(original, backend.get(original.nameIdentifier(), 
original.type()));
+    assertEquals(4, getSemanticModelPO(original.id()).getCurrentVersion());
+    assertEquals(List.of(1, 2, 3, 4), activeSnapshotVersions(original.id()));
+
+    assertStoredDefinition(original, 1);
+    assertStoredDefinition(empty, 2);
+    assertStoredDefinition(absent, 3);
+    assertStoredDefinition(original, 4);
   }
 
   @TestTemplate
@@ -727,6 +803,75 @@ public class TestSemanticModelJDBCBackend extends 
TestJDBCBackend {
         .build();
   }
 
+  private static SemanticModelEntity withDefinition(
+      SemanticModelEntity model, SemanticModelDefinition definition) {
+    return SemanticModelEntity.builder()
+        .withId(model.id())
+        .withName(model.name())
+        .withNamespace(model.namespace())
+        .withComment(model.comment())
+        .withDefinition(definition)
+        .withProperties(model.properties())
+        .withAuditInfo(model.auditInfo())
+        .build();
+  }
+
+  private void assertStoredDefinition(SemanticModelEntity model, int version)
+      throws IOException, SQLException {
+    JsonNode expected =
+        JsonUtils.anyFieldMapper()
+            .readTree(
+                JsonUtils.anyFieldMapper()
+                    .writeValueAsString(
+                        
SemanticModelDefinitionDTO.fromDefinition(model.definition())));
+    SemanticModelVersionInfoPO snapshot =
+        SessionUtils.getWithoutCommit(
+            SemanticModelVersionInfoMapper.class,
+            mapper ->
+                
mapper.selectSemanticModelVersionInfoBySemanticModelIdAndVersion(
+                    model.id(), version));
+    assertNotNull(snapshot);
+    Map<String, String> columns =
+        Map.of(
+            "datasets", "datasets",
+            "relationships", "relationships",
+            "metrics", "metrics",
+            "ai_context", "aiContext",
+            "custom_extensions", "customExtensions");
+    try (SqlSession session =
+            
SqlSessionFactoryHelper.getInstance().getSqlSessionFactory().openSession(true);
+        Connection connection = session.getConnection();
+        PreparedStatement statement =
+            connection.prepareStatement(
+                "SELECT datasets, relationships, metrics, ai_context, 
custom_extensions"
+                    + " FROM semantic_model_version_info"
+                    + " WHERE semantic_model_id = ? AND version = ? AND 
deleted_at = 0")) {
+      statement.setLong(1, model.id());
+      statement.setInt(2, version);
+      try (ResultSet result = statement.executeQuery()) {
+        assertTrue(result.next());
+        for (Map.Entry<String, String> column : columns.entrySet()) {
+          String json = result.getString(column.getKey());
+          if (expected.has(column.getValue())) {
+            assertNotNull(json, column.getKey());
+            assertEquals(
+                expected.get(column.getValue()),
+                JsonUtils.anyFieldMapper().readTree(json),
+                column.getKey());
+          } else {
+            assertNull(json, column.getKey());
+          }
+        }
+        assertEquals(result.getString("datasets"), snapshot.datasets());
+        assertEquals(result.getString("relationships"), 
snapshot.relationships());
+        assertEquals(result.getString("metrics"), snapshot.metrics());
+        assertEquals(result.getString("ai_context"), snapshot.aiContext());
+        assertEquals(result.getString("custom_extensions"), 
snapshot.customExtensions());
+        assertFalse(result.next());
+      }
+    }
+  }
+
   private int countRows(String tableName, Long semanticModelId) {
     if (!SemanticModelMetaMapper.TABLE_NAME.equals(tableName)
         && !SemanticModelVersionInfoMapper.TABLE_NAME.equals(tableName)) {
diff --git 
a/core/src/test/java/org/apache/gravitino/storage/relational/po/TestSemanticModelPO.java
 
b/core/src/test/java/org/apache/gravitino/storage/relational/po/TestSemanticModelPO.java
new file mode 100644
index 0000000000..3bb806ce0d
--- /dev/null
+++ 
b/core/src/test/java/org/apache/gravitino/storage/relational/po/TestSemanticModelPO.java
@@ -0,0 +1,191 @@
+/*
+ * 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 static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.time.Instant;
+import java.util.Map;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.dto.semantic.SemanticModelDefinitionDTO;
+import org.apache.gravitino.json.JsonUtils;
+import org.apache.gravitino.meta.AuditInfo;
+import org.apache.gravitino.meta.NamespacedEntityId;
+import org.apache.gravitino.meta.SemanticModelEntity;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+class TestSemanticModelPO {
+
+  private static final String DEFINITION =
+      """
+      {
+        "datasets": [
+          {"name": "orders", "source": {"namespace": ["sales", "mart"], 
"name": "orders"},
+           "primaryKey": [], "aiContext": "Order facts"},
+          {"name": "customers", "source": {"namespace": ["sales", "mart"], 
"name": "customers"}}
+        ],
+        "relationships": [
+          {"name": "orders_customers", "from": "orders", "to": "customers",
+           "fromColumns": ["customer_id", "region"], "toColumns": ["id", 
"region"]}
+        ],
+        "metrics": [
+          {"name": "z_count", "expression": {
+            "dialects": [{"dialect": "ANSI_SQL", "expression": "COUNT(*)"}]},
+           "aiContext": "Number of orders", "customExtensions": []},
+          {"name": "a_revenue", "expression": {
+            "dialects": [{"dialect": "ANSI_SQL", "expression": 
"SUM(orders.amount)"}]},
+           "datatype": "Decimal"}
+        ],
+        "aiContext": {
+          "instructions": "Use certified definitions", "synonyms": [], 
"examples": [],
+          "threshold": 0.123456789012345678901234567890,
+          "nested": {"flags": [true, null], "count": 
123456789012345678901234567890}
+        },
+        "customExtensions": [
+          {"vendorName": "z_vendor", "data": "first"},
+          {"vendorName": "a_vendor", "data": "second"}
+        ]
+      }
+      """;
+
+  @Test
+  void testDefinitionFieldsRoundTrip() throws JsonProcessingException {
+    SemanticModelEntity entity = entity(DEFINITION);
+    SemanticModelVersionInfoPO snapshot = snapshot(entity);
+    JsonNode expected = JsonUtils.anyFieldMapper().readTree(DEFINITION);
+
+    assertEquals(
+        expected.get("datasets"), 
JsonUtils.anyFieldMapper().readTree(snapshot.datasets()));
+    assertEquals(
+        expected.get("relationships"),
+        JsonUtils.anyFieldMapper().readTree(snapshot.relationships()));
+    assertEquals(expected.get("metrics"), 
JsonUtils.anyFieldMapper().readTree(snapshot.metrics()));
+    assertEquals(
+        expected.get("aiContext"), 
JsonUtils.anyFieldMapper().readTree(snapshot.aiContext()));
+    assertEquals(
+        expected.get("customExtensions"),
+        JsonUtils.anyFieldMapper().readTree(snapshot.customExtensions()));
+    assertEquals(
+        entity, SemanticModelPO.fromSemanticModelPO(identity(snapshot), 
entity.namespace()));
+  }
+
+  @ParameterizedTest
+  @ValueSource(booleans = {false, true})
+  void testAbsentAndEmptyOptionalFields(boolean explicitEmpty) throws 
JsonProcessingException {
+    ObjectNode definition = (ObjectNode) 
JsonUtils.anyFieldMapper().readTree(DEFINITION);
+    definition.remove("aiContext");
+    for (String field : new String[] {"relationships", "metrics", 
"customExtensions"}) {
+      definition.remove(field);
+      if (explicitEmpty) {
+        definition.putArray(field);
+      }
+    }
+    SemanticModelEntity entity = entity(definition.toString());
+    SemanticModelVersionInfoPO snapshot = snapshot(entity);
+
+    assertNull(snapshot.aiContext());
+    assertEquals(explicitEmpty ? "[]" : null, snapshot.relationships());
+    assertEquals(explicitEmpty ? "[]" : null, snapshot.metrics());
+    assertEquals(explicitEmpty ? "[]" : null, snapshot.customExtensions());
+    assertEquals(
+        entity, SemanticModelPO.fromSemanticModelPO(identity(snapshot), 
entity.namespace()));
+  }
+
+  @ParameterizedTest
+  @ValueSource(strings = {"\"Use certified definitions\"", "\"\"", "{}"})
+  void testAIContextRepresentations(String context) throws 
JsonProcessingException {
+    ObjectNode definition = (ObjectNode) 
JsonUtils.anyFieldMapper().readTree(DEFINITION);
+    definition.set("aiContext", JsonUtils.anyFieldMapper().readTree(context));
+    SemanticModelEntity entity = entity(definition.toString());
+    SemanticModelVersionInfoPO snapshot = snapshot(entity);
+
+    assertEquals(context, snapshot.aiContext());
+    assertEquals(
+        entity, SemanticModelPO.fromSemanticModelPO(identity(snapshot), 
entity.namespace()));
+  }
+
+  @ParameterizedTest
+  @CsvSource({
+    "datasets, datasets",
+    "relationships, relationships",
+    "metrics, metrics",
+    "aiContext, ai_context",
+    "customExtensions, custom_extensions"
+  })
+  void testCorruptedDefinitionColumnFailsModelLoad(String field, String 
columnName)
+      throws Exception {
+    SemanticModelEntity entity = entity(DEFINITION);
+    SemanticModelVersionInfoPO snapshot = snapshot(entity);
+    FieldUtils.writeField(snapshot, field, "{", true);
+
+    RuntimeException failure =
+        assertThrows(
+            RuntimeException.class,
+            () -> SemanticModelPO.fromSemanticModelPO(identity(snapshot), 
entity.namespace()));
+    assertEquals(
+        "Failed to deserialize Semantic Model column '" + columnName + "'", 
failure.getMessage());
+    assertInstanceOf(JsonProcessingException.class, failure.getCause());
+  }
+
+  private static SemanticModelEntity entity(String definition) throws 
JsonProcessingException {
+    return SemanticModelEntity.builder()
+        .withId(4L)
+        .withName("sales_model")
+        .withNamespace(Namespace.of("metalake", "catalog", "schema"))
+        .withComment("Sales definitions")
+        .withDefinition(
+            JsonUtils.anyFieldMapper()
+                .readValue(definition, SemanticModelDefinitionDTO.class)
+                .toDefinition())
+        .withProperties(Map.of("owner", "analytics"))
+        .withAuditInfo(
+            
AuditInfo.builder().withCreator("user").withCreateTime(Instant.EPOCH).build())
+        .build();
+  }
+
+  private static SemanticModelVersionInfoPO snapshot(SemanticModelEntity 
entity) {
+    return SemanticModelPO.initializeSemanticModelVersionInfoPO(
+        entity, new NamespacedEntityId(3L, 1L, 2L), 1);
+  }
+
+  private static SemanticModelPO identity(SemanticModelVersionInfoPO snapshot) 
{
+    return SemanticModelPO.builder()
+        .withSemanticModelId(snapshot.semanticModelId())
+        .withSemanticModelName(snapshot.semanticModelName())
+        .withMetalakeId(snapshot.metalakeId())
+        .withCatalogId(snapshot.catalogId())
+        .withSchemaId(snapshot.schemaId())
+        .withAuditInfo(snapshot.auditInfo())
+        .withCurrentVersion(snapshot.version())
+        .withLastVersion(snapshot.version())
+        .withDeletedAt(snapshot.deletedAt())
+        .withSemanticModelVersionInfoPO(snapshot)
+        .build();
+  }
+}
diff --git 
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestSemanticModelMetaService.java
 
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestSemanticModelMetaService.java
index 5bc2f6888c..d89969d301 100644
--- 
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestSemanticModelMetaService.java
+++ 
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestSemanticModelMetaService.java
@@ -183,10 +183,10 @@ public class TestSemanticModelMetaService extends 
TestJDBCBackend {
     assertEquals(2, versions.size());
     assertEquals(oldName, versions.get(1).name);
     assertEquals("v1 comment", versions.get(1).comment);
-    assertTrue(versions.get(1).definition.contains("orders_v1"));
+    assertTrue(versions.get(1).datasets.contains("orders_v1"));
     assertEquals(newName, versions.get(2).name);
     assertEquals("v2 comment", versions.get(2).comment);
-    assertTrue(versions.get(2).definition.contains("orders_v2"));
+    assertTrue(versions.get(2).datasets.contains("orders_v2"));
     assertEquals(0L, versions.get(1).deletedAt);
     assertEquals(0L, versions.get(2).deletedAt);
 
@@ -594,7 +594,7 @@ public class TestSemanticModelMetaService extends 
TestJDBCBackend {
             statement.executeQuery(
                 String.format(
                     "SELECT version, semantic_model_name, 
semantic_model_comment,"
-                        + " semantic_model_definition, deleted_at"
+                        + " datasets, deleted_at"
                         + " FROM semantic_model_version_info WHERE 
semantic_model_id = %d",
                     semanticModelId))) {
       while (resultSet.next()) {
@@ -603,7 +603,7 @@ public class TestSemanticModelMetaService extends 
TestJDBCBackend {
             new VersionState(
                 resultSet.getString("semantic_model_name"),
                 resultSet.getString("semantic_model_comment"),
-                resultSet.getString("semantic_model_definition"),
+                resultSet.getString("datasets"),
                 resultSet.getLong("deleted_at")));
       }
       return versions;
@@ -674,13 +674,13 @@ public class TestSemanticModelMetaService extends 
TestJDBCBackend {
   private static class VersionState {
     private final String name;
     private final String comment;
-    private final String definition;
+    private final String datasets;
     private final long deletedAt;
 
-    private VersionState(String name, String comment, String definition, long 
deletedAt) {
+    private VersionState(String name, String comment, String datasets, long 
deletedAt) {
       this.name = name;
       this.comment = comment;
-      this.definition = definition;
+      this.datasets = datasets;
       this.deletedAt = deletedAt;
     }
   }
diff --git a/design-docs/gravitino-semantic-model-design.md 
b/design-docs/gravitino-semantic-model-design.md
index af2319d165..117c2a62d5 100644
--- a/design-docs/gravitino-semantic-model-design.md
+++ b/design-docs/gravitino-semantic-model-design.md
@@ -585,7 +585,11 @@ CREATE TABLE IF NOT EXISTS `semantic_model_version_info` (
     `version` INT UNSIGNED NOT NULL COMMENT 'semantic model version',
     `semantic_model_name` VARCHAR(128) NOT NULL COMMENT 'semantic model name 
snapshot',
     `semantic_model_comment` TEXT DEFAULT NULL COMMENT 'semantic model comment 
snapshot',
-    `semantic_model_definition` MEDIUMTEXT NOT NULL COMMENT 'structured 
definition snapshot (JSON)',
+    `datasets` MEDIUMTEXT NOT NULL COMMENT 'dataset definitions snapshot 
(JSON)',
+    `relationships` MEDIUMTEXT DEFAULT NULL COMMENT 'relationship definitions 
snapshot (JSON)',
+    `metrics` MEDIUMTEXT DEFAULT NULL COMMENT 'metric definitions snapshot 
(JSON)',
+    `ai_context` MEDIUMTEXT DEFAULT NULL COMMENT 'model AI context snapshot 
(JSON)',
+    `custom_extensions` MEDIUMTEXT DEFAULT NULL COMMENT 'model custom 
extensions snapshot (JSON)',
     `properties` MEDIUMTEXT DEFAULT NULL COMMENT 'semantic model properties 
snapshot (JSON)',
     `audit_info` MEDIUMTEXT NOT NULL COMMENT 'semantic model version audit 
info',
     `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'version 
deleted at',
@@ -598,11 +602,16 @@ CREATE TABLE IF NOT EXISTS `semantic_model_version_info` (
   COMMENT 'semantic model version information';
 ```
 
-`semantic_model_definition` contains AI context, datasets, relationships, 
metrics, and custom
-extensions as structured JSON managed by typed converters. Keeping the 
definition as one snapshot
-preserves collection order and allows create and alter to validate and persist 
the complete model
-atomically. Child members are not stored in separate tables because they do 
not have independent
-identity, lifecycle, or authorization.
+Each version row stores the complete definition in five JSON columns: 
`datasets`, `relationships`,
+`metrics`, `ai_context`, and `custom_extensions`. Typed converters preserve 
the existing field
+representations and collection order, including string-or-object AI context. 
Absent optional fields
+use SQL `NULL`, while explicit empty collections use JSON `[]`. Create and 
alter still validate and
+persist all definition fields atomically in one snapshot. Child members are 
not stored in separate
+tables because they do not have independent identity, lifecycle, or 
authorization.
+
+These Semantic Model tables are introduced in 2.0.0. Before that release, 
storage changes update the
+2.0.0 schema and 1.3.0-to-2.0.0 upgrade scripts in place for all supported 
databases. Released versions
+have no Semantic Model data to migrate.
 
 #### Version and Lifecycle Behavior
 
diff --git a/scripts/h2/schema-2.0.0-h2.sql b/scripts/h2/schema-2.0.0-h2.sql
index 9343462fe6..2c41c3e1e6 100644
--- a/scripts/h2/schema-2.0.0-h2.sql
+++ b/scripts/h2/schema-2.0.0-h2.sql
@@ -597,7 +597,11 @@ CREATE TABLE IF NOT EXISTS `semantic_model_version_info` (
     `version` INT UNSIGNED NOT NULL COMMENT 'semantic model version',
     `semantic_model_name` VARCHAR(128) NOT NULL COMMENT 'semantic model name 
snapshot',
     `semantic_model_comment` CLOB DEFAULT NULL COMMENT 'semantic model comment 
snapshot',
-    `semantic_model_definition` CLOB NOT NULL COMMENT 'structured definition 
snapshot (JSON)',
+    `datasets` CLOB NOT NULL COMMENT 'dataset definitions snapshot (JSON)',
+    `relationships` CLOB DEFAULT NULL COMMENT 'relationship definitions 
snapshot (JSON)',
+    `metrics` CLOB DEFAULT NULL COMMENT 'metric definitions snapshot (JSON)',
+    `ai_context` CLOB DEFAULT NULL COMMENT 'model AI context snapshot (JSON)',
+    `custom_extensions` CLOB DEFAULT NULL COMMENT 'model custom extensions 
snapshot (JSON)',
     `properties` CLOB DEFAULT NULL COMMENT 'semantic model properties snapshot 
(JSON)',
     `audit_info` CLOB NOT NULL COMMENT 'semantic model version audit info',
     `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'version 
deleted at',
diff --git a/scripts/h2/upgrade-1.3.0-to-2.0.0-h2.sql 
b/scripts/h2/upgrade-1.3.0-to-2.0.0-h2.sql
index 6d84961017..c979b98762 100644
--- a/scripts/h2/upgrade-1.3.0-to-2.0.0-h2.sql
+++ b/scripts/h2/upgrade-1.3.0-to-2.0.0-h2.sql
@@ -91,7 +91,11 @@ CREATE TABLE IF NOT EXISTS `semantic_model_version_info` (
     `version` INT UNSIGNED NOT NULL COMMENT 'semantic model version',
     `semantic_model_name` VARCHAR(128) NOT NULL COMMENT 'semantic model name 
snapshot',
     `semantic_model_comment` CLOB DEFAULT NULL COMMENT 'semantic model comment 
snapshot',
-    `semantic_model_definition` CLOB NOT NULL COMMENT 'structured definition 
snapshot (JSON)',
+    `datasets` CLOB NOT NULL COMMENT 'dataset definitions snapshot (JSON)',
+    `relationships` CLOB DEFAULT NULL COMMENT 'relationship definitions 
snapshot (JSON)',
+    `metrics` CLOB DEFAULT NULL COMMENT 'metric definitions snapshot (JSON)',
+    `ai_context` CLOB DEFAULT NULL COMMENT 'model AI context snapshot (JSON)',
+    `custom_extensions` CLOB DEFAULT NULL COMMENT 'model custom extensions 
snapshot (JSON)',
     `properties` CLOB DEFAULT NULL COMMENT 'semantic model properties snapshot 
(JSON)',
     `audit_info` CLOB NOT NULL COMMENT 'semantic model version audit info',
     `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'version 
deleted at',
diff --git a/scripts/mysql/schema-2.0.0-mysql.sql 
b/scripts/mysql/schema-2.0.0-mysql.sql
index d8110fa483..6790855212 100644
--- a/scripts/mysql/schema-2.0.0-mysql.sql
+++ b/scripts/mysql/schema-2.0.0-mysql.sql
@@ -588,7 +588,11 @@ CREATE TABLE IF NOT EXISTS `semantic_model_version_info` (
     `version` INT UNSIGNED NOT NULL COMMENT 'semantic model version',
     `semantic_model_name` VARCHAR(128) NOT NULL COMMENT 'semantic model name 
snapshot',
     `semantic_model_comment` TEXT DEFAULT NULL COMMENT 'semantic model comment 
snapshot',
-    `semantic_model_definition` MEDIUMTEXT NOT NULL COMMENT 'structured 
definition snapshot (JSON)',
+    `datasets` MEDIUMTEXT NOT NULL COMMENT 'dataset definitions snapshot 
(JSON)',
+    `relationships` MEDIUMTEXT DEFAULT NULL COMMENT 'relationship definitions 
snapshot (JSON)',
+    `metrics` MEDIUMTEXT DEFAULT NULL COMMENT 'metric definitions snapshot 
(JSON)',
+    `ai_context` MEDIUMTEXT DEFAULT NULL COMMENT 'model AI context snapshot 
(JSON)',
+    `custom_extensions` MEDIUMTEXT DEFAULT NULL COMMENT 'model custom 
extensions snapshot (JSON)',
     `properties` MEDIUMTEXT DEFAULT NULL COMMENT 'semantic model properties 
snapshot (JSON)',
     `audit_info` MEDIUMTEXT NOT NULL COMMENT 'semantic model version audit 
info',
     `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'version 
deleted at',
diff --git a/scripts/mysql/upgrade-1.3.0-to-2.0.0-mysql.sql 
b/scripts/mysql/upgrade-1.3.0-to-2.0.0-mysql.sql
index 07c35e345c..8f1cc5e977 100644
--- a/scripts/mysql/upgrade-1.3.0-to-2.0.0-mysql.sql
+++ b/scripts/mysql/upgrade-1.3.0-to-2.0.0-mysql.sql
@@ -538,7 +538,11 @@ CREATE TABLE IF NOT EXISTS `semantic_model_version_info` (
     `version` INT UNSIGNED NOT NULL COMMENT 'semantic model version',
     `semantic_model_name` VARCHAR(128) NOT NULL COMMENT 'semantic model name 
snapshot',
     `semantic_model_comment` TEXT DEFAULT NULL COMMENT 'semantic model comment 
snapshot',
-    `semantic_model_definition` MEDIUMTEXT NOT NULL COMMENT 'structured 
definition snapshot (JSON)',
+    `datasets` MEDIUMTEXT NOT NULL COMMENT 'dataset definitions snapshot 
(JSON)',
+    `relationships` MEDIUMTEXT DEFAULT NULL COMMENT 'relationship definitions 
snapshot (JSON)',
+    `metrics` MEDIUMTEXT DEFAULT NULL COMMENT 'metric definitions snapshot 
(JSON)',
+    `ai_context` MEDIUMTEXT DEFAULT NULL COMMENT 'model AI context snapshot 
(JSON)',
+    `custom_extensions` MEDIUMTEXT DEFAULT NULL COMMENT 'model custom 
extensions snapshot (JSON)',
     `properties` MEDIUMTEXT DEFAULT NULL COMMENT 'semantic model properties 
snapshot (JSON)',
     `audit_info` MEDIUMTEXT NOT NULL COMMENT 'semantic model version audit 
info',
     `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'version 
deleted at',
diff --git a/scripts/postgresql/schema-2.0.0-postgresql.sql 
b/scripts/postgresql/schema-2.0.0-postgresql.sql
index 81b2e8cc71..7efb0a60ba 100644
--- a/scripts/postgresql/schema-2.0.0-postgresql.sql
+++ b/scripts/postgresql/schema-2.0.0-postgresql.sql
@@ -1036,7 +1036,11 @@ CREATE TABLE IF NOT EXISTS semantic_model_version_info (
     version INT NOT NULL,
     semantic_model_name VARCHAR(128) NOT NULL,
     semantic_model_comment TEXT DEFAULT NULL,
-    semantic_model_definition TEXT NOT NULL,
+    datasets TEXT NOT NULL,
+    relationships TEXT DEFAULT NULL,
+    metrics TEXT DEFAULT NULL,
+    ai_context TEXT DEFAULT NULL,
+    custom_extensions TEXT DEFAULT NULL,
     properties TEXT DEFAULT NULL,
     audit_info TEXT NOT NULL,
     deleted_at BIGINT NOT NULL DEFAULT 0,
@@ -1057,7 +1061,11 @@ COMMENT ON COLUMN 
semantic_model_version_info.semantic_model_id IS 'semantic mod
 COMMENT ON COLUMN semantic_model_version_info.version IS 'semantic model 
version';
 COMMENT ON COLUMN semantic_model_version_info.semantic_model_name IS 'semantic 
model name snapshot';
 COMMENT ON COLUMN semantic_model_version_info.semantic_model_comment IS 
'semantic model comment snapshot';
-COMMENT ON COLUMN semantic_model_version_info.semantic_model_definition IS 
'structured definition snapshot (JSON)';
+COMMENT ON COLUMN semantic_model_version_info.datasets IS 'dataset definitions 
snapshot (JSON)';
+COMMENT ON COLUMN semantic_model_version_info.relationships IS 'relationship 
definitions snapshot (JSON)';
+COMMENT ON COLUMN semantic_model_version_info.metrics IS 'metric definitions 
snapshot (JSON)';
+COMMENT ON COLUMN semantic_model_version_info.ai_context IS 'model AI context 
snapshot (JSON)';
+COMMENT ON COLUMN semantic_model_version_info.custom_extensions IS 'model 
custom extensions snapshot (JSON)';
 COMMENT ON COLUMN semantic_model_version_info.properties IS 'semantic model 
properties snapshot (JSON)';
 COMMENT ON COLUMN semantic_model_version_info.audit_info IS 'semantic model 
version audit info';
 COMMENT ON COLUMN semantic_model_version_info.deleted_at IS 'version deleted 
at';
diff --git a/scripts/postgresql/upgrade-1.3.0-to-2.0.0-postgresql.sql 
b/scripts/postgresql/upgrade-1.3.0-to-2.0.0-postgresql.sql
index 0cf799dd09..1bb1bd0556 100644
--- a/scripts/postgresql/upgrade-1.3.0-to-2.0.0-postgresql.sql
+++ b/scripts/postgresql/upgrade-1.3.0-to-2.0.0-postgresql.sql
@@ -125,7 +125,11 @@ CREATE TABLE IF NOT EXISTS semantic_model_version_info (
     version INT NOT NULL,
     semantic_model_name VARCHAR(128) NOT NULL,
     semantic_model_comment TEXT DEFAULT NULL,
-    semantic_model_definition TEXT NOT NULL,
+    datasets TEXT NOT NULL,
+    relationships TEXT DEFAULT NULL,
+    metrics TEXT DEFAULT NULL,
+    ai_context TEXT DEFAULT NULL,
+    custom_extensions TEXT DEFAULT NULL,
     properties TEXT DEFAULT NULL,
     audit_info TEXT NOT NULL,
     deleted_at BIGINT NOT NULL DEFAULT 0,
@@ -146,7 +150,11 @@ COMMENT ON COLUMN 
semantic_model_version_info.semantic_model_id IS 'semantic mod
 COMMENT ON COLUMN semantic_model_version_info.version IS 'semantic model 
version';
 COMMENT ON COLUMN semantic_model_version_info.semantic_model_name IS 'semantic 
model name snapshot';
 COMMENT ON COLUMN semantic_model_version_info.semantic_model_comment IS 
'semantic model comment snapshot';
-COMMENT ON COLUMN semantic_model_version_info.semantic_model_definition IS 
'structured definition snapshot (JSON)';
+COMMENT ON COLUMN semantic_model_version_info.datasets IS 'dataset definitions 
snapshot (JSON)';
+COMMENT ON COLUMN semantic_model_version_info.relationships IS 'relationship 
definitions snapshot (JSON)';
+COMMENT ON COLUMN semantic_model_version_info.metrics IS 'metric definitions 
snapshot (JSON)';
+COMMENT ON COLUMN semantic_model_version_info.ai_context IS 'model AI context 
snapshot (JSON)';
+COMMENT ON COLUMN semantic_model_version_info.custom_extensions IS 'model 
custom extensions snapshot (JSON)';
 COMMENT ON COLUMN semantic_model_version_info.properties IS 'semantic model 
properties snapshot (JSON)';
 COMMENT ON COLUMN semantic_model_version_info.audit_info IS 'semantic model 
version audit info';
 COMMENT ON COLUMN semantic_model_version_info.deleted_at IS 'version deleted 
at';

Reply via email to