Copilot commented on code in PR #10879:
URL: https://github.com/apache/gravitino/pull/10879#discussion_r3151721099


##########
core/src/main/java/org/apache/gravitino/storage/relational/service/ViewMetaService.java:
##########
@@ -158,104 +151,81 @@ public GenericEntity getViewByIdentifier(NameIdentifier 
identifier) {
       metricsSource = GRAVITINO_RELATIONAL_STORE_METRIC_NAME,
       baseMetricName = "insertViewFromEntity")
   public void insertView(GenericEntity entity, boolean overwrite) throws 
IOException {
-    Namespace namespace = entity.namespace();
-    NamespaceUtil.checkView(namespace);
-
-    ViewPO.Builder builder = createViewPOBuilder(namespace);
-    ViewPO viewPO =
-        builder
-            .withViewId(entity.id())
-            .withViewName(entity.name())
-            .withCurrentVersion(1L)
-            .withLastVersion(1L)
-            .withDeletedAt(0L)
+    NamespaceUtil.checkView(entity.namespace());
+
+    ViewEntity viewEntity =
+        ViewEntity.builder()
+            .withId(entity.id())
+            .withName(entity.name())
+            .withNamespace(entity.namespace())
+            .withColumns(new Column[0])
+            .withRepresentations(
+                new Representation[] {
+                  
SQLRepresentation.builder().withDialect("unknown").withSql("placeholder").build()
+                })
+            .withAuditInfo(AuditInfo.EMPTY)
             .build();
-
-    insertView(viewPO, overwrite);
+    insertView(viewEntity, overwrite);
   }

Review Comment:
   Persisting a synthetic placeholder representation/columns for 
`GenericEntity` view inserts risks storing materially incorrect view 
definitions in the metadata store, which can be hard to detect later. If this 
path is only intended for tests/legacy compatibility, consider failing fast 
(throw) unless sufficient view definition data is provided, or clearly 
separating it into a test-only helper; alternatively, require callers to insert 
`ViewEntity` for views so persisted metadata always reflects real definitions.



##########
core/src/main/java/org/apache/gravitino/storage/relational/mapper/ViewMetaMapper.java:
##########
@@ -21,26 +21,69 @@
 
 import java.util.List;
 import org.apache.gravitino.storage.relational.po.ViewPO;
+import org.apache.gravitino.storage.relational.po.ViewVersionInfoPO;
 import org.apache.ibatis.annotations.DeleteProvider;
 import org.apache.ibatis.annotations.InsertProvider;
+import org.apache.ibatis.annotations.One;
 import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Result;
+import org.apache.ibatis.annotations.ResultMap;
+import org.apache.ibatis.annotations.Results;
+import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.SelectProvider;
 import org.apache.ibatis.annotations.UpdateProvider;
 
-/**
- * A MyBatis Mapper for view meta operation SQLs.
- *
- * <p>This interface class is a specification defined by MyBatis. It requires 
this interface class
- * to identify the corresponding SQLs for execution. We can write SQLs in an 
additional XML file, or
- * write SQLs with annotations in this interface Mapper. See: <a
- * href="https://mybatis.org/mybatis-3/getting-started.html";></a>
- */
+/** A MyBatis Mapper for view meta operation SQLs. */
 public interface ViewMetaMapper {
   String TABLE_NAME = "view_meta";
-
+  String VERSION_TABLE_NAME = "view_version_info";
+
+  @Results(
+      id = "mapToViewVersionInfoPO",
+      value = {
+        @Result(property = "id", column = "id", id = true),
+        @Result(property = "metalakeId", column = "version_metalake_id"),
+        @Result(property = "catalogId", column = "version_catalog_id"),
+        @Result(property = "schemaId", column = "version_schema_id"),
+        @Result(property = "viewId", column = "version_view_id"),
+        @Result(property = "version", column = "version"),
+        @Result(property = "viewComment", column = "view_comment"),
+        @Result(property = "columns", column = "columns"),
+        @Result(property = "properties", column = "properties"),
+        @Result(property = "defaultCatalog", column = "default_catalog"),
+        @Result(property = "defaultSchema", column = "default_schema"),
+        @Result(property = "representations", column = "representations"),
+        @Result(property = "auditInfo", column = "version_audit_info"),
+        @Result(property = "deletedAt", column = "version_deleted_at")
+      })
+  @Select("SELECT 1") // Dummy SQL to avoid MyBatis error, never be executed
+  ViewVersionInfoPO mapToViewVersionInfoPO();
+
+  @Results(
+      id = "viewPOResultMap",
+      value = {
+        @Result(property = "viewId", column = "view_id", id = true),
+        @Result(property = "viewName", column = "view_name"),
+        @Result(property = "metalakeId", column = "metalake_id"),
+        @Result(property = "catalogId", column = "catalog_id"),
+        @Result(property = "schemaId", column = "schema_id"),
+        @Result(property = "currentVersion", column = "current_version"),
+        @Result(property = "lastVersion", column = "last_version"),
+        @Result(property = "auditInfo", column = "audit_info"),
+        @Result(property = "deletedAt", column = "deleted_at"),
+        @Result(
+            property = "viewVersionInfoPO",
+            javaType = ViewVersionInfoPO.class,
+            column =
+                
"{id,version_metalake_id,version_catalog_id,version_schema_id,version_view_id,"
+                    + 
"version,view_comment,columns,properties,default_catalog,default_schema,"
+                    + "representations,version_audit_info,version_deleted_at}",
+            one = @One(resultMap = "mapToViewVersionInfoPO"))
+      })

Review Comment:
   This MyBatis annotation mapping is very likely invalid: `@One` typically 
requires a `select = ...` (not a `resultMap = ...`), and the composite `column 
= \"{...}\"` format is not in the expected `{prop=col,...}` form. The 
`@Select(\"SELECT 1\")` dummy method is also a red flag and may break at 
runtime or fail to compile depending on MyBatis version. Prefer mapping the 
joined columns directly into the nested object (e.g., via dot-path properties 
like `viewVersionInfoPO.id`, `viewVersionInfoPO.viewComment`, etc.), or switch 
to XML mapping where nested `resultMap` associations are supported cleanly.



##########
core/src/main/java/org/apache/gravitino/storage/relational/service/ViewMetaService.java:
##########
@@ -158,104 +151,81 @@ public GenericEntity getViewByIdentifier(NameIdentifier 
identifier) {
       metricsSource = GRAVITINO_RELATIONAL_STORE_METRIC_NAME,
       baseMetricName = "insertViewFromEntity")
   public void insertView(GenericEntity entity, boolean overwrite) throws 
IOException {
-    Namespace namespace = entity.namespace();
-    NamespaceUtil.checkView(namespace);
-
-    ViewPO.Builder builder = createViewPOBuilder(namespace);
-    ViewPO viewPO =
-        builder
-            .withViewId(entity.id())
-            .withViewName(entity.name())
-            .withCurrentVersion(1L)
-            .withLastVersion(1L)
-            .withDeletedAt(0L)
+    NamespaceUtil.checkView(entity.namespace());
+
+    ViewEntity viewEntity =
+        ViewEntity.builder()
+            .withId(entity.id())
+            .withName(entity.name())
+            .withNamespace(entity.namespace())
+            .withColumns(new Column[0])
+            .withRepresentations(
+                new Representation[] {
+                  
SQLRepresentation.builder().withDialect("unknown").withSql("placeholder").build()
+                })
+            .withAuditInfo(AuditInfo.EMPTY)
             .build();
-
-    insertView(viewPO, overwrite);
+    insertView(viewEntity, overwrite);
   }
 
-  /**
-   * Update a view.
-   *
-   * @param ident The identifier of the view.
-   * @param updater The function to update the view entity.
-   * @return The updated GenericEntity.
-   * @throws IOException If an I/O error occurs.
-   */
   @Monitored(
       metricsSource = GRAVITINO_RELATIONAL_STORE_METRIC_NAME,
       baseMetricName = "updateViewByIdentifier")
-  public <E extends HasIdentifier> GenericEntity updateView(
+  public <E extends Entity & HasIdentifier> ViewEntity updateView(
       NameIdentifier ident, Function<E, E> updater) throws IOException {
     NameIdentifierUtil.checkView(ident);
 
     ViewPO oldViewPO = viewPOFetcher().apply(ident);
+    ViewEntity oldViewEntity = fromViewPO(oldViewPO, ident.namespace());
+    ViewEntity newEntity = (ViewEntity) updater.apply((E) oldViewEntity);
+    Preconditions.checkArgument(
+        Objects.equals(oldViewEntity.id(), newEntity.id()),
+        "The updated view entity id: %s should be same with the entity id 
before: %s",
+        newEntity.id(),
+        oldViewEntity.id());
 
-    GenericEntity oldEntity =
-        GenericEntity.builder()
-            .withId(oldViewPO.getViewId())
-            .withName(oldViewPO.getViewName())
-            .withNamespace(ident.namespace())
-            .withEntityType(Entity.EntityType.VIEW)
-            .build();
-
-    GenericEntity newEntity = (GenericEntity) updater.apply((E) oldEntity);
-
-    // Check if the namespace (schema) has changed and resolve new schema ID 
if needed
-    boolean isSchemaChanged =
-        newEntity.namespace() != null && 
!newEntity.namespace().equals(ident.namespace());
-    Long schemaId =
-        isSchemaChanged
-            ? EntityIdService.getEntityId(
-                NameIdentifier.of(newEntity.namespace().levels()), 
Entity.EntityType.SCHEMA)
-            : oldViewPO.getSchemaId();
-
-    ViewPO newViewPO =
-        ViewPO.builder()
-            .withViewId(oldViewPO.getViewId())
-            .withViewName(newEntity.name())
-            .withMetalakeId(oldViewPO.getMetalakeId())
-            .withCatalogId(oldViewPO.getCatalogId())
-            .withSchemaId(schemaId)
-            .withDeletedAt(oldViewPO.getDeletedAt())
-            .withLastVersion(oldViewPO.getLastVersion())
-            .withCurrentVersion(oldViewPO.getCurrentVersion())
-            .build();
-
-    updateView(oldViewPO, newViewPO);
-
-    return newEntity;
+    try {
+      ViewPO newViewPO = updateViewPO(oldViewPO, newEntity);
+      SessionUtils.doMultipleWithCommit(
+          () ->
+              SessionUtils.doWithoutCommit(
+                  ViewVersionInfoMapper.class,
+                  mapper -> 
mapper.insertViewVersionInfo(newViewPO.getViewVersionInfoPO())),
+          () ->
+              SessionUtils.doWithoutCommit(
+                  ViewMetaMapper.class, mapper -> 
mapper.updateViewMeta(newViewPO, oldViewPO)));
+      return newEntity;
+    } catch (RuntimeException re) {
+      ExceptionUtils.checkSQLException(
+          re, Entity.EntityType.VIEW, newEntity.nameIdentifier().toString());
+      throw re;
+    }
   }
 
-  /**
-   * Delete a view by identifier.
-   *
-   * @param ident The identifier of the view.
-   * @return true if the view was deleted, false otherwise.
-   */
   @Monitored(
       metricsSource = GRAVITINO_RELATIONAL_STORE_METRIC_NAME,
       baseMetricName = "deleteViewByIdentifier")
   public boolean deleteView(NameIdentifier ident) {
     NameIdentifierUtil.checkView(ident);
-
     ViewPO viewPO = viewPOFetcher().apply(ident);
     return deleteView(viewPO.getViewId());
   }
 
-  private ViewPO getViewPOBySchemaIdAndName(Long schemaId, String viewName) {
-    ViewPO viewPO =
-        SessionUtils.getWithoutCommit(
+  @Monitored(
+      metricsSource = GRAVITINO_RELATIONAL_STORE_METRIC_NAME,
+      baseMetricName = "deleteViewMetasByLegacyTimeline")
+  public int deleteViewMetasByLegacyTimeline(Long legacyTimeline, int limit) {
+    int versionDeletedCount =
+        SessionUtils.doWithCommitAndFetchResult(
+            ViewVersionInfoMapper.class,
+            mapper -> 
mapper.deleteViewVersionsByLegacyTimeline(legacyTimeline, limit));
+
+    int metaDeletedCount =
+        SessionUtils.doWithCommitAndFetchResult(
             ViewMetaMapper.class,
-            mapper -> mapper.selectViewMetaBySchemaIdAndName(schemaId, 
viewName));
+            mapper -> mapper.deleteViewMetasByLegacyTimeline(legacyTimeline, 
limit));
 
-    if (viewPO == null) {
-      throw new NoSuchEntityException(
-          NoSuchEntityException.NO_SUCH_ENTITY_MESSAGE,
-          Entity.EntityType.VIEW.name().toLowerCase(),
-          viewName);
-    }
-    return viewPO;
+    return versionDeletedCount + metaDeletedCount;
   }

Review Comment:
   The `limit` parameter is applied independently to version rows and meta 
rows, so a single call can delete up to `2 * limit` rows and may delete 
versions and metas unevenly when limits are hit. If callers rely on `limit` as 
an upper bound on total deletions (or want to avoid orphan rows under tight 
limits), consider enforcing a single overall budget (e.g., delete versions 
first, then delete metas with `limit - versionDeletedCount`), and ideally run 
both deletions in one transaction.



##########
core/src/main/java/org/apache/gravitino/storage/relational/service/ViewMetaService.java:
##########
@@ -158,104 +151,81 @@ public GenericEntity getViewByIdentifier(NameIdentifier 
identifier) {
       metricsSource = GRAVITINO_RELATIONAL_STORE_METRIC_NAME,
       baseMetricName = "insertViewFromEntity")
   public void insertView(GenericEntity entity, boolean overwrite) throws 
IOException {
-    Namespace namespace = entity.namespace();
-    NamespaceUtil.checkView(namespace);
-
-    ViewPO.Builder builder = createViewPOBuilder(namespace);
-    ViewPO viewPO =
-        builder
-            .withViewId(entity.id())
-            .withViewName(entity.name())
-            .withCurrentVersion(1L)
-            .withLastVersion(1L)
-            .withDeletedAt(0L)
+    NamespaceUtil.checkView(entity.namespace());
+
+    ViewEntity viewEntity =
+        ViewEntity.builder()
+            .withId(entity.id())
+            .withName(entity.name())
+            .withNamespace(entity.namespace())
+            .withColumns(new Column[0])
+            .withRepresentations(
+                new Representation[] {
+                  
SQLRepresentation.builder().withDialect("unknown").withSql("placeholder").build()
+                })
+            .withAuditInfo(AuditInfo.EMPTY)
             .build();
-
-    insertView(viewPO, overwrite);
+    insertView(viewEntity, overwrite);
   }
 
-  /**
-   * Update a view.
-   *
-   * @param ident The identifier of the view.
-   * @param updater The function to update the view entity.
-   * @return The updated GenericEntity.
-   * @throws IOException If an I/O error occurs.
-   */
   @Monitored(
       metricsSource = GRAVITINO_RELATIONAL_STORE_METRIC_NAME,
       baseMetricName = "updateViewByIdentifier")
-  public <E extends HasIdentifier> GenericEntity updateView(
+  public <E extends Entity & HasIdentifier> ViewEntity updateView(
       NameIdentifier ident, Function<E, E> updater) throws IOException {
     NameIdentifierUtil.checkView(ident);
 
     ViewPO oldViewPO = viewPOFetcher().apply(ident);
+    ViewEntity oldViewEntity = fromViewPO(oldViewPO, ident.namespace());
+    ViewEntity newEntity = (ViewEntity) updater.apply((E) oldViewEntity);

Review Comment:
   This API uses generics with unchecked casts (`(E) oldViewEntity` and 
`(ViewEntity) updater.apply(...)`), which can lead to `ClassCastException` at 
runtime and makes the contract unclear. Since this service now operates on 
`ViewEntity`, prefer a strongly-typed signature like `Function<ViewEntity, 
ViewEntity>` (or overloads) to remove unsafe casts and make misuse impossible 
at compile time.
   ```suggestion
     public ViewEntity updateView(NameIdentifier ident, Function<ViewEntity, 
ViewEntity> updater)
         throws IOException {
       NameIdentifierUtil.checkView(ident);
   
       ViewPO oldViewPO = viewPOFetcher().apply(ident);
       ViewEntity oldViewEntity = fromViewPO(oldViewPO, ident.namespace());
       ViewEntity newEntity = updater.apply(oldViewEntity);
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to