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 a4023593e0 [#8215] connector(trino/flink): Support for reading/writing 
time precision (#8216)
a4023593e0 is described below

commit a4023593e07c1d7c2591e5b23a7cefdb20fd4355
Author: predator4ann <[email protected]>
AuthorDate: Fri Sep 5 20:41:51 2025 +0800

    [#8215] connector(trino/flink): Support for reading/writing time precision 
(#8216)
    
    ### Why are the changes needed?
    
    Fix: #8215
    
    ### Does this PR introduce _any_ user-facing change?
    
    No
    
    ### How was this patch tested?
    
    UT already added.
    
    ---------
    
    Signed-off-by: zacsun <[email protected]>
---
 .../gravitino/flink/connector/utils/TypeUtils.java | 53 +++++++++++--
 .../flink/connector/utils/TestTypeUtils.java       | 90 ++++++++++++++++++++--
 .../testsets/jdbc-postgresql/00006_datatype.txt    | 14 ++--
 .../catalog/hive/HiveDataTypeTransformer.java      | 17 ++++
 .../iceberg/IcebergDataTypeTransformer.java        |  9 +++
 .../jdbc/mysql/MySQLDataTypeTransformer.java       |  9 +++
 .../postgresql/PostgreSQLDataTypeTransformer.java  | 40 ++++++++--
 .../connector/util/GeneralDataTypeTransformer.java | 47 +++++++++--
 .../connector/util/TestDataTypeTransformer.java    |  6 +-
 9 files changed, 249 insertions(+), 36 deletions(-)

diff --git 
a/flink-connector/flink/src/main/java/org/apache/gravitino/flink/connector/utils/TypeUtils.java
 
b/flink-connector/flink/src/main/java/org/apache/gravitino/flink/connector/utils/TypeUtils.java
index 8766bea836..3ce11846d7 100644
--- 
a/flink-connector/flink/src/main/java/org/apache/gravitino/flink/connector/utils/TypeUtils.java
+++ 
b/flink-connector/flink/src/main/java/org/apache/gravitino/flink/connector/utils/TypeUtils.java
@@ -36,6 +36,13 @@ import org.apache.gravitino.rel.types.Types;
 
 public class TypeUtils {
 
+  // Flink supports time/timestamp precision from 0 to 9 (nanosecond 
precision).
+  // @see
+  // 
https://nightlies.apache.org/flink/flink-docs-release-2.1/docs/dev/table/types/#date-and-time
+  protected static final int FLINK_SECONDS_PRECISION = 0;
+  protected static final int FLINK_MICROS_PRECISION = 6;
+  protected static final int FLINK_NANOS_PRECISION = 9;
+
   private TypeUtils() {}
 
   public static Type toGravitinoType(LogicalType logicalType) {
@@ -68,9 +75,15 @@ public class TypeUtils {
       case DATE:
         return Types.DateType.get();
       case TIME_WITHOUT_TIME_ZONE:
-        return Types.TimeType.get();
+        org.apache.flink.table.types.logical.TimeType timeType =
+            (org.apache.flink.table.types.logical.TimeType) logicalType;
+        int timePrecision = timeType.getPrecision();
+        return Types.TimeType.of(timePrecision);
       case TIMESTAMP_WITHOUT_TIME_ZONE:
-        return Types.TimestampType.withoutTimeZone();
+        org.apache.flink.table.types.logical.TimestampType timestampType =
+            (org.apache.flink.table.types.logical.TimestampType) logicalType;
+        int timestampPrecision = timestampType.getPrecision();
+        return Types.TimestampType.withoutTimeZone(timestampPrecision);
       case INTERVAL_YEAR_MONTH:
         return Types.IntervalYearType.get();
       case INTERVAL_DAY_TIME:
@@ -78,8 +91,15 @@ public class TypeUtils {
       case FLOAT:
         return Types.FloatType.get();
       case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+        org.apache.flink.table.types.logical.LocalZonedTimestampType 
localZonedTimestampType =
+            (org.apache.flink.table.types.logical.LocalZonedTimestampType) 
logicalType;
+        int localZonedPrecision = localZonedTimestampType.getPrecision();
+        return Types.TimestampType.withTimeZone(localZonedPrecision);
       case TIMESTAMP_WITH_TIME_ZONE:
-        return Types.TimestampType.withTimeZone();
+        org.apache.flink.table.types.logical.ZonedTimestampType 
zonedTimestampType =
+            (org.apache.flink.table.types.logical.ZonedTimestampType) 
logicalType;
+        int zonedPrecision = zonedTimestampType.getPrecision();
+        return Types.TimestampType.withTimeZone(zonedPrecision);
       case ARRAY:
         ArrayType arrayType = (ArrayType) logicalType;
         Type elementType = toGravitinoType(arrayType.getElementType());
@@ -155,10 +175,20 @@ public class TypeUtils {
         return DataTypes.DATE();
       case TIMESTAMP:
         Types.TimestampType timestampType = (Types.TimestampType) 
gravitinoType;
+        int precision = FLINK_MICROS_PRECISION;
+        if (timestampType.hasPrecisionSet()) {
+          precision = timestampType.precision();
+          if (precision < FLINK_SECONDS_PRECISION || precision > 
FLINK_NANOS_PRECISION) {
+            throw new UnsupportedOperationException(
+                "Unsupported timestamp precision for Flink: "
+                    + precision
+                    + ". Flink supports precision from 0 to 9.");
+          }
+        }
         if (timestampType.hasTimeZone()) {
-          return DataTypes.TIMESTAMP_LTZ();
+          return DataTypes.TIMESTAMP_LTZ(precision);
         } else {
-          return DataTypes.TIMESTAMP();
+          return DataTypes.TIMESTAMP(precision);
         }
       case LIST:
         Types.ListType listType = (Types.ListType) gravitinoType;
@@ -188,7 +218,18 @@ public class TypeUtils {
       case NULL:
         return DataTypes.NULL();
       case TIME:
-        return DataTypes.TIME();
+        Types.TimeType timeType = (Types.TimeType) gravitinoType;
+        int timePrecision = FLINK_SECONDS_PRECISION;
+        if (timeType.hasPrecisionSet()) {
+          timePrecision = timeType.precision();
+          if (timePrecision < FLINK_SECONDS_PRECISION || timePrecision > 
FLINK_NANOS_PRECISION) {
+            throw new UnsupportedOperationException(
+                "Unsupported time precision for Flink: "
+                    + timePrecision
+                    + ". Flink supports precision from 0 to 9.");
+          }
+        }
+        return DataTypes.TIME(timePrecision);
       case INTERVAL_YEAR:
         return DataTypes.INTERVAL(DataTypes.YEAR());
       case INTERVAL_DAY:
diff --git 
a/flink-connector/flink/src/test/java/org/apache/gravitino/flink/connector/utils/TestTypeUtils.java
 
b/flink-connector/flink/src/test/java/org/apache/gravitino/flink/connector/utils/TestTypeUtils.java
index 8a9297cb51..438e5fbe37 100644
--- 
a/flink-connector/flink/src/test/java/org/apache/gravitino/flink/connector/utils/TestTypeUtils.java
+++ 
b/flink-connector/flink/src/test/java/org/apache/gravitino/flink/connector/utils/TestTypeUtils.java
@@ -69,13 +69,13 @@ public class TestTypeUtils {
     Assertions.assertEquals(Types.ByteType.get(), 
TypeUtils.toGravitinoType(new TinyIntType()));
     Assertions.assertEquals(Types.ShortType.get(), 
TypeUtils.toGravitinoType(new SmallIntType()));
     Assertions.assertEquals(
-        Types.TimestampType.withoutTimeZone(), TypeUtils.toGravitinoType(new 
TimestampType()));
+        Types.TimestampType.withoutTimeZone(6), TypeUtils.toGravitinoType(new 
TimestampType()));
     Assertions.assertEquals(
-        Types.TimestampType.withTimeZone(), TypeUtils.toGravitinoType(new 
ZonedTimestampType()));
+        Types.TimestampType.withTimeZone(6), TypeUtils.toGravitinoType(new 
ZonedTimestampType()));
     Assertions.assertEquals(
-        Types.TimestampType.withTimeZone(),
+        Types.TimestampType.withTimeZone(6),
         TypeUtils.toGravitinoType(new LocalZonedTimestampType()));
-    Assertions.assertEquals(Types.TimeType.get(), 
TypeUtils.toGravitinoType(new TimeType()));
+    Assertions.assertEquals(Types.TimeType.of(0), 
TypeUtils.toGravitinoType(new TimeType()));
     Assertions.assertEquals(
         Types.IntervalDayType.get(),
         TypeUtils.toGravitinoType(
@@ -132,11 +132,10 @@ public class TestTypeUtils {
     Assertions.assertEquals(DataTypes.BYTES(), 
TypeUtils.toFlinkType(Types.BinaryType.get()));
     Assertions.assertEquals(DataTypes.BINARY(10), 
TypeUtils.toFlinkType(Types.FixedType.of(10)));
     Assertions.assertEquals(
-        DataTypes.TIMESTAMP(6), 
TypeUtils.toFlinkType(Types.TimestampType.withoutTimeZone()));
+        DataTypes.TIMESTAMP(6), 
TypeUtils.toFlinkType(Types.TimestampType.withoutTimeZone(6)));
     Assertions.assertEquals(
-        DataTypes.TIMESTAMP_WITH_LOCAL_TIME_ZONE(6),
-        TypeUtils.toFlinkType(Types.TimestampType.withTimeZone()));
-    Assertions.assertEquals(DataTypes.TIME(), 
TypeUtils.toFlinkType(Types.TimeType.get()));
+        DataTypes.TIMESTAMP_LTZ(6), 
TypeUtils.toFlinkType(Types.TimestampType.withTimeZone(6)));
+    Assertions.assertEquals(DataTypes.TIME(0), 
TypeUtils.toFlinkType(Types.TimeType.of(0)));
     Assertions.assertEquals(
         DataTypes.INTERVAL(DataTypes.DAY()), 
TypeUtils.toFlinkType(Types.IntervalDayType.get()));
     Assertions.assertEquals(
@@ -164,4 +163,79 @@ public class TestTypeUtils {
         UnsupportedOperationException.class,
         () -> TypeUtils.toFlinkType(Types.UnparsedType.of("unknown")));
   }
+
+  @Test
+  public void testTimePrecisionConversion() {
+    // TIME
+    Assertions.assertEquals(Types.TimeType.of(0), 
TypeUtils.toGravitinoType(new TimeType(0)));
+    Assertions.assertEquals(Types.TimeType.of(3), 
TypeUtils.toGravitinoType(new TimeType(3)));
+    Assertions.assertEquals(Types.TimeType.of(6), 
TypeUtils.toGravitinoType(new TimeType(6)));
+    Assertions.assertEquals(Types.TimeType.of(9), 
TypeUtils.toGravitinoType(new TimeType(9)));
+
+    // Test converting back
+    Assertions.assertEquals(DataTypes.TIME(0), 
TypeUtils.toFlinkType(Types.TimeType.of(0)));
+    Assertions.assertEquals(DataTypes.TIME(3), 
TypeUtils.toFlinkType(Types.TimeType.of(3)));
+    Assertions.assertEquals(DataTypes.TIME(6), 
TypeUtils.toFlinkType(Types.TimeType.of(6)));
+    Assertions.assertEquals(DataTypes.TIME(9), 
TypeUtils.toFlinkType(Types.TimeType.of(9)));
+  }
+
+  @Test
+  public void testTimestampPrecisionConversion() {
+    // TIMESTAMP without timezone
+    Assertions.assertEquals(
+        Types.TimestampType.withoutTimeZone(0), TypeUtils.toGravitinoType(new 
TimestampType(0)));
+    Assertions.assertEquals(
+        Types.TimestampType.withoutTimeZone(3), TypeUtils.toGravitinoType(new 
TimestampType(3)));
+    Assertions.assertEquals(
+        Types.TimestampType.withoutTimeZone(6), TypeUtils.toGravitinoType(new 
TimestampType(6)));
+    Assertions.assertEquals(
+        Types.TimestampType.withoutTimeZone(9), TypeUtils.toGravitinoType(new 
TimestampType(9)));
+
+    // TIMESTAMP with timezone (LocalZoned)
+    Assertions.assertEquals(
+        Types.TimestampType.withTimeZone(0),
+        TypeUtils.toGravitinoType(new LocalZonedTimestampType(0)));
+    Assertions.assertEquals(
+        Types.TimestampType.withTimeZone(3),
+        TypeUtils.toGravitinoType(new LocalZonedTimestampType(3)));
+    Assertions.assertEquals(
+        Types.TimestampType.withTimeZone(6),
+        TypeUtils.toGravitinoType(new LocalZonedTimestampType(6)));
+    Assertions.assertEquals(
+        Types.TimestampType.withTimeZone(9),
+        TypeUtils.toGravitinoType(new LocalZonedTimestampType(9)));
+
+    // Test converting back
+    Assertions.assertEquals(
+        DataTypes.TIMESTAMP(0), 
TypeUtils.toFlinkType(Types.TimestampType.withoutTimeZone(0)));
+    Assertions.assertEquals(
+        DataTypes.TIMESTAMP(3), 
TypeUtils.toFlinkType(Types.TimestampType.withoutTimeZone(3)));
+    Assertions.assertEquals(
+        DataTypes.TIMESTAMP(6), 
TypeUtils.toFlinkType(Types.TimestampType.withoutTimeZone(6)));
+    Assertions.assertEquals(
+        DataTypes.TIMESTAMP(9), 
TypeUtils.toFlinkType(Types.TimestampType.withoutTimeZone(9)));
+
+    Assertions.assertEquals(
+        DataTypes.TIMESTAMP_LTZ(0), 
TypeUtils.toFlinkType(Types.TimestampType.withTimeZone(0)));
+    Assertions.assertEquals(
+        DataTypes.TIMESTAMP_LTZ(3), 
TypeUtils.toFlinkType(Types.TimestampType.withTimeZone(3)));
+    Assertions.assertEquals(
+        DataTypes.TIMESTAMP_LTZ(6), 
TypeUtils.toFlinkType(Types.TimestampType.withTimeZone(6)));
+    Assertions.assertEquals(
+        DataTypes.TIMESTAMP_LTZ(9), 
TypeUtils.toFlinkType(Types.TimestampType.withTimeZone(9)));
+  }
+
+  @Test
+  public void testInvalidPrecisionHandling() {
+    Assertions.assertThrows(
+        UnsupportedOperationException.class, () -> 
TypeUtils.toFlinkType(Types.TimeType.of(10)));
+
+    Assertions.assertThrows(
+        UnsupportedOperationException.class,
+        () -> TypeUtils.toFlinkType(Types.TimestampType.withoutTimeZone(10)));
+
+    Assertions.assertThrows(
+        UnsupportedOperationException.class,
+        () -> TypeUtils.toFlinkType(Types.TimestampType.withTimeZone(10)));
+  }
 }
diff --git 
a/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-postgresql/00006_datatype.txt
 
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-postgresql/00006_datatype.txt
index 36c36db470..e37cbee1a5 100644
--- 
a/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-postgresql/00006_datatype.txt
+++ 
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-postgresql/00006_datatype.txt
@@ -17,9 +17,9 @@ CREATE TABLE
    f11 integer,
    f12 bigint,
    f13 date,
-   f14 time(6),
-   f15 timestamp(6),
-   f16 timestamp(6) with time zone
+   f14 time(3),
+   f15 timestamp(3),
+   f16 timestamp(3) with time zone
 )
 COMMENT ''"
 
@@ -27,7 +27,7 @@ INSERT: 1 row
 
 INSERT: 1 row
 
-"Sample text 1","Text1               
","65","123.456","7.89","12.34","false","100","1000","1000","100000","2024-01-01","08:00:00.000000","2024-01-01
 08:00:00.000000","2024-01-01 08:00:00.000000 UTC"
+"Sample text 1","Text1               
","65","123.456","7.89","12.34","false","100","1000","1000","100000","2024-01-01","08:00:00.000","2024-01-01
 08:00:00.000","2024-01-01 08:00:00.000 UTC"
 "","","","","","","","","","","","","","",""
 
 CREATE TABLE
@@ -45,9 +45,9 @@ CREATE TABLE
    f11 integer NOT NULL,
    f12 bigint NOT NULL,
    f13 date NOT NULL,
-   f14 time(6) NOT NULL,
-   f15 timestamp(6) NOT NULL,
-   f16 timestamp(6) with time zone NOT NULL
+   f14 time(3) NOT NULL,
+   f15 timestamp(3) NOT NULL,
+   f16 timestamp(3) with time zone NOT NULL
 )
 COMMENT ''"
 
diff --git 
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/hive/HiveDataTypeTransformer.java
 
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/hive/HiveDataTypeTransformer.java
index f047f61d5e..a01d3fe3ac 100644
--- 
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/hive/HiveDataTypeTransformer.java
+++ 
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/hive/HiveDataTypeTransformer.java
@@ -20,6 +20,7 @@
 package org.apache.gravitino.trino.connector.catalog.hive;
 
 import io.trino.spi.TrinoException;
+import io.trino.spi.type.TimestampType;
 import io.trino.spi.type.VarcharType;
 import org.apache.gravitino.rel.types.Type;
 import org.apache.gravitino.rel.types.Types;
@@ -39,6 +40,8 @@ public class HiveDataTypeTransformer extends 
GeneralDataTypeTransformer {
       throw new TrinoException(
           GravitinoErrorCode.GRAVITINO_UNSUPPORTED_GRAVITINO_DATATYPE,
           "Unsupported gravitino datatype: " + type);
+    } else if (Type.Name.TIMESTAMP == type.name()) {
+      return TimestampType.TIMESTAMP_MILLIS;
     }
     return super.getTrinoType(type);
   }
@@ -72,6 +75,20 @@ public class HiveDataTypeTransformer extends 
GeneralDataTypeTransformer {
       }
 
       return Types.FixedCharType.of(charType.getLength());
+    } else if 
(io.trino.spi.type.TimestampType.class.isAssignableFrom(typeClass)) {
+      // When creating a table in Hive, the timestamp data type only supports 
timestamp and
+      // timestamp(3)
+      // with the precision being 3 (milliseconds precision)
+      io.trino.spi.type.TimestampType timestampType = 
(io.trino.spi.type.TimestampType) type;
+      int precision = timestampType.getPrecision();
+      if (precision != TRINO_MILLIS_PRECISION) {
+        throw new TrinoException(
+            GravitinoErrorCode.GRAVITINO_ILLEGAL_ARGUMENT,
+            "Incorrect timestamp precision for timestamp("
+                + precision
+                + "), the configured precision is MILLISECONDS;");
+      }
+      return Types.TimestampType.withoutTimeZone();
     }
 
     return super.getGravitinoType(type);
diff --git 
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/iceberg/IcebergDataTypeTransformer.java
 
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/iceberg/IcebergDataTypeTransformer.java
index 2c4fa12f8f..042a61f3a1 100644
--- 
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/iceberg/IcebergDataTypeTransformer.java
+++ 
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/iceberg/IcebergDataTypeTransformer.java
@@ -49,6 +49,15 @@ public class IcebergDataTypeTransformer extends 
GeneralDataTypeTransformer {
             "Iceberg does not support the datatype VARCHAR with length");
       }
       return Types.StringType.get();
+    } else if (io.trino.spi.type.TimeType.class.isAssignableFrom(typeClass)) {
+      // Iceberg only supports time type with microsecond (6) precision
+      return Types.TimeType.of(TRINO_MICROS_PRECISION);
+    } else if 
(io.trino.spi.type.TimestampType.class.isAssignableFrom(typeClass)) {
+      // Iceberg only supports timestamp type (without time zone) with 
microsecond (6) precision
+      return Types.TimestampType.withoutTimeZone(TRINO_MICROS_PRECISION);
+    } else if 
(io.trino.spi.type.TimestampWithTimeZoneType.class.isAssignableFrom(typeClass)) 
{
+      // Iceberg only supports timestamp with time zone type with microsecond 
(6) precision
+      return Types.TimestampType.withTimeZone(TRINO_MICROS_PRECISION);
     }
 
     return super.getGravitinoType(type);
diff --git 
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/jdbc/mysql/MySQLDataTypeTransformer.java
 
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/jdbc/mysql/MySQLDataTypeTransformer.java
index 920793d2e9..b6ecf8696d 100644
--- 
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/jdbc/mysql/MySQLDataTypeTransformer.java
+++ 
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/jdbc/mysql/MySQLDataTypeTransformer.java
@@ -103,6 +103,15 @@ public class MySQLDataTypeTransformer extends 
GeneralDataTypeTransformer {
       return Types.VarCharType.of(length);
     } else if (typeClass == JSON_TYPE.getClass()) {
       return 
Types.ExternalType.of(MySQLExternalDataType.JSON.getMysqlTypeName());
+    } else if (io.trino.spi.type.TimeType.class.isAssignableFrom(typeClass)) {
+      // MySQL only supports time type with second (0) precision
+      return Types.TimeType.of(TRINO_SECONDS_PRECISION);
+    } else if 
(io.trino.spi.type.TimestampType.class.isAssignableFrom(typeClass)) {
+      // MySQL only supports timestamp type (without time zone) with second 
(0) precision
+      return Types.TimestampType.withoutTimeZone(TRINO_SECONDS_PRECISION);
+    } else if 
(io.trino.spi.type.TimestampWithTimeZoneType.class.isAssignableFrom(typeClass)) 
{
+      // MySQL only supports timestamp with time zone type with second (0) 
precision
+      return Types.TimestampType.withTimeZone(TRINO_SECONDS_PRECISION);
     }
 
     return super.getGravitinoType(type);
diff --git 
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/jdbc/postgresql/PostgreSQLDataTypeTransformer.java
 
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/jdbc/postgresql/PostgreSQLDataTypeTransformer.java
index 0134fbacc8..cd04413e32 100644
--- 
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/jdbc/postgresql/PostgreSQLDataTypeTransformer.java
+++ 
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/jdbc/postgresql/PostgreSQLDataTypeTransformer.java
@@ -45,13 +45,43 @@ public class PostgreSQLDataTypeTransformer extends 
GeneralDataTypeTransformer {
       return io.trino.spi.type.VarcharType.createUnboundedVarcharType();
     } else if (Name.TIMESTAMP == type.name()) {
       Types.TimestampType timestampType = (Types.TimestampType) type;
-      if (timestampType.hasTimeZone()) {
-        return TimestampWithTimeZoneType.TIMESTAMP_TZ_MICROS;
-      } else {
-        return TimestampType.TIMESTAMP_MICROS;
+      boolean hasTimeZone = timestampType.hasTimeZone();
+      if (timestampType.hasPrecisionSet()) {
+        int precision = timestampType.precision();
+        if (precision >= TRINO_SECONDS_PRECISION && precision <= 
TRINO_PICOS_PRECISION) {
+          // Exceeding precision will be reduced to the maximum allowed 
precision of 6 (microseconds
+          // precision)
+          precision = Math.min(TRINO_MICROS_PRECISION, precision);
+          return hasTimeZone
+              ? 
TimestampWithTimeZoneType.createTimestampWithTimeZoneType(precision)
+              : TimestampType.createTimestampType(precision);
+        } else {
+          throw new TrinoException(
+              GravitinoErrorCode.GRAVITINO_UNSUPPORTED_GRAVITINO_DATATYPE,
+              "Unsupported timestamp precision for PostgreSQL: " + precision);
+        }
       }
+      // When precision is not set, the default precision is 3 (milliseconds 
precision)
+      return hasTimeZone
+          ? TimestampWithTimeZoneType.TIMESTAMP_TZ_MILLIS
+          : TimestampType.TIMESTAMP_MILLIS;
     } else if (Name.TIME == type.name()) {
-      return TimeType.TIME_MICROS;
+      Types.TimeType timeType = (Types.TimeType) type;
+      if (timeType.hasPrecisionSet()) {
+        int precision = timeType.precision();
+        if (precision >= TRINO_SECONDS_PRECISION && precision <= 
TRINO_PICOS_PRECISION) {
+          // Exceeding precision will be reduced to the maximum allowed 
precision of 6 (microseconds
+          // precision)
+          precision = Math.min(TRINO_MICROS_PRECISION, precision);
+          return TimeType.createTimeType(precision);
+        } else {
+          throw new TrinoException(
+              GravitinoErrorCode.GRAVITINO_UNSUPPORTED_GRAVITINO_DATATYPE,
+              "Unsupported time precision for PostgreSQL: " + precision);
+        }
+      }
+      // When precision is not set, the default precision is 3 (milliseconds 
precision)
+      return TimeType.TIME_MILLIS;
     }
 
     return super.getTrinoType(type);
diff --git 
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/util/GeneralDataTypeTransformer.java
 
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/util/GeneralDataTypeTransformer.java
index 4c972dd917..90c5222512 100644
--- 
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/util/GeneralDataTypeTransformer.java
+++ 
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/util/GeneralDataTypeTransformer.java
@@ -55,6 +55,14 @@ import 
org.apache.gravitino.trino.connector.GravitinoErrorCode;
 /** This class is used to transform datatype between Apache Gravitino and 
Trino */
 public class GeneralDataTypeTransformer {
 
+  // Trino supports time/timestamp precision from 0 to 12 (picoseconds 
precision).
+  // @see
+  // https://trino.io/docs/current/language/types.html#date-and-time
+  protected static final int TRINO_SECONDS_PRECISION = 0;
+  protected static final int TRINO_MILLIS_PRECISION = 3;
+  protected static final int TRINO_MICROS_PRECISION = 6;
+  protected static final int TRINO_PICOS_PRECISION = 12;
+
   /**
    * Transforms a Gravitino datatype to a Trino datatype.
    *
@@ -108,13 +116,38 @@ public class GeneralDataTypeTransformer {
       case DATE:
         return DATE;
       case TIME:
+        Types.TimeType timeType = (Types.TimeType) type;
+        if (timeType.hasPrecisionSet()) {
+          int precision = timeType.precision();
+          if (precision >= TRINO_SECONDS_PRECISION && precision <= 
TRINO_PICOS_PRECISION) {
+            return TimeType.createTimeType(precision);
+          } else {
+            throw new TrinoException(
+                GravitinoErrorCode.GRAVITINO_UNSUPPORTED_GRAVITINO_DATATYPE,
+                "Unsupported time precision for Trino: " + precision);
+          }
+        }
+        // default millis (precision 3) to match Trino default precision
         return TimeType.TIME_MILLIS;
       case TIMESTAMP:
-        if (((Types.TimestampType) type).hasTimeZone()) {
-          return TimestampWithTimeZoneType.TIMESTAMP_TZ_MILLIS;
-        } else {
-          return TimestampType.TIMESTAMP_MILLIS;
+        Types.TimestampType timestampType = (Types.TimestampType) type;
+        boolean hasTimeZone = timestampType.hasTimeZone();
+        if (timestampType.hasPrecisionSet()) {
+          int precision = timestampType.precision();
+          if (precision >= TRINO_SECONDS_PRECISION && precision <= 
TRINO_PICOS_PRECISION) {
+            return hasTimeZone
+                ? 
TimestampWithTimeZoneType.createTimestampWithTimeZoneType(precision)
+                : TimestampType.createTimestampType(precision);
+          } else {
+            throw new TrinoException(
+                GravitinoErrorCode.GRAVITINO_UNSUPPORTED_GRAVITINO_DATATYPE,
+                "Unsupported timestamp precision for Trino: " + precision);
+          }
         }
+        // default millis (precision 3) to match Trino default precision
+        return hasTimeZone
+            ? TimestampWithTimeZoneType.TIMESTAMP_TZ_MILLIS
+            : TimestampType.TIMESTAMP_MILLIS;
       case LIST:
         return new ArrayType(getTrinoType(((Types.ListType) 
type).elementType()));
       case MAP:
@@ -191,11 +224,11 @@ public class GeneralDataTypeTransformer {
     } else if (typeClass == io.trino.spi.type.DateType.class) {
       return Types.DateType.get();
     } else if (io.trino.spi.type.TimeType.class.isAssignableFrom(typeClass)) {
-      return Types.TimeType.get();
+      return Types.TimeType.of(((TimeType) type).getPrecision());
     } else if 
(io.trino.spi.type.TimestampType.class.isAssignableFrom(typeClass)) {
-      return Types.TimestampType.withoutTimeZone();
+      return Types.TimestampType.withoutTimeZone(((TimestampType) 
type).getPrecision());
     } else if 
(io.trino.spi.type.TimestampWithTimeZoneType.class.isAssignableFrom(typeClass)) 
{
-      return Types.TimestampType.withTimeZone();
+      return Types.TimestampType.withTimeZone(((TimestampWithTimeZoneType) 
type).getPrecision());
     } else if (typeClass == io.trino.spi.type.ArrayType.class) {
       // Ignore nullability for the type, we could only get nullability from 
column metadata
       return Types.ListType.of(
diff --git 
a/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/util/TestDataTypeTransformer.java
 
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/util/TestDataTypeTransformer.java
index b5548170a5..777713b98d 100644
--- 
a/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/util/TestDataTypeTransformer.java
+++ 
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/util/TestDataTypeTransformer.java
@@ -88,13 +88,13 @@ public class TestDataTypeTransformer {
     Assertions.assertEquals(
         dataTypeTransformer.getGravitinoType(DateType.DATE), 
Types.DateType.get());
     Assertions.assertEquals(
-        dataTypeTransformer.getGravitinoType(TimeType.TIME_MILLIS), 
Types.TimeType.get());
+        dataTypeTransformer.getGravitinoType(TimeType.TIME_MILLIS), 
Types.TimeType.of(3));
     Assertions.assertEquals(
         dataTypeTransformer.getGravitinoType(TimestampType.TIMESTAMP_MILLIS),
-        Types.TimestampType.withoutTimeZone());
+        Types.TimestampType.withoutTimeZone(3));
     Assertions.assertEquals(
         
dataTypeTransformer.getGravitinoType(TimestampWithTimeZoneType.TIMESTAMP_TZ_MILLIS),
-        Types.TimestampType.withTimeZone());
+        Types.TimestampType.withTimeZone(3));
 
     Assertions.assertEquals(
         dataTypeTransformer.getGravitinoType(new 
ArrayType(IntegerType.INTEGER)),

Reply via email to