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

yuqi4733 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 a30b149f8e [#8940] improvement(core): Support serialize/deserialize 
for distributionImpl and sortOrderImpl  (#9198)
a30b149f8e is described below

commit a30b149f8eec2ac40e70dad1701963d4a62581de
Author: Junda Yang <[email protected]>
AuthorDate: Thu Nov 20 23:14:36 2025 -0800

    [#8940] improvement(core): Support serialize/deserialize for 
distributionImpl and sortOrderImpl  (#9198)
    
    ### What changes were proposed in this pull request?
    
    - Added JSON serializers/deserializers for Partitioning objects in
    JsonUtils
    - Enhanced POConverters to properly serialize/deserialize Distribution,
    SortOrder[], Index[], and Transform[] (partitions) by converting to DTOs
    before JSON serialization
    - Simplified Indexes.IndexImpl by removing custom Jackson serializers
    (105 lines deleted) and adding equals()/hashCode() methods
    - Added comprehensive serialization tests in new TestSerializer class
    
    ### Why are the changes needed?
    
    Enable proper persistence of Lance table metadata (distribution, sort
    orders, indexes, partitions) in the relational backend. Previously these
    fields were not being serialized/deserialized, preventing complete
    metadata management for generic lakehouse tables.
    
    Fix: #8940
    
    ### Does this PR introduce _any_ user-facing change?
    
    No. Internal serialization improvement - table metadata is now properly
    persisted and retrieved.
    
    ### How was this patch tested?
    
    - New TestSerializer unit tests for Distribution, SortOrder, Index, and
    Partitioning serialization
    - Enhanced TestPOConverters to validate table entity conversion with all
    metadata fields
    - Updated CatalogGenericLakehouseLanceIT integration test to create and
    load tables with full metadata
    
    Co-authored-by: Mini Yu <[email protected]>
---
 .../org/apache/gravitino/rel/indexes/Indexes.java  | 105 +++---------
 .../java/org/apache/gravitino/rel/TestIndex.java   |  57 -------
 .../test/CatalogGenericLakehouseLanceIT.java       |  52 +++++-
 .../java/org/apache/gravitino/json/JsonUtils.java  |   6 +
 .../org/apache/gravitino/json/TestSerializer.java  | 186 +++++++++++++++++++++
 .../storage/relational/utils/POConverters.java     |  68 +++++++-
 .../storage/relational/utils/TestPOConverters.java |  18 ++
 7 files changed, 340 insertions(+), 152 deletions(-)

diff --git a/api/src/main/java/org/apache/gravitino/rel/indexes/Indexes.java 
b/api/src/main/java/org/apache/gravitino/rel/indexes/Indexes.java
index d1b1a1f523..c6303e7938 100644
--- a/api/src/main/java/org/apache/gravitino/rel/indexes/Indexes.java
+++ b/api/src/main/java/org/apache/gravitino/rel/indexes/Indexes.java
@@ -18,21 +18,8 @@
  */
 package org.apache.gravitino.rel.indexes;
 
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-import java.io.IOException;
-import java.util.List;
-import java.util.Locale;
+import com.google.common.base.Objects;
+import java.util.Arrays;
 
 /** Helper methods to create index to pass into Apache Gravitino. */
 public class Indexes {
@@ -89,76 +76,7 @@ public class Indexes {
         .build();
   }
 
-  /** Custom JSON serializer for Index objects. */
-  public static class IndexSerializer extends JsonSerializer<Index> {
-    @Override
-    public void serialize(Index value, JsonGenerator gen, SerializerProvider 
serializers)
-        throws IOException {
-      gen.writeStartObject();
-      gen.writeStringField("indexType", 
value.type().name().toUpperCase(Locale.ROOT));
-      if (null != value.name()) {
-        gen.writeStringField("name", value.name());
-      }
-      gen.writeFieldName("fieldNames");
-      gen.writeObject(value.fieldNames());
-      gen.writeEndObject();
-    }
-  }
-
-  /** Custom JSON deserializer for Index objects. */
-  public static class IndexDeserializer extends JsonDeserializer<Index> {
-
-    @Override
-    public Index deserialize(JsonParser p, DeserializationContext ctxt) throws 
IOException {
-      JsonNode node = p.getCodec().readTree(p);
-      Preconditions.checkArgument(
-          node != null && !node.isNull() && node.isObject(),
-          "Index must be a valid JSON object, but found: %s",
-          node);
-
-      IndexImpl.Builder builder = IndexImpl.builder();
-      Preconditions.checkArgument(
-          node.has("indexType"), "Cannot parse index from missing type: %s", 
node);
-      String indexType = getString("indexType", node);
-      
builder.withIndexType(Index.IndexType.valueOf(indexType.toUpperCase(Locale.ROOT)));
-      if (node.has("name")) {
-        builder.withName(getString("name", node));
-      }
-      Preconditions.checkArgument(
-          node.has("fieldNames"), "Cannot parse index from missing field 
names: %s", node);
-      List<String[]> fieldNames = Lists.newArrayList();
-      node.get("fieldNames").forEach(field -> 
fieldNames.add(getStringArray((ArrayNode) field)));
-      builder.withFieldNames(fieldNames.toArray(new String[0][0]));
-      return builder.build();
-    }
-
-    private static String[] getStringArray(ArrayNode node) {
-      String[] array = new String[node.size()];
-      for (int i = 0; i < node.size(); i++) {
-        array[i] = node.get(i).asText();
-      }
-      return array;
-    }
-
-    private static String getString(String property, JsonNode node) {
-      Preconditions.checkArgument(node.has(property), "Cannot parse missing 
string: %s", property);
-      JsonNode pNode = node.get(property);
-      return convertToString(property, pNode);
-    }
-
-    private static String convertToString(String property, JsonNode pNode) {
-      Preconditions.checkArgument(
-          pNode != null && !pNode.isNull() && pNode.isTextual(),
-          "Cannot parse to a string value %s: %s",
-          property,
-          pNode);
-      return pNode.asText();
-    }
-  }
-
   /** The user side implementation of the index. */
-  @JsonSerialize(using = IndexSerializer.class)
-  @JsonDeserialize(using = IndexDeserializer.class)
   public static final class IndexImpl implements Index {
     private final IndexType indexType;
 
@@ -203,6 +121,25 @@ public class Indexes {
       return fieldNames;
     }
 
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+      IndexImpl index = (IndexImpl) o;
+      return indexType == index.indexType
+          && Objects.equal(name, index.name)
+          && Arrays.deepEquals(fieldNames, index.fieldNames);
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hashCode(indexType, name, Arrays.hashCode(fieldNames));
+    }
+
     /**
      * @return the builder for creating a new instance of IndexImpl.
      */
diff --git a/api/src/test/java/org/apache/gravitino/rel/TestIndex.java 
b/api/src/test/java/org/apache/gravitino/rel/TestIndex.java
deleted file mode 100644
index 4a807fbb7b..0000000000
--- a/api/src/test/java/org/apache/gravitino/rel/TestIndex.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- *  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.rel;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.MapperFeature;
-import com.fasterxml.jackson.databind.SerializationFeature;
-import com.fasterxml.jackson.databind.cfg.EnumFeature;
-import com.fasterxml.jackson.databind.json.JsonMapper;
-import org.apache.gravitino.rel.indexes.Index;
-import org.apache.gravitino.rel.indexes.Indexes;
-import org.apache.gravitino.rel.indexes.Indexes.IndexImpl;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-public class TestIndex {
-
-  @Test
-  void testIndexSerialization() throws JsonProcessingException {
-    String[][] fields = {{"column1"}, {"column2", "subcolumn"}};
-    Index index = Indexes.unique("test_index", fields);
-
-    JsonMapper jsonMapper =
-        JsonMapper.builder()
-            .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
-            .configure(EnumFeature.WRITE_ENUMS_TO_LOWERCASE, true)
-            .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
-            .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
-            
.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true)
-            .build();
-
-    String json = jsonMapper.writeValueAsString(index);
-
-    Index deserializedIndex = jsonMapper.readValue(json, IndexImpl.class);
-    Assertions.assertEquals(index.type(), deserializedIndex.type());
-    Assertions.assertEquals(index.name(), deserializedIndex.name());
-    Assertions.assertArrayEquals(index.fieldNames(), 
deserializedIndex.fieldNames());
-  }
-}
diff --git 
a/catalogs/catalog-generic-lakehouse/src/test/java/org/apache/gravitino/catalog/lakehouse/integration/test/CatalogGenericLakehouseLanceIT.java
 
b/catalogs/catalog-generic-lakehouse/src/test/java/org/apache/gravitino/catalog/lakehouse/integration/test/CatalogGenericLakehouseLanceIT.java
index c4790c4d85..1b4fd8b172 100644
--- 
a/catalogs/catalog-generic-lakehouse/src/test/java/org/apache/gravitino/catalog/lakehouse/integration/test/CatalogGenericLakehouseLanceIT.java
+++ 
b/catalogs/catalog-generic-lakehouse/src/test/java/org/apache/gravitino/catalog/lakehouse/integration/test/CatalogGenericLakehouseLanceIT.java
@@ -58,7 +58,22 @@ import org.apache.gravitino.integration.test.util.BaseIT;
 import org.apache.gravitino.integration.test.util.GravitinoITUtils;
 import org.apache.gravitino.rel.Column;
 import org.apache.gravitino.rel.Table;
+import org.apache.gravitino.rel.expressions.NamedReference;
+import org.apache.gravitino.rel.expressions.distributions.Distribution;
+import org.apache.gravitino.rel.expressions.distributions.Distributions;
+import org.apache.gravitino.rel.expressions.distributions.Strategy;
+import org.apache.gravitino.rel.expressions.literals.Literals;
+import org.apache.gravitino.rel.expressions.sorts.NullOrdering;
+import org.apache.gravitino.rel.expressions.sorts.SortDirection;
+import org.apache.gravitino.rel.expressions.sorts.SortOrder;
+import org.apache.gravitino.rel.expressions.sorts.SortOrders;
+import org.apache.gravitino.rel.expressions.transforms.Transform;
 import org.apache.gravitino.rel.expressions.transforms.Transforms;
+import org.apache.gravitino.rel.indexes.Index;
+import org.apache.gravitino.rel.indexes.Index.IndexType;
+import org.apache.gravitino.rel.indexes.Indexes;
+import org.apache.gravitino.rel.partitions.Partitions;
+import org.apache.gravitino.rel.partitions.RangePartition;
 import org.apache.gravitino.rel.types.Types;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
@@ -176,6 +191,30 @@ public class CatalogGenericLakehouseLanceIT extends BaseIT 
{
     properties = createProperties();
     properties.put("format", "lance");
 
+    Distribution distribution =
+        Distributions.of(Strategy.EVEN, 10, 
NamedReference.field(LANCE_COL_NAME1));
+    SortOrder[] sortOrders =
+        new SortOrder[] {
+          SortOrders.of(
+              NamedReference.field(LANCE_COL_NAME2),
+              SortDirection.ASCENDING,
+              NullOrdering.NULLS_FIRST)
+        };
+    Index[] indexes =
+        new Index[] {
+          Indexes.of(IndexType.UNIQUE_KEY, "unique_index", new String[][] 
{{LANCE_COL_NAME3}})
+        };
+
+    RangePartition p1 =
+        Partitions.range(
+            "p1", Literals.stringLiteral("20220101"), Literals.NULL, 
Collections.emptyMap());
+    RangePartition p2 =
+        Partitions.range(
+            "p2", Literals.stringLiteral("20220301"), Literals.NULL, 
Collections.emptyMap());
+    Transform[] partitioning = {
+      Transforms.range(new String[] {LANCE_COL_NAME3}, new RangePartition[] 
{p1, p2})
+    };
+
     createdTable =
         catalog
             .asTableCatalog()
@@ -184,9 +223,10 @@ public class CatalogGenericLakehouseLanceIT extends BaseIT 
{
                 columns,
                 TABLE_COMMENT,
                 properties,
-                Transforms.EMPTY_TRANSFORM,
-                null,
-                null);
+                partitioning,
+                distribution,
+                sortOrders,
+                indexes);
     Assertions.assertEquals(createdTable.name(), tableName);
     createdTableProperties = createdTable.properties();
     Assertions.assertEquals("lance", createdTableProperties.get("format"));
@@ -198,6 +238,12 @@ public class CatalogGenericLakehouseLanceIT extends BaseIT 
{
     Assertions.assertEquals(expectedTableLocation, 
createdTableProperties.get("location"));
     Assertions.assertTrue(new File(expectedTableLocation).exists());
 
+    Table loadTable = catalog.asTableCatalog().loadTable(nameIdentifier);
+    Assertions.assertEquals(distribution, loadTable.distribution());
+    Assertions.assertArrayEquals(sortOrders, loadTable.sortOrder());
+    Assertions.assertArrayEquals(indexes, loadTable.index());
+    Assertions.assertArrayEquals(partitioning, loadTable.partitioning());
+
     // Now try to load table
     Table loadedTable = catalog.asTableCatalog().loadTable(nameIdentifier);
     Assertions.assertEquals(createdTable.name(), loadedTable.name());
diff --git a/common/src/main/java/org/apache/gravitino/json/JsonUtils.java 
b/common/src/main/java/org/apache/gravitino/json/JsonUtils.java
index fb9a33f268..b35243b17b 100644
--- a/common/src/main/java/org/apache/gravitino/json/JsonUtils.java
+++ b/common/src/main/java/org/apache/gravitino/json/JsonUtils.java
@@ -18,6 +18,10 @@
  */
 package org.apache.gravitino.json;
 
+import static 
org.apache.gravitino.dto.rel.expressions.FunctionArg.ArgType.FIELD;
+import static 
org.apache.gravitino.dto.rel.expressions.FunctionArg.ArgType.FUNCTION;
+import static 
org.apache.gravitino.dto.rel.expressions.FunctionArg.ArgType.LITERAL;
+
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.PropertyAccessor;
 import com.fasterxml.jackson.core.JacksonException;
@@ -304,6 +308,8 @@ public class JsonUtils {
                 new SimpleModule()
                     .addDeserializer(Type.class, new TypeDeserializer())
                     .addSerializer(Type.class, new TypeSerializer())
+                    .addDeserializer(Partitioning.class, new 
PartitioningDeserializer())
+                    .addSerializer(Partitioning.class, new 
PartitioningSerializer())
                     .addDeserializer(Expression.class, new 
ColumnDefaultValueDeserializer())
                     .addSerializer(Expression.class, new 
ColumnDefaultValueSerializer())
                     .addDeserializer(StatisticValue.class, new 
StatisticValueDeserializer())
diff --git a/common/src/test/java/org/apache/gravitino/json/TestSerializer.java 
b/common/src/test/java/org/apache/gravitino/json/TestSerializer.java
new file mode 100644
index 0000000000..a8fff05622
--- /dev/null
+++ b/common/src/test/java/org/apache/gravitino/json/TestSerializer.java
@@ -0,0 +1,186 @@
+/*
+ * 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.json;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.apache.gravitino.dto.rel.DistributionDTO;
+import org.apache.gravitino.dto.rel.SortOrderDTO;
+import org.apache.gravitino.dto.rel.expressions.LiteralDTO;
+import org.apache.gravitino.dto.rel.indexes.IndexDTO;
+import org.apache.gravitino.dto.rel.partitioning.DayPartitioningDTO;
+import org.apache.gravitino.dto.rel.partitioning.Partitioning;
+import org.apache.gravitino.dto.rel.partitioning.RangePartitioningDTO;
+import org.apache.gravitino.dto.rel.partitions.RangePartitionDTO;
+import org.apache.gravitino.dto.util.DTOConverters;
+import org.apache.gravitino.rel.expressions.FunctionExpression;
+import org.apache.gravitino.rel.expressions.NamedReference;
+import org.apache.gravitino.rel.expressions.distributions.Distributions;
+import 
org.apache.gravitino.rel.expressions.distributions.Distributions.DistributionImpl;
+import org.apache.gravitino.rel.expressions.distributions.Strategy;
+import org.apache.gravitino.rel.expressions.literals.Literals;
+import org.apache.gravitino.rel.expressions.sorts.NullOrdering;
+import org.apache.gravitino.rel.expressions.sorts.SortDirection;
+import org.apache.gravitino.rel.expressions.sorts.SortOrders;
+import org.apache.gravitino.rel.expressions.sorts.SortOrders.SortImpl;
+import org.apache.gravitino.rel.expressions.transforms.Transform;
+import org.apache.gravitino.rel.indexes.Index.IndexType;
+import org.apache.gravitino.rel.indexes.Indexes;
+import org.apache.gravitino.rel.indexes.Indexes.IndexImpl;
+import org.apache.gravitino.rel.types.Types;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+public class TestSerializer {
+
+  @Test
+  void testDistributionImplSerializer() throws JsonProcessingException {
+    DistributionImpl distribution =
+        (DistributionImpl) Distributions.of(Strategy.EVEN, 10, 
NamedReference.field("col1"));
+    String actualJson =
+        
JsonUtils.anyFieldMapper().writeValueAsString(DTOConverters.toDTO(distribution));
+    String expectedJson =
+        """
+            
{"strategy":"even","number":10,"funcArgs":[{"type":"field","fieldName":["col1"]}]}""";
+    Assertions.assertEquals(expectedJson, actualJson);
+    DistributionDTO deserialized =
+        JsonUtils.anyFieldMapper().readValue(actualJson, 
DistributionDTO.class);
+    Assertions.assertEquals(distribution, DTOConverters.fromDTO(deserialized));
+
+    distribution =
+        (DistributionImpl)
+            Distributions.of(
+                Strategy.EVEN,
+                10,
+                FunctionExpression.of(
+                    "bucket", Literals.integerLiteral(10), 
NamedReference.field("col_1")));
+    actualJson = 
JsonUtils.anyFieldMapper().writeValueAsString(DTOConverters.toDTO(distribution));
+    expectedJson =
+        """
+          
{"strategy":"even","number":10,"funcArgs":[{"type":"function","funcName":"bucket",\
+          
"funcArgs":[{"type":"literal","dataType":"integer","value":"10"},{"type":"field",\
+          "fieldName":["col_1"]}]}]}""";
+    Assertions.assertEquals(expectedJson, actualJson);
+    deserialized = JsonUtils.anyFieldMapper().readValue(actualJson, 
DistributionDTO.class);
+    Assertions.assertEquals(distribution, DTOConverters.fromDTO(deserialized));
+  }
+
+  @Test
+  void testSortOrderSerializer() throws JsonProcessingException {
+    SortImpl sortOrder =
+        SortOrders.of(
+            NamedReference.field("col1"), SortDirection.ASCENDING, 
NullOrdering.NULLS_LAST);
+    String actualJson =
+        
JsonUtils.anyFieldMapper().writeValueAsString(DTOConverters.toDTO(sortOrder));
+    String expectedJson =
+        """
+            
{"sortTerm":{"type":"field","fieldName":["col1"]},"direction":"asc",\
+            "nullOrdering":"nulls_last"}""";
+    Assertions.assertEquals(expectedJson, actualJson);
+
+    SortOrderDTO deserialized =
+        JsonUtils.anyFieldMapper().readValue(actualJson, SortOrderDTO.class);
+    Assertions.assertEquals(sortOrder, DTOConverters.fromDTO(deserialized));
+
+    sortOrder =
+        SortOrders.of(
+            FunctionExpression.of("lower", NamedReference.field("col_1")),
+            SortDirection.DESCENDING);
+    actualJson = 
JsonUtils.anyFieldMapper().writeValueAsString(DTOConverters.toDTO(sortOrder));
+    expectedJson =
+        """
+          
{"sortTerm":{"type":"function","funcName":"lower","funcArgs":[{"type":"field",\
+          
"fieldName":["col_1"]}]},"direction":"desc","nullOrdering":"nulls_last"}""";
+    Assertions.assertEquals(expectedJson, actualJson);
+    deserialized = JsonUtils.anyFieldMapper().readValue(actualJson, 
SortOrderDTO.class);
+    Assertions.assertEquals(sortOrder, DTOConverters.fromDTO(deserialized));
+  }
+
+  @Test
+  void testIndexImplSerializer() throws JsonProcessingException {
+    IndexImpl index =
+        (IndexImpl)
+            Indexes.of(IndexType.PRIMARY_KEY, "index_1", new String[][] {new 
String[] {"col1"}});
+
+    String actualJson = 
JsonUtils.anyFieldMapper().writeValueAsString((DTOConverters.toDTO(index)));
+    String expectedJson =
+        """
+            
{"indexType":"PRIMARY_KEY","name":"index_1","fieldNames":[["col1"]]}""";
+    Assertions.assertEquals(expectedJson, actualJson);
+    IndexDTO deserialized = JsonUtils.anyFieldMapper().readValue(actualJson, 
IndexDTO.class);
+    Assertions.assertEquals(index, DTOConverters.fromDTO(deserialized));
+
+    index =
+        (IndexImpl)
+            Indexes.of(
+                IndexType.UNIQUE_KEY,
+                "index_2",
+                new String[][] {new String[] {"col1"}, new String[] {"col2"}});
+    actualJson = JsonUtils.anyFieldMapper().writeValueAsString(index);
+    expectedJson =
+        """
+            
{"indexType":"unique_key","name":"index_2","fieldNames":[["col1"],["col2"]]}""";
+    Assertions.assertEquals(expectedJson, actualJson);
+  }
+
+  @Test
+  @Disabled("Disable until Partitioning serializer is implemented")
+  void testPartitioningSerializer() throws JsonProcessingException {
+    Transform transform = DayPartitioningDTO.of("dt");
+    String actualJson =
+        
JsonUtils.anyFieldMapper().writeValueAsString(DTOConverters.toDTO(transform));
+    String expectedJson = """
+          {"strategy":"day","fieldName":["dt"]}""";
+    Assertions.assertEquals(expectedJson, actualJson);
+
+    Partitioning deserialized =
+        JsonUtils.anyFieldMapper().readValue(actualJson, Partitioning.class);
+    Assertions.assertEquals(transform, DTOConverters.fromDTO(deserialized));
+
+    transform =
+        RangePartitioningDTO.of(
+            new String[] {"dt"},
+            new RangePartitionDTO[] {
+              RangePartitionDTO.builder()
+                  .withName("p1")
+                  .withLower(
+                      LiteralDTO.builder()
+                          .withValue("2023-01-01")
+                          .withDataType(Types.StringType.get())
+                          .build())
+                  .withUpper(
+                      LiteralDTO.builder()
+                          .withValue("2024-01-01")
+                          .withDataType(Types.StringType.get())
+                          .build())
+                  .build(),
+            });
+
+    actualJson = JsonUtils.anyFieldMapper().writeValueAsString(transform);
+    expectedJson =
+        """
+            
{"type":"range_partitioning","fieldName":["dt"],"assignments":[{"name":"p1",
+            
"properties":null,"upper":{"type":"literal","dataType":"string","value":"2024-01-01"},
+            
"lower":{"type":"literal","dataType":"string","value":"2023-01-01"}}]}""";
+    Assertions.assertEquals(expectedJson, actualJson);
+    deserialized = JsonUtils.anyFieldMapper().readValue(actualJson, 
Partitioning.class);
+    Assertions.assertEquals(transform, DTOConverters.fromDTO(deserialized));
+  }
+}
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 48cf8cc00e..63f46f4e11 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
@@ -36,7 +36,11 @@ import org.apache.gravitino.authorization.Privilege;
 import org.apache.gravitino.authorization.Privileges;
 import org.apache.gravitino.authorization.SecurableObject;
 import org.apache.gravitino.authorization.SecurableObjects;
+import org.apache.gravitino.dto.rel.DistributionDTO;
+import org.apache.gravitino.dto.rel.SortOrderDTO;
 import org.apache.gravitino.dto.rel.expressions.FunctionArg;
+import org.apache.gravitino.dto.rel.indexes.IndexDTO;
+import org.apache.gravitino.dto.rel.partitioning.Partitioning;
 import org.apache.gravitino.dto.util.DTOConverters;
 import org.apache.gravitino.file.Fileset;
 import org.apache.gravitino.json.JsonUtils;
@@ -60,7 +64,6 @@ import org.apache.gravitino.policy.Policy;
 import org.apache.gravitino.policy.PolicyContent;
 import org.apache.gravitino.rel.Column;
 import org.apache.gravitino.rel.expressions.Expression;
-import org.apache.gravitino.rel.indexes.Indexes.IndexImpl;
 import org.apache.gravitino.rel.types.Type;
 import org.apache.gravitino.storage.relational.po.CatalogPO;
 import org.apache.gravitino.storage.relational.po.ColumnPO;
@@ -407,9 +410,24 @@ public class POConverters {
           .withIndexes(
               tableEntity.getIndexes() == null
                   ? null
-                  : 
JsonUtils.anyFieldMapper().writeValueAsString(tableEntity.getIndexes()));
-
-      // TODO, handle these fields(distribution, sort order, partition) later
+                  : JsonUtils.anyFieldMapper()
+                      
.writeValueAsString(DTOConverters.toDTOs(tableEntity.getIndexes())))
+          .withDistribution(
+              tableEntity.getDistribution() == null
+                  ? null
+                  : JsonUtils.anyFieldMapper()
+                      
.writeValueAsString(DTOConverters.toDTO(tableEntity.getDistribution())))
+          .withSortOrders(
+              tableEntity.getSortOrder() == null
+                  ? null
+                  : JsonUtils.anyFieldMapper()
+                      
.writeValueAsString(DTOConverters.toDTOs(tableEntity.getSortOrder())))
+          .withPartitions(
+              tableEntity.getPartitions() == null
+                  ? null
+                  : JsonUtils.anyFieldMapper()
+                      
.writeValueAsString(DTOConverters.toDTOs(tableEntity.getPartitions())));
+      // TODO support partitions later
       return builder.build();
     } catch (JsonProcessingException e) {
       throw new RuntimeException("Failed to serialize json object:", e);
@@ -451,9 +469,25 @@ public class POConverters {
               .withIndexes(
                   newTable.getIndexes() == null
                       ? null
-                      : 
JsonUtils.anyFieldMapper().writeValueAsString(newTable.getIndexes()))
+                      : JsonUtils.anyFieldMapper()
+                          
.writeValueAsString(DTOConverters.toDTOs(newTable.getIndexes())))
+              .withDistribution(
+                  newTable.getDistribution() == null
+                      ? null
+                      : JsonUtils.anyFieldMapper()
+                          
.writeValueAsString(DTOConverters.toDTO(newTable.getDistribution())))
+              .withSortOrders(
+                  newTable.getSortOrder() == null
+                      ? null
+                      : JsonUtils.anyFieldMapper()
+                          
.writeValueAsString(DTOConverters.toDTOs(newTable.getSortOrder())))
+              .withPartitions(
+                  newTable.getPartitions() == null
+                      ? null
+                      : JsonUtils.anyFieldMapper()
+                          
.writeValueAsString(DTOConverters.toDTOs(newTable.getPartitions())))
+              // TODO support partitions later
               .withFormat(newTable.getFormat());
-      // TODO other fields(partitioning, distribution, sortorder) in the 
refactor PRs.
 
       return builder.build();
     } catch (JsonProcessingException e) {
@@ -482,11 +516,29 @@ public class POConverters {
           .withColumns(fromColumnPOs(columnPOs))
           .withAuditInfo(
               JsonUtils.anyFieldMapper().readValue(tablePO.getAuditInfo(), 
AuditInfo.class))
-          // TODO add field partition, distribution and sort order;
+          .withDistribution(
+              StringUtils.isBlank(tablePO.getDistribution())
+                  ? null
+                  : DTOConverters.fromDTO(
+                      JsonUtils.anyFieldMapper()
+                          .readValue(tablePO.getDistribution(), 
DistributionDTO.class)))
+          .withSortOrder(
+              StringUtils.isBlank(tablePO.getSortOrders())
+                  ? null
+                  : DTOConverters.fromDTOs(
+                      JsonUtils.anyFieldMapper()
+                          .readValue(tablePO.getSortOrders(), 
SortOrderDTO[].class)))
           .withIndexes(
               StringUtils.isBlank(tablePO.getIndexes())
                   ? null
-                  : JsonUtils.anyFieldMapper().readValue(tablePO.getIndexes(), 
IndexImpl[].class))
+                  : DTOConverters.fromDTOs(
+                      
JsonUtils.anyFieldMapper().readValue(tablePO.getIndexes(), IndexDTO[].class)))
+          // TODO add field partition, distribution and sort order;
+          .withPartitions(
+              StringUtils.isBlank(tablePO.getPartitions())
+                  ? null
+                  : JsonUtils.anyFieldMapper()
+                      .readValue(tablePO.getPartitions(), 
Partitioning[].class))
           .withFormat(tablePO.getFormat())
           .withComment(tablePO.getComment())
           .withProperties(
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 db9d0cce86..eea8837baf 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
@@ -65,7 +65,13 @@ import org.apache.gravitino.policy.Policy;
 import org.apache.gravitino.policy.PolicyContent;
 import org.apache.gravitino.policy.PolicyContents;
 import org.apache.gravitino.rel.expressions.Expression;
+import org.apache.gravitino.rel.expressions.NamedReference;
+import org.apache.gravitino.rel.expressions.distributions.Distributions;
+import org.apache.gravitino.rel.expressions.distributions.Strategy;
 import org.apache.gravitino.rel.expressions.literals.Literals;
+import org.apache.gravitino.rel.expressions.sorts.SortDirection;
+import org.apache.gravitino.rel.expressions.sorts.SortOrder;
+import org.apache.gravitino.rel.expressions.sorts.SortOrders;
 import org.apache.gravitino.rel.types.Type;
 import org.apache.gravitino.rel.types.Types;
 import org.apache.gravitino.stats.StatisticValues;
@@ -616,6 +622,15 @@ public class TestPOConverters {
     assertEquals(1, initPO.getCurrentVersion());
     assertEquals(1, initPO.getLastVersion());
     assertEquals(0, initPO.getDeletedAt());
+
+    TableEntity entity =
+        POConverters.fromTableAndColumnPOs(
+            initPO,
+            Lists.newArrayList(),
+            NamespaceUtil.ofTable("test_metalake", "test_catalog", 
"test_schema"));
+
+    Assertions.assertEquals(tableEntity.getDistribution(), 
entity.getDistribution());
+    Assertions.assertArrayEquals(tableEntity.getPartitions(), 
entity.getPartitions());
   }
 
   @Test
@@ -1382,6 +1397,9 @@ public class TestPOConverters {
         .withName(name)
         .withNamespace(namespace)
         .withColumns(columns)
+        .withDistribution(Distributions.of(Strategy.EVEN, 10, 
NamedReference.field("key")))
+        .withSortOrder(
+            new SortOrder[] {SortOrders.of(NamedReference.field("col1"), 
SortDirection.ASCENDING)})
         .withAuditInfo(auditInfo)
         .build();
   }

Reply via email to