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 7a4f230f3e [#12903] fix(trino-connector): Keep MySQL time/timestamp
precision in the Trino connector (#12906)
7a4f230f3e is described below
commit 7a4f230f3ef94356ade7b3890e1a0212f50f2dae
Author: qbhan <[email protected]>
AuthorDate: Mon Oct 12 14:40:50 2026 +0800
[#12903] fix(trino-connector): Keep MySQL time/timestamp precision in the
Trino connector (#12906)
### What changes were proposed in this pull request?
Four commits:
1. **Read path** (`MySQLDataTypeTransformer.getTrinoType`): map the
precision reported by the MySQL catalog to the Trino type with the same
precision, capped at 6.
2. **MySQL catalog** (`MysqlTableOperations`): add a
`renderDefaultValue` hook to `JdbcTableOperations` and render
`CURRENT_TIMESTAMP(p)` for columns with a precision, which MySQL
requires.
3. **Write path** (`MySQLDataTypeTransformer.getGravitinoType`): pass
the Trino precision through, capped at 6, and implement
`ConnectorMetadata.getSupportedType` through a new
`GeneralDataTypeTransformer.getSupportedType` hook so that CTAS from a
source with a precision above 6 is coerced by Trino instead of failing
when the pages are written.
4. **MySQL catalog**: when the JDBC driver cannot report the fractional
seconds precision (MySQL Connector/J before 8.0.16, where
`calculateDatetimePrecision` returns null), read it from
`information_schema.columns.COLUMN_TYPE`. This reuses the
`information_schema` query #13036 added for lossy types and only runs
when a `TIME`/`DATETIME`/`TIMESTAMP` column has an unknown precision, so
a current driver pays nothing. Rows are grouped by their exact
`TABLE_SCHEMA` so that with `lower_case_table_names=0` two schemas
differing only in case are told apart, while with
`lower_case_table_names=1` a request whose case differs from the stored
name still resolves.
Tests: integration testset `00016_datetime_precision`, updated
`00006`/`00011` expectations and the `trino-452-446` patch;
`CatalogMysqlIT.testColumnDefaultValueWithDatetimePrecision` (create
table and add column with `CURRENT_TIMESTAMP` defaults on precision
columns);
`TestMysqlTableOperations.testLoadDatetimePrecisionWithoutDriverSupport`
(simulates the old driver by overriding `calculateDatetimePrecision`);
unit tests in `TestMySQLDataTypeTransformer`,
`TestGravitinoMetadataGetSupportedType` and
`TestMysqlTableOperationsSqlGeneration`. The type mapping is documented
in `docs/trino-connector/catalog-mysql.md` and the driver note in
`docs/jdbc-mysql-catalog.md` is updated.
### Why are the changes needed?
The Gravitino Trino connector mapped every MySQL
`TIME`/`DATETIME`/`TIMESTAMP` column to precision 0 in both directions.
Reading a column with fractional seconds failed with `Expected 0s for
digits beyond precision 0`, and tables created through Trino silently
lost the requested precision. The native Trino MySQL connector maps the
precision exactly in both directions; this PR aligns the Gravitino
connector with it.
Passing the precision through on the write side requires the MySQL
catalog to render `CURRENT_TIMESTAMP(p)`, since `DATETIME(6) DEFAULT
CURRENT_TIMESTAMP` is rejected by MySQL with `Invalid default value`.
The precision the catalog reported used to depend on the driver version:
with Connector/J before 8.0.16 it was unknown, the connector fell back
to precision 0 while a table created through Trino got `DATETIME(3)`,
and reading it back failed. Reading the precision from
`information_schema` removes that dependency.
Fix: #12903
### Does this PR introduce _any_ user-facing change?
Yes.
- MySQL columns with fractional seconds are exposed as `time(p)` /
`timestamp(p)` / `timestamp(p) with time zone` instead of precision 0.
- `TIMESTAMP` without a precision, which is `timestamp(3)` in Trino, now
creates a `DATETIME(3)` column instead of `DATETIME`, matching the
native Trino MySQL connector. The same applies to `TIME` and `TIMESTAMP
WITH TIME ZONE`.
- The fractional seconds precision reported by the MySQL catalog no
longer depends on the MySQL Connector/J version.
### How was this patch tested?
- Unit tests listed above; `./gradlew
:trino-connector:trino-connector:test :catalogs:catalog-jdbc-mysql:test
-PskipITs` passes.
- New Docker tests
`CatalogMysqlIT.testColumnDefaultValueWithDatetimePrecision` and
`TestMysqlTableOperations.testLoadDatetimePrecisionWithoutDriverSupport`.
- Integration testset `00016_datetime_precision` covering read of a
MySQL table with `TIME/DATETIME/TIMESTAMP(3|6)` columns,
create/insert/CTAS through Trino, and CTAS from a precision-9 source;
updated `00006`/`00011` expectations and the `trino-452-446` patch.
- Verified against MySQL 8.0.46 that `DATETIME(6) DEFAULT
CURRENT_TIMESTAMP` is rejected while `CURRENT_TIMESTAMP(6)` is accepted,
that `information_schema.columns.COLUMN_TYPE` carries the precision
(`datetime(6)`, `time(3)`, bare `datetime` for 0), and that the schema
resolution behaves as described with `lower_case_table_names` set to 0
and to 1.
---
.../converter/JdbcColumnDefaultValueConverter.java | 4 +-
.../jdbc/operation/JdbcTableOperations.java | 15 ++-
.../mysql/operation/MysqlTableOperations.java | 122 +++++++++++++++----
.../mysql/integration/test/CatalogMysqlIT.java | 69 +++++++++++
.../mysql/operation/TestMysqlTableOperations.java | 59 ++++++++++
.../TestMysqlTableOperationsSqlGeneration.java | 63 ++++++++++
docs/jdbc-mysql-catalog.md | 22 +---
docs/trino-connector/catalog-mysql.md | 14 +++
.../docker-script/init/mysql/init.sql | 21 ++++
.../testsets/jdbc-mysql/00006_datatype.txt | 14 +--
.../jdbc-mysql/00011_column_properties.txt | 14 +--
.../jdbc-mysql/00016_datetime_precision.sql | 41 +++++++
.../jdbc-mysql/00016_datetime_precision.txt | 54 +++++++++
.../trino-ci-testset/testsets/trino-452-446.patch | 37 ++++--
.../trino/connector/GravitinoMetadata.java | 6 +
.../jdbc/mysql/MySQLDataTypeTransformer.java | 63 ++++++++--
.../connector/util/GeneralDataTypeTransformer.java | 12 ++
.../TestGravitinoMetadataGetSupportedType.java | 66 +++++++++++
.../jdbc/mysql/TestMySQLDataTypeTransformer.java | 131 +++++++++++++++++++++
19 files changed, 746 insertions(+), 81 deletions(-)
diff --git
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/converter/JdbcColumnDefaultValueConverter.java
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/converter/JdbcColumnDefaultValueConverter.java
index f2fa7d85b0..44c14eb0a9 100644
---
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/converter/JdbcColumnDefaultValueConverter.java
+++
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/converter/JdbcColumnDefaultValueConverter.java
@@ -34,7 +34,9 @@ import org.apache.gravitino.rel.types.Types;
public class JdbcColumnDefaultValueConverter {
- protected static final String CURRENT_TIMESTAMP = "CURRENT_TIMESTAMP";
+ /** The DDL rendering of the current timestamp default value function. */
+ public static final String CURRENT_TIMESTAMP = "CURRENT_TIMESTAMP";
+
protected static final String NULL = "NULL";
protected static final DateTimeFormatter DATE_TIME_FORMATTER =
new DateTimeFormatterBuilder()
diff --git
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/operation/JdbcTableOperations.java
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/operation/JdbcTableOperations.java
index 00cf4a9111..3aaf10b881 100644
---
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/operation/JdbcTableOperations.java
+++
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/operation/JdbcTableOperations.java
@@ -63,6 +63,7 @@ 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.Indexes;
+import org.apache.gravitino.rel.types.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -115,10 +116,22 @@ public abstract class JdbcTableOperations implements
TableOperation {
if (DEFAULT_VALUE_NOT_SET.equals(column.defaultValue())) {
return;
}
- String defaultValue =
columnDefaultValueConverter.fromGravitino(column.defaultValue());
+ String defaultValue = renderDefaultValue(column.dataType(),
column.defaultValue());
sqlBuilder.append("DEFAULT ").append(defaultValue).append(SPACE);
}
+ /**
+ * Renders a column default value for DDL. Subclasses override it when the
rendering depends on
+ * the column type.
+ *
+ * @param type the column type.
+ * @param defaultValue the default value, never {@link
Column#DEFAULT_VALUE_NOT_SET}.
+ * @return the default value as it appears after the DEFAULT keyword.
+ */
+ protected String renderDefaultValue(Type type, Expression defaultValue) {
+ return columnDefaultValueConverter.fromGravitino(defaultValue);
+ }
+
@Override
public void create(
String databaseName,
diff --git
a/catalogs/catalog-jdbc-mysql/src/main/java/org/apache/gravitino/catalog/mysql/operation/MysqlTableOperations.java
b/catalogs/catalog-jdbc-mysql/src/main/java/org/apache/gravitino/catalog/mysql/operation/MysqlTableOperations.java
index 0417d05d08..7f80ceba9c 100644
---
a/catalogs/catalog-jdbc-mysql/src/main/java/org/apache/gravitino/catalog/mysql/operation/MysqlTableOperations.java
+++
b/catalogs/catalog-jdbc-mysql/src/main/java/org/apache/gravitino/catalog/mysql/operation/MysqlTableOperations.java
@@ -18,6 +18,7 @@
*/
package org.apache.gravitino.catalog.mysql.operation;
+import static
org.apache.gravitino.catalog.jdbc.converter.JdbcColumnDefaultValueConverter.CURRENT_TIMESTAMP;
import static
org.apache.gravitino.catalog.jdbc.utils.JdbcConnectorUtils.escapeSqlLiteral;
import static
org.apache.gravitino.catalog.mysql.MysqlTablePropertiesMetadata.MYSQL_AUTO_INCREMENT_OFFSET_KEY;
import static
org.apache.gravitino.catalog.mysql.MysqlTablePropertiesMetadata.MYSQL_ENGINE_KEY;
@@ -52,6 +53,7 @@ import org.apache.gravitino.exceptions.NoSuchColumnException;
import org.apache.gravitino.exceptions.NoSuchTableException;
import org.apache.gravitino.rel.Column;
import org.apache.gravitino.rel.TableChange;
+import org.apache.gravitino.rel.expressions.Expression;
import org.apache.gravitino.rel.expressions.distributions.Distribution;
import org.apache.gravitino.rel.expressions.distributions.Distributions;
import org.apache.gravitino.rel.expressions.transforms.Transform;
@@ -218,10 +220,12 @@ public class MysqlTableOperations extends
JdbcTableOperations {
}
/**
- * VARBINARY/ENUM/SET fall back to a bare Types.ExternalType, and BIT/BINARY
of any width both
- * collapse to plain Types.BinaryType, because JdbcTypeConverter only sees
TYPE_NAME/COLUMN_SIZE.
- * If any column needs it, this queries information_schema.columns once for
the whole table to
- * recover the full declaration (e.g. "enum('a','b','c')", "binary(16)") and
fixes those columns.
+ * VARBINARY/ENUM/SET fall back to a bare Types.ExternalType, BIT/BINARY of
any width both
+ * collapse to plain Types.BinaryType, and the fractional seconds precision
of TIME/DATETIME/
+ * TIMESTAMP is unknown when the driver predates 8.0.16, because
JdbcTypeConverter only sees
+ * TYPE_NAME/COLUMN_SIZE. If any column needs it, this queries
information_schema.columns once for
+ * the whole table to recover the full declaration (e.g.
"enum('a','b','c')", "binary(16)",
+ * "datetime(6)") and fixes those columns.
*/
private static void correctLossyTypeColumns(
Connection connection, String databaseName, String tableName,
JdbcTable.Builder tableBuilder)
@@ -247,7 +251,7 @@ public class MysqlTableOperations extends
JdbcTableOperations {
private static boolean isLossyTypeCandidate(Column column) {
Type type = column.dataType();
- if (type instanceof Types.BinaryType) {
+ if (type instanceof Types.BinaryType || hasUnknownDatetimePrecision(type))
{
return true;
}
if (type instanceof Types.ExternalType) {
@@ -263,13 +267,47 @@ public class MysqlTableOperations extends
JdbcTableOperations {
if (StringUtils.isEmpty(fullType)) {
return column;
}
+ Type type = column.dataType();
+ if (hasUnknownDatetimePrecision(type)) {
+ // MySQL leaves the precision out of the declaration when it is 0, e.g.
"datetime".
+ return copyWithType(column, withDatetimePrecision(type,
parseLength(fullType, 0)));
+ }
// A plain BINARY(1) (or unspecified length) already round-trips to
Types.BinaryType as-is.
- if (column.dataType() instanceof Types.BinaryType && parseLength(fullType)
<= 1) {
+ if (type instanceof Types.BinaryType && parseLength(fullType, 1) <= 1) {
return column;
}
+ return copyWithType(column, Types.ExternalType.of(fullType));
+ }
+
+ private static boolean hasUnknownDatetimePrecision(Type type) {
+ if (type instanceof Types.TimeType) {
+ return !((Types.TimeType) type).hasPrecisionSet();
+ }
+ if (type instanceof Types.TimestampType) {
+ return !((Types.TimestampType) type).hasPrecisionSet();
+ }
+ return false;
+ }
+
+ private static Type withDatetimePrecision(Type type, int precision) {
+ if (type instanceof Types.TimeType) {
+ return Types.TimeType.of(precision);
+ }
+ return ((Types.TimestampType) type).hasTimeZone()
+ ? Types.TimestampType.withTimeZone(precision)
+ : Types.TimestampType.withoutTimeZone(precision);
+ }
+
+ /** Parses the "(n)" of a column declaration such as "binary(16)" or
"datetime(6)". */
+ private static int parseLength(String fullType, int defaultLength) {
+ Matcher matcher = LENGTH_PATTERN.matcher(fullType);
+ return matcher.find() ? Integer.parseInt(matcher.group(1)) : defaultLength;
+ }
+
+ private static JdbcColumn copyWithType(JdbcColumn column, Type type) {
return JdbcColumn.builder()
.withName(column.name())
- .withType(Types.ExternalType.of(fullType))
+ .withType(type)
.withComment(column.comment())
.withNullable(column.nullable())
.withAutoIncrement(column.autoIncrement())
@@ -277,19 +315,16 @@ public class MysqlTableOperations extends
JdbcTableOperations {
.build();
}
- private static int parseLength(String fullType) {
- Matcher matcher = LENGTH_PATTERN.matcher(fullType);
- return matcher.find() ? Integer.parseInt(matcher.group(1)) : 1;
- }
-
private static Map<String, String> fetchColumnFullTypes(
Connection connection, String databaseName, String tableName) throws
SQLException {
- Map<String, String> columnTypes = new HashMap<>();
- // TABLE_SCHEMA/TABLE_NAME comparisons in information_schema use a
case-insensitive collation,
- // so with lower_case_table_names=0 a schema holding both "a_b" and "A_B"
would return rows for
- // both; re-check TABLE_NAME exactly, matching the pattern used in
getColumnBuilder.
+ // TABLE_SCHEMA/TABLE_NAME comparisons in information_schema use a
case-insensitive collation.
+ // TABLE_NAME is re-checked exactly, matching the pattern used in
getColumnBuilder. The rows are
+ // grouped by their exact TABLE_SCHEMA: with lower_case_table_names=0 both
"a_b" and "A_B" may
+ // exist and only the exact one is wanted, while with
lower_case_table_names=1 the stored name
+ // may differ in case from the requested one and is then the only
candidate.
+ Map<String, Map<String, String>> columnTypesBySchema = new HashMap<>();
String query =
- "SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE FROM
information_schema.columns "
+ "SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_TYPE FROM
information_schema.columns "
+ "WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?";
try (PreparedStatement statement = connection.prepareStatement(query)) {
statement.setString(1, databaseName);
@@ -297,12 +332,29 @@ public class MysqlTableOperations extends
JdbcTableOperations {
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
if (Objects.equals(resultSet.getString("TABLE_NAME"), tableName)) {
- columnTypes.put(resultSet.getString("COLUMN_NAME"),
resultSet.getString("COLUMN_TYPE"));
+ columnTypesBySchema
+ .computeIfAbsent(resultSet.getString("TABLE_SCHEMA"), schema
-> new HashMap<>())
+ .put(resultSet.getString("COLUMN_NAME"),
resultSet.getString("COLUMN_TYPE"));
}
}
}
}
- return columnTypes;
+ Map<String, String> exactSchemaTypes =
columnTypesBySchema.get(databaseName);
+ if (exactSchemaTypes != null) {
+ return exactSchemaTypes;
+ }
+ if (columnTypesBySchema.size() == 1) {
+ return columnTypesBySchema.values().iterator().next();
+ }
+ if (columnTypesBySchema.size() > 1) {
+ LOG.warn(
+ "Schemas {} differ only in case from {} and none matches exactly,
leaving the column "
+ + "types of {} as reported by the driver",
+ columnTypesBySchema.keySet(),
+ databaseName,
+ tableName);
+ }
+ return Collections.emptyMap();
}
@Override
@@ -412,6 +464,29 @@ public class MysqlTableOperations extends
JdbcTableOperations {
return result;
}
+ /**
+ * {@inheritDoc}
+ *
+ * <p>MySQL requires the fractional seconds precision of CURRENT_TIMESTAMP
to match the one of the
+ * column: {@code DATETIME(6) DEFAULT CURRENT_TIMESTAMP} is rejected while
{@code DATETIME(6)
+ * DEFAULT CURRENT_TIMESTAMP(6)} is accepted. Columns without a precision,
or with precision 0,
+ * keep the bare CURRENT_TIMESTAMP.
+ *
+ * @see <a
href="https://dev.mysql.com/doc/refman/8.0/en/timestamp-initialization.html">Automatic
+ * Initialization and Updating for TIMESTAMP and DATETIME</a>
+ */
+ @Override
+ protected String renderDefaultValue(Type type, Expression defaultValue) {
+ String rendered = super.renderDefaultValue(type, defaultValue);
+ if (CURRENT_TIMESTAMP.equals(rendered) && type instanceof
Types.TimestampType) {
+ int precision = ((Types.TimestampType) type).precision();
+ if (precision > 0) {
+ return String.format("%s(%d)", rendered, precision);
+ }
+ }
+ return rendered;
+ }
+
private String updateColumnAutoIncrementDefinition(
JdbcTable table, TableChange.UpdateColumnAutoIncrement change) {
if (change.fieldName().length > 1) {
@@ -571,7 +646,7 @@ public class MysqlTableOperations extends
JdbcTableOperations {
if (!Column.DEFAULT_VALUE_NOT_SET.equals(addColumn.getDefaultValue())) {
columnDefinition
.append("DEFAULT ")
-
.append(columnDefaultValueConverter.fromGravitino(addColumn.getDefaultValue()))
+ .append(renderDefaultValue(addColumn.getDataType(),
addColumn.getDefaultValue()))
.append(SPACE);
}
@@ -797,11 +872,10 @@ public class MysqlTableOperations extends
JdbcTableOperations {
String driverVersion = getMySQLDriverVersion();
if (driverVersion != null &&
!isMySQLDriverVersionSupported(driverVersion)) {
LOG.warn(
- "MySQL driver version {} is below 8.0.16, columnSize may not be
accurate for precision calculation. "
- + "Returning null for {} type precision. Driver version: {}",
+ "MySQL driver version {} is below 8.0.16, columnSize may not be
accurate for precision "
+ + "calculation. Falling back to information_schema for the {}
precision.",
driverVersion,
- upperTypeName,
- driverVersion);
+ upperTypeName);
return null;
}
}
diff --git
a/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlIT.java
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlIT.java
index b0a8e08139..6fef5372f9 100644
---
a/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlIT.java
+++
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlIT.java
@@ -535,6 +535,75 @@ public class CatalogMysqlIT extends BaseIT {
createdTable.columns()[4].defaultValue());
}
+ @Test
+ void testColumnDefaultValueWithDatetimePrecision() {
+ // MySQL rejects `DATETIME(6) DEFAULT CURRENT_TIMESTAMP`: once a column
declares a fractional
+ // seconds precision, CURRENT_TIMESTAMP has to carry the same one.
+ Column col1 =
+ Column.of(
+ "col_1",
+ Types.TimestampType.withoutTimeZone(6),
+ "col_1_comment",
+ false,
+ false,
+ DEFAULT_VALUE_OF_CURRENT_TIMESTAMP);
+ Column col2 =
+ Column.of(
+ "col_2",
+ Types.TimestampType.withTimeZone(3),
+ "col_2_comment",
+ false,
+ false,
+ DEFAULT_VALUE_OF_CURRENT_TIMESTAMP);
+ Column col3 =
+ Column.of(
+ "col_3",
+ Types.TimestampType.withoutTimeZone(),
+ "col_3_comment",
+ false,
+ false,
+ DEFAULT_VALUE_OF_CURRENT_TIMESTAMP);
+
+ NameIdentifier tableIdent =
+ NameIdentifier.of(schemaName,
GravitinoITUtils.genRandomName("mysql_it_table"));
+ TableCatalog tableCatalog = catalog.asTableCatalog();
+ tableCatalog.createTable(tableIdent, new Column[] {col1, col2, col3},
null, ImmutableMap.of());
+
+ // An added column is rendered the same way as the ones above.
+ Column col4 =
+ Column.of(
+ "col_4",
+ Types.TimestampType.withoutTimeZone(6),
+ "col_4_comment",
+ false,
+ false,
+ DEFAULT_VALUE_OF_CURRENT_TIMESTAMP);
+ tableCatalog.alterTable(
+ tableIdent,
+ TableChange.addColumn(
+ new String[] {col4.name()},
+ col4.dataType(),
+ col4.comment(),
+ TableChange.ColumnPosition.defaultPos(),
+ col4.nullable(),
+ col4.autoIncrement(),
+ col4.defaultValue()));
+
+ Table createdTable = tableCatalog.loadTable(tableIdent);
+ Assertions.assertEquals(
+ Types.TimestampType.withoutTimeZone(6),
createdTable.columns()[0].dataType());
+ Assertions.assertEquals(
+ Types.TimestampType.withTimeZone(3),
createdTable.columns()[1].dataType());
+ Assertions.assertEquals(
+ Types.TimestampType.withoutTimeZone(0),
createdTable.columns()[2].dataType());
+ Assertions.assertEquals(
+ Types.TimestampType.withoutTimeZone(6),
createdTable.columns()[3].dataType());
+ for (Column column : createdTable.columns()) {
+ Assertions.assertEquals(
+ DEFAULT_VALUE_OF_CURRENT_TIMESTAMP, column.defaultValue(),
column.name());
+ }
+ }
+
@Test
// MySQL support column default value expression after 8.0.13
// see https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html
diff --git
a/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/operation/TestMysqlTableOperations.java
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/operation/TestMysqlTableOperations.java
index f4cd86e9e6..9a871df0e3 100644
---
a/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/operation/TestMysqlTableOperations.java
+++
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/operation/TestMysqlTableOperations.java
@@ -37,6 +37,8 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.gravitino.catalog.jdbc.JdbcColumn;
import org.apache.gravitino.catalog.jdbc.JdbcTable;
import org.apache.gravitino.catalog.jdbc.utils.DataSourceUtils;
+import
org.apache.gravitino.catalog.mysql.converter.MysqlColumnDefaultValueConverter;
+import org.apache.gravitino.catalog.mysql.converter.MysqlTypeConverter;
import org.apache.gravitino.exceptions.GravitinoRuntimeException;
import org.apache.gravitino.rel.Column;
import org.apache.gravitino.rel.TableChange;
@@ -1143,6 +1145,63 @@ public class TestMysqlTableOperations extends TestMysql {
"Non-datetime type should return 0 precision");
}
+ @Test
+ public void testLoadDatetimePrecisionWithoutDriverSupport() throws Exception
{
+ // MySQL Connector/J before 8.0.16 reports a COLUMN_SIZE the fractional
seconds precision
+ // cannot be derived from, which calculateDatetimePrecision signals by
returning null. The
+ // precision must then be recovered from information_schema instead of
being left unset.
+ String tableName = RandomStringUtils.randomAlphabetic(16) +
"_datetime_precision_table";
+ DataSource dataSource =
DataSourceUtils.createDataSource(getMySQLCatalogProperties());
+ try {
+ try (Connection connection = dataSource.getConnection()) {
+ connection.setCatalog(TEST_DB_NAME.toString());
+ try (Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "CREATE TABLE `%s` ("
+ + "c_int INT, "
+ + "c_time TIME(3), "
+ + "c_time_no_precision TIME, "
+ + "c_datetime DATETIME(6), "
+ + "c_datetime_no_precision DATETIME, "
+ + "c_timestamp TIMESTAMP(3) NULL)",
+ tableName));
+ }
+ }
+
+ MysqlTableOperations tableOperations =
+ new MysqlTableOperations() {
+ @Override
+ public Integer calculateDatetimePrecision(String typeName, int
columnSize, int scale) {
+ return null;
+ }
+ };
+ tableOperations.initialize(
+ dataSource,
+ JDBC_EXCEPTION_CONVERTER,
+ new MysqlTypeConverter(),
+ new MysqlColumnDefaultValueConverter(),
+ Collections.emptyMap());
+
+ JdbcTable table = tableOperations.load(TEST_DB_NAME.toString(),
tableName);
+ Map<String, Type> typeByColumn =
+
Arrays.stream(table.columns()).collect(Collectors.toMap(Column::name,
Column::dataType));
+
+ // A column without a precision must be left untouched by the correction.
+ Assertions.assertEquals(Types.IntegerType.get(),
typeByColumn.get("c_int"));
+ Assertions.assertEquals(Types.TimeType.of(3),
typeByColumn.get("c_time"));
+ Assertions.assertEquals(Types.TimeType.of(0),
typeByColumn.get("c_time_no_precision"));
+ Assertions.assertEquals(
+ Types.TimestampType.withoutTimeZone(6),
typeByColumn.get("c_datetime"));
+ Assertions.assertEquals(
+ Types.TimestampType.withoutTimeZone(0),
typeByColumn.get("c_datetime_no_precision"));
+ Assertions.assertEquals(Types.TimestampType.withTimeZone(3),
typeByColumn.get("c_timestamp"));
+ } finally {
+ TABLE_OPERATIONS.drop(TEST_DB_NAME.toString(), tableName);
+ DataSourceUtils.closeDataSource(dataSource);
+ }
+ }
+
@Test
public void testLoadColumnsWithLossyTypeDeclarations() throws Exception {
// These types either drop their parameters (VARBINARY/ENUM/SET) or
collapse distinct widths
diff --git
a/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/operation/TestMysqlTableOperationsSqlGeneration.java
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/operation/TestMysqlTableOperationsSqlGeneration.java
index ad8966c2c8..7f240760a0 100644
---
a/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/operation/TestMysqlTableOperationsSqlGeneration.java
+++
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/operation/TestMysqlTableOperationsSqlGeneration.java
@@ -24,7 +24,9 @@ import org.apache.gravitino.catalog.jdbc.JdbcTable;
import
org.apache.gravitino.catalog.jdbc.converter.JdbcColumnDefaultValueConverter;
import org.apache.gravitino.catalog.jdbc.converter.JdbcExceptionConverter;
import org.apache.gravitino.catalog.mysql.converter.MysqlTypeConverter;
+import org.apache.gravitino.rel.Column;
import org.apache.gravitino.rel.TableChange;
+import org.apache.gravitino.rel.expressions.FunctionExpression;
import org.apache.gravitino.rel.expressions.distributions.Distributions;
import org.apache.gravitino.rel.expressions.literals.Literals;
import org.apache.gravitino.rel.expressions.transforms.Transforms;
@@ -106,6 +108,67 @@ public class TestMysqlTableOperationsSqlGeneration {
"Should contain DEFAULT value but was: " + sql);
}
+ @Test
+ public void testCurrentTimestampDefaultValueKeepsColumnPrecision() {
+ TestableMysqlTableOperations ops = new TestableMysqlTableOperations();
+
+ JdbcColumn datetime =
+ JdbcColumn.builder()
+ .withName("datetime_col")
+ .withType(Types.TimestampType.withoutTimeZone())
+ .withDefaultValue(Column.DEFAULT_VALUE_OF_CURRENT_TIMESTAMP)
+ .build();
+ JdbcColumn datetime0 =
+ JdbcColumn.builder()
+ .withName("datetime_col_0")
+ .withType(Types.TimestampType.withoutTimeZone(0))
+ .withDefaultValue(Column.DEFAULT_VALUE_OF_CURRENT_TIMESTAMP)
+ .build();
+ JdbcColumn datetime6 =
+ JdbcColumn.builder()
+ .withName("datetime_col_6")
+ .withType(Types.TimestampType.withoutTimeZone(6))
+ .withDefaultValue(Column.DEFAULT_VALUE_OF_CURRENT_TIMESTAMP)
+ .build();
+ JdbcColumn timestamp3 =
+ JdbcColumn.builder()
+ .withName("timestamp_col_3")
+ .withType(Types.TimestampType.withTimeZone(3))
+ .withDefaultValue(Column.DEFAULT_VALUE_OF_CURRENT_TIMESTAMP)
+ .build();
+ JdbcColumn upperCase =
+ JdbcColumn.builder()
+ .withName("upper_case_col")
+ .withType(Types.TimestampType.withoutTimeZone(6))
+ .withDefaultValue(FunctionExpression.of("CURRENT_TIMESTAMP"))
+ .build();
+
+ String sql =
+ ops.createTableSql(
+ "test_table", new JdbcColumn[] {datetime, datetime0, datetime6,
timestamp3, upperCase});
+ Assertions.assertTrue(
+ sql.contains("`datetime_col` datetime NULL DEFAULT CURRENT_TIMESTAMP
"), sql);
+ Assertions.assertTrue(
+ sql.contains("`datetime_col_0` datetime(0) NULL DEFAULT
CURRENT_TIMESTAMP "), sql);
+ Assertions.assertTrue(
+ sql.contains("`datetime_col_6` datetime(6) NULL DEFAULT
CURRENT_TIMESTAMP(6) "), sql);
+ Assertions.assertTrue(
+ sql.contains("`timestamp_col_3` timestamp(3) NULL DEFAULT
CURRENT_TIMESTAMP(3) "), sql);
+ Assertions.assertTrue(
+ sql.contains("`upper_case_col` datetime(6) NULL DEFAULT
CURRENT_TIMESTAMP(6) "), sql);
+
+ String alterSql =
+ ops.alterTableSql(
+ "test_table",
+ TableChange.addColumn(
+ new String[] {"added_col"},
+ Types.TimestampType.withoutTimeZone(6),
+ Column.DEFAULT_VALUE_OF_CURRENT_TIMESTAMP));
+ Assertions.assertTrue(
+ alterSql.contains("ADD COLUMN `added_col` datetime(6) DEFAULT
CURRENT_TIMESTAMP(6) "),
+ alterSql);
+ }
+
@Test
public void testEscapeCommentsInGeneratedSql() {
TestableMysqlTableOperations ops = new TestableMysqlTableOperations();
diff --git a/docs/jdbc-mysql-catalog.md b/docs/jdbc-mysql-catalog.md
index 978ffdb6aa..ec15f1736e 100644
--- a/docs/jdbc-mysql-catalog.md
+++ b/docs/jdbc-mysql-catalog.md
@@ -72,26 +72,14 @@ message if the driver is absent.
### Driver Version Compatibility
-The MySQL catalog includes driver version compatibility checks for datetime
precision calculation:
-
-- **MySQL Connector/J versions >= 8.0.16**: Full support for datetime
precision calculation
-- **MySQL Connector/J versions < 8.0.16**: Limited support - datetime
precision calculation returns `null` with a warning log
-
-This limitation affects the following datetime types:
-- `TIME(p)` - time precision
-- `TIMESTAMP(p)` - timestamp precision
-- `DATETIME(p)` - datetime precision
-
-When using an unsupported driver version, the system will:
-1. Continue to work normally with default precision (0)
-2. Log a warning message indicating the driver version limitation
-3. Return `null` for precision calculations to avoid incorrect results
+The fractional seconds precision of `TIME(p)`, `DATETIME(p)` and
`TIMESTAMP(p)` columns is derived from the
+JDBC metadata, whose `COLUMN_SIZE` is only accurate with MySQL Connector/J
8.0.16 or later. With an older
+driver the catalog logs a warning and falls back to
`information_schema.columns` for the precision.
**Example warning log:**
```
-WARN: MySQL driver version mysql-connector-java-8.0.11 is below 8.0.16,
-columnSize may not be accurate for precision calculation.
-Returning null for TIMESTAMP type precision. Driver version:
mysql-connector-java-8.0.11
+WARN: MySQL driver version mysql-connector-java-8.0.11 is below 8.0.16,
columnSize may not be accurate
+for precision calculation. Falling back to information_schema for the
TIMESTAMP precision.
```
**Recommended driver versions:**
diff --git a/docs/trino-connector/catalog-mysql.md
b/docs/trino-connector/catalog-mysql.md
index 99f007fe64..ed7088cab1 100644
--- a/docs/trino-connector/catalog-mysql.md
+++ b/docs/trino-connector/catalog-mysql.md
@@ -25,6 +25,20 @@ The Gravitino Trino connector supports `CREATE TABLE AS
SELECT`.
`CREATE OR REPLACE TABLE AS SELECT` is not supported. Use `DROP TABLE`
followed by `CREATE TABLE AS SELECT` as an alternative.
:::
+### Time type mapping
+
+`TIME`, `TIMESTAMP` and `TIMESTAMP WITH TIME ZONE` columns keep their
precision in both directions, the same way
+the native Trino MySQL connector does:
+
+| Trino type | MySQL type |
+|-------------------------------|----------------|
+| `TIME(n)` | `TIME(n)` |
+| `TIMESTAMP(n)` | `DATETIME(n)` |
+| `TIMESTAMP(n) WITH TIME ZONE` | `TIMESTAMP(n)` |
+
+`n` ranges from 0 to 6, a higher precision is reduced to 6. `TIMESTAMP`
without a precision is `TIMESTAMP(3)` in
+Trino, so it creates a `DATETIME(3)` column.
+
## Alter Table
Support for the following alter table operations:
diff --git a/integration-test-common/docker-script/init/mysql/init.sql
b/integration-test-common/docker-script/init/mysql/init.sql
index f6486358d1..4a71a3342a 100644
--- a/integration-test-common/docker-script/init/mysql/init.sql
+++ b/integration-test-common/docker-script/init/mysql/init.sql
@@ -135,6 +135,27 @@ INSERT INTO gt_mysql_test_all_type.demo (
ST_GeomFromText('POINT(10 20)'), -- point_col
ST_GeomFromText('LINESTRING(0 0, 10 10)') -- geometry_col
);
+CREATE TABLE gt_mysql_test_all_type.demo_datetime_precision
+(
+ id INT NOT NULL PRIMARY KEY,
+ time_col_3 TIME(3),
+ time_col_6 TIME(6),
+ datetime_col_3 DATETIME(3),
+ datetime_col_6 DATETIME(6),
+ timestamp_col_3 TIMESTAMP(3) NULL,
+ timestamp_col_6 TIMESTAMP(6) NULL
+);
+
+INSERT INTO gt_mysql_test_all_type.demo_datetime_precision VALUES (
+ 1,
+ '14:30:00.123',
+ '14:30:00.123456',
+ '2025-07-04 14:30:00.123',
+ '2025-07-04 14:30:00.123456',
+ '2025-07-04 14:30:00.123',
+ '2025-07-04 14:30:00.123456'
+);
+
CREATE DATABASE gt_mysql_test_column_properties;
/*
+----------------+-------------------------------------+------+-----+---------------------+-------------------+
diff --git
a/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00006_datatype.txt
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00006_datatype.txt
index b7658af842..6d88278385 100644
---
a/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00006_datatype.txt
+++
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00006_datatype.txt
@@ -18,9 +18,9 @@ CREATE TABLE
f11 integer,
f12 bigint,
f13 date,
- f14 time(0),
- f15 timestamp(0),
- f16 timestamp(0) with time zone,
+ f14 time(3),
+ f15 timestamp(3),
+ f16 timestamp(3) with time zone,
f17 json
)
COMMENT ''
@@ -32,7 +32,7 @@ INSERT: 1 row
INSERT: 1 row
-"Sample text 1","Text1
","65","123.456","7.89","12.34","false","1","100","1000","1000","100000","2024-01-01","08:00:00","2024-01-01
08:00:00","2024-01-01 08:00:00 UTC","{""x"":300,""y"":""AFRICA"",""z"":null}"
+"Sample text 1","Text1
","65","123.456","7.89","12.34","false","1","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","{""x"":300,""y"":""AFRICA"",""z"":null}"
"","","","","","","","","","","","","","","","",""
CREATE TABLE
@@ -51,9 +51,9 @@ CREATE TABLE
f11 integer NOT NULL,
f12 bigint NOT NULL,
f13 date NOT NULL,
- f14 time(0) NOT NULL,
- f15 timestamp(0) NOT NULL,
- f16 timestamp(0) with time zone NOT NULL,
+ f14 time(3) NOT NULL,
+ f15 timestamp(3) NOT NULL,
+ f16 timestamp(3) with time zone NOT NULL,
f17 json NOT NULL
)
COMMENT ''
diff --git
a/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00011_column_properties.txt
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00011_column_properties.txt
index 117d2f1d4f..e5a192e21f 100644
---
a/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00011_column_properties.txt
+++
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00011_column_properties.txt
@@ -46,7 +46,7 @@ WITH (
date_col_2 date,
date_col_3 date,
timestamp_col_1 timestamp(0) with time zone WITH (default =
'CURRENT_TIMESTAMP'),
- timestamp_col_2 timestamp(0) with time zone WITH (default =
'CURRENT_TIMESTAMP')
+ timestamp_col_2 timestamp(6) with time zone WITH (default =
'CURRENT_TIMESTAMP')
)
COMMENT ''
WITH (
@@ -70,10 +70,10 @@ CREATE TABLE
f11 integer WITH (default = '1'),
f12 bigint WITH (default = '1'),
f13 date WITH (default = '2024-04-01'),
- f14 time(0) WITH (default = '08:00'),
- f15 timestamp(0) WITH (default = '2012-12-31 11:30:45'),
- f16 timestamp(0) with time zone WITH (default = '2012-12-31 11:30:45'),
- f17 timestamp(0) with time zone WITH (default = 'CURRENT_TIMESTAMP')
+ f14 time(3) WITH (default = '08:00'),
+ f15 timestamp(3) WITH (default = '2012-12-31 11:30:45'),
+ f16 timestamp(3) with time zone WITH (default = '2012-12-31 11:30:45'),
+ f17 timestamp(3) with time zone WITH (default = 'CURRENT_TIMESTAMP')
)
COMMENT ''
WITH (
@@ -91,7 +91,7 @@ CREATE TABLE
"CREATE TABLE
gt_mysql.gt_mysql_test_column_properties_v2.test_create_with_auto_increment_1 (
key1 integer NOT NULL WITH (auto_increment = true),
- f1 timestamp(0),
+ f1 timestamp(3),
f2 date
)
COMMENT ''
@@ -107,7 +107,7 @@ CREATE TABLE
"CREATE TABLE
gt_mysql.gt_mysql_test_column_properties_v2.test_create_with_auto_increment_2 (
key1 integer NOT NULL WITH (auto_increment = true),
key2 integer NOT NULL,
- f1 timestamp(0),
+ f1 timestamp(3),
f2 date
)
COMMENT ''
diff --git
a/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00016_datetime_precision.sql
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00016_datetime_precision.sql
new file mode 100644
index 0000000000..ee22633e45
--- /dev/null
+++
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00016_datetime_precision.sql
@@ -0,0 +1,41 @@
+-- Table create by integration-test-common/docker-script/init/mysql/init.sql
+show create table gt_mysql.gt_mysql_test_all_type.demo_datetime_precision;
+
+select * from gt_mysql.gt_mysql_test_all_type.demo_datetime_precision;
+
+CREATE TABLE gt_mysql.gt_mysql_test_all_type.demo_datetime_precision_v2 (
+ id INT NOT NULL,
+ time_col TIME,
+ time_col_6 TIME(6),
+ datetime_col TIMESTAMP,
+ datetime_col_6 TIMESTAMP(6),
+ datetime_col_9 TIMESTAMP(9),
+ timestamp_col TIMESTAMP WITH TIME ZONE,
+ timestamp_col_6 TIMESTAMP(6) WITH TIME ZONE WITH
(default='CURRENT_TIMESTAMP')
+)
+WITH (
+ primary_key = ARRAY['id']
+);
+
+SHOW CREATE TABLE gt_mysql.gt_mysql_test_all_type.demo_datetime_precision_v2;
+
+INSERT INTO gt_mysql.gt_mysql_test_all_type.demo_datetime_precision_v2
+VALUES (1, TIME '14:30:00.123', TIME '14:30:00.123456', TIMESTAMP '2025-07-04
14:30:00.123', TIMESTAMP '2025-07-04 14:30:00.123456', TIMESTAMP '2025-07-04
14:30:00.123456', TIMESTAMP '2025-07-04 14:30:00.123 UTC', TIMESTAMP
'2025-07-04 14:30:00.123456 UTC'),
+ (2, TIME '23:59:59.999', TIME '23:59:59.999999', TIMESTAMP '2025-12-31
23:59:59.999', TIMESTAMP '2025-12-31 23:59:59.999999', TIMESTAMP '2025-12-31
23:59:59.999999', TIMESTAMP '2025-12-31 23:59:59.999 UTC', TIMESTAMP
'2025-12-31 23:59:59.999999 UTC');
+
+SELECT * FROM gt_mysql.gt_mysql_test_all_type.demo_datetime_precision_v2 ORDER
BY id;
+
+CREATE TABLE gt_mysql.gt_mysql_test_all_type.demo_datetime_precision_ctas AS
SELECT * FROM gt_mysql.gt_mysql_test_all_type.demo_datetime_precision_v2;
+
+SELECT * FROM gt_mysql.gt_mysql_test_all_type.demo_datetime_precision_ctas
ORDER BY id;
+
+DROP TABLE gt_mysql.gt_mysql_test_all_type.demo_datetime_precision_ctas;
+
+CREATE TABLE gt_mysql.gt_mysql_test_all_type.demo_datetime_precision_ctas_9 AS
+SELECT TIME '14:30:00.123456789' AS time_col_9, TIMESTAMP '2025-07-04
14:30:00.123456789' AS datetime_col_9, TIMESTAMP '2025-07-04 14:30:00.123456789
UTC' AS timestamp_col_9;
+
+SELECT * FROM gt_mysql.gt_mysql_test_all_type.demo_datetime_precision_ctas_9;
+
+DROP TABLE gt_mysql.gt_mysql_test_all_type.demo_datetime_precision_ctas_9;
+
+DROP TABLE gt_mysql.gt_mysql_test_all_type.demo_datetime_precision_v2;
diff --git
a/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00016_datetime_precision.txt
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00016_datetime_precision.txt
new file mode 100644
index 0000000000..51b2568b97
--- /dev/null
+++
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00016_datetime_precision.txt
@@ -0,0 +1,54 @@
+"CREATE TABLE gt_mysql.gt_mysql_test_all_type.demo_datetime_precision (
+ id integer NOT NULL,
+ time_col_3 time(3),
+ time_col_6 time(6),
+ datetime_col_3 timestamp(3),
+ datetime_col_6 timestamp(6),
+ timestamp_col_3 timestamp(3) with time zone,
+ timestamp_col_6 timestamp(6) with time zone
+)
+COMMENT ''
+WITH (
+ engine = 'InnoDB',
+ primary_key = ARRAY['id']
+)"
+
+"1","14:30:00.123","14:30:00.123456","2025-07-04 14:30:00.123","2025-07-04
14:30:00.123456","2025-07-04 14:30:00.123 UTC","2025-07-04 14:30:00.123456 UTC"
+
+CREATE TABLE
+
+"CREATE TABLE gt_mysql.gt_mysql_test_all_type.demo_datetime_precision_v2 (
+ id integer NOT NULL,
+ time_col time(3),
+ time_col_6 time(6),
+ datetime_col timestamp(3),
+ datetime_col_6 timestamp(6),
+ datetime_col_9 timestamp(6),
+ timestamp_col timestamp(3) with time zone,
+ timestamp_col_6 timestamp(6) with time zone WITH (default =
'CURRENT_TIMESTAMP')
+)
+COMMENT ''
+WITH (
+ engine = 'InnoDB',
+ primary_key = ARRAY['id']
+)"
+
+INSERT: 2 rows
+
+"1","14:30:00.123","14:30:00.123456","2025-07-04 14:30:00.123","2025-07-04
14:30:00.123456","2025-07-04 14:30:00.123456","2025-07-04 14:30:00.123
UTC","2025-07-04 14:30:00.123456 UTC"
+"2","23:59:59.999","23:59:59.999999","2025-12-31 23:59:59.999","2025-12-31
23:59:59.999999","2025-12-31 23:59:59.999999","2025-12-31 23:59:59.999
UTC","2025-12-31 23:59:59.999999 UTC"
+
+CREATE TABLE: 2 rows
+
+"1","14:30:00.123","14:30:00.123456","2025-07-04 14:30:00.123","2025-07-04
14:30:00.123456","2025-07-04 14:30:00.123456","2025-07-04 14:30:00.123
UTC","2025-07-04 14:30:00.123456 UTC"
+"2","23:59:59.999","23:59:59.999999","2025-12-31 23:59:59.999","2025-12-31
23:59:59.999999","2025-12-31 23:59:59.999999","2025-12-31 23:59:59.999
UTC","2025-12-31 23:59:59.999999 UTC"
+
+DROP TABLE
+
+CREATE TABLE: 1 row
+
+"14:30:00.123457","2025-07-04 14:30:00.123457","2025-07-04 14:30:00.123457 UTC"
+
+DROP TABLE
+
+DROP TABLE
diff --git
a/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/trino-452-446.patch
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/trino-452-446.patch
index 369b9f456b..a6c6ce731f 100644
---
a/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/trino-452-446.patch
+++
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/trino-452-446.patch
@@ -61,9 +61,9 @@ index 117d2f1d4..c3e55683f 100644
date_col_2 date,
date_col_3 date,
- timestamp_col_1 timestamp(0) with time zone WITH (default =
'CURRENT_TIMESTAMP'),
-- timestamp_col_2 timestamp(0) with time zone WITH (default =
'CURRENT_TIMESTAMP')
+- timestamp_col_2 timestamp(6) with time zone WITH (default =
'CURRENT_TIMESTAMP')
+ timestamp_col_1 timestamp(0) with time zone WITH ( default =
'CURRENT_TIMESTAMP' ),
-+ timestamp_col_2 timestamp(0) with time zone WITH ( default =
'CURRENT_TIMESTAMP' )
++ timestamp_col_2 timestamp(6) with time zone WITH ( default =
'CURRENT_TIMESTAMP' )
)
COMMENT ''
WITH (
@@ -83,10 +83,10 @@ index 117d2f1d4..c3e55683f 100644
- f11 integer WITH (default = '1'),
- f12 bigint WITH (default = '1'),
- f13 date WITH (default = '2024-04-01'),
-- f14 time(0) WITH (default = '08:00'),
-- f15 timestamp(0) WITH (default = '2012-12-31 11:30:45'),
-- f16 timestamp(0) with time zone WITH (default = '2012-12-31 11:30:45'),
-- f17 timestamp(0) with time zone WITH (default = 'CURRENT_TIMESTAMP')
+- f14 time(3) WITH (default = '08:00'),
+- f15 timestamp(3) WITH (default = '2012-12-31 11:30:45'),
+- f16 timestamp(3) with time zone WITH (default = '2012-12-31 11:30:45'),
+- f17 timestamp(3) with time zone WITH (default = 'CURRENT_TIMESTAMP')
+ key1 integer NOT NULL WITH ( auto_increment = true ),
+ f1 varchar(200) WITH ( default = 'VARCHAR' ),
+ f2 char(20) WITH ( default = 'CHAR' ),
@@ -99,10 +99,10 @@ index 117d2f1d4..c3e55683f 100644
+ f11 integer WITH ( default = '1' ),
+ f12 bigint WITH ( default = '1' ),
+ f13 date WITH ( default = '2024-04-01' ),
-+ f14 time(0) WITH ( default = '08:00' ),
-+ f15 timestamp(0) WITH ( default = '2012-12-31 11:30:45' ),
-+ f16 timestamp(0) with time zone WITH ( default = '2012-12-31 11:30:45' ),
-+ f17 timestamp(0) with time zone WITH ( default = 'CURRENT_TIMESTAMP' )
++ f14 time(3) WITH ( default = '08:00' ),
++ f15 timestamp(3) WITH ( default = '2012-12-31 11:30:45' ),
++ f16 timestamp(3) with time zone WITH ( default = '2012-12-31 11:30:45' ),
++ f17 timestamp(3) with time zone WITH ( default = 'CURRENT_TIMESTAMP' )
)
COMMENT ''
WITH (
@@ -112,7 +112,7 @@ index 117d2f1d4..c3e55683f 100644
"CREATE TABLE
gt_mysql.gt_mysql_test_column_properties_v2.test_create_with_auto_increment_1 (
- key1 integer NOT NULL WITH (auto_increment = true),
+ key1 integer NOT NULL WITH ( auto_increment = true ),
- f1 timestamp(0),
+ f1 timestamp(3),
f2 date
)
@@ -105,7 +105,7 @@ DROP TABLE
@@ -122,7 +122,7 @@ index 117d2f1d4..c3e55683f 100644
- key1 integer NOT NULL WITH (auto_increment = true),
+ key1 integer NOT NULL WITH ( auto_increment = true ),
key2 integer NOT NULL,
- f1 timestamp(0),
+ f1 timestamp(3),
f2 date
@@ -121,7 +121,7 @@ DROP TABLE
CREATE TABLE
@@ -142,3 +142,16 @@ index 117d2f1d4..c3e55683f 100644
key2 integer,
col1 integer
)
+diff --git
a/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00016_datetime_precision.txt
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00016_datetime_precision.txt
+index 51f226ea2..b57d66f9e 100644
+---
a/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00016_datetime_precision.txt
++++
b/trino-connector/integration-test/src/test/resources/trino-ci-testset/testsets/jdbc-mysql/00016_datetime_precision.txt
+@@ -25,7 +25,7 @@ CREATE TABLE
+ datetime_col_6 timestamp(6),
+ datetime_col_9 timestamp(6),
+ timestamp_col timestamp(3) with time zone,
+- timestamp_col_6 timestamp(6) with time zone WITH (default =
'CURRENT_TIMESTAMP')
++ timestamp_col_6 timestamp(6) with time zone WITH ( default =
'CURRENT_TIMESTAMP' )
+ )
+ COMMENT ''
+ WITH (
diff --git
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/GravitinoMetadata.java
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/GravitinoMetadata.java
index 3d578aa72a..9c37025507 100644
---
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/GravitinoMetadata.java
+++
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/GravitinoMetadata.java
@@ -230,6 +230,12 @@ public abstract class GravitinoMetadata implements
ConnectorMetadata {
catalogConnectorMetadata.createTable(table, saveMode == SaveMode.IGNORE);
}
+ @Override
+ public Optional<Type> getSupportedType(
+ ConnectorSession session, Map<String, Object> tableProperties, Type
type) {
+ return metadataAdapter.getDataTypeTransformer().getSupportedType(type);
+ }
+
@Override
public ConnectorOutputTableHandle beginCreateTable(
ConnectorSession session,
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 b6ecf8696d..7a2f57c573 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
@@ -24,6 +24,7 @@ import io.trino.spi.type.CharType;
import io.trino.spi.type.TimeType;
import io.trino.spi.type.TimestampType;
import io.trino.spi.type.TimestampWithTimeZoneType;
+import java.util.Optional;
import org.apache.gravitino.rel.types.Type;
import org.apache.gravitino.rel.types.Type.Name;
import org.apache.gravitino.rel.types.Types;
@@ -49,13 +50,23 @@ public class MySQLDataTypeTransformer 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_SECONDS;
- } else {
- return TimestampType.TIMESTAMP_SECONDS;
- }
+ // Fall back to the MySQL default fractional seconds precision of 0 when
the catalog does
+ // not report one.
+ int precision =
+ timestampType.hasPrecisionSet()
+ ? toMySQLFractionalSecondsPrecision(timestampType.precision())
+ : TRINO_SECONDS_PRECISION;
+ return timestampType.hasTimeZone()
+ ?
TimestampWithTimeZoneType.createTimestampWithTimeZoneType(precision)
+ : TimestampType.createTimestampType(precision);
} else if (Name.TIME == type.name()) {
- return TimeType.TIME_SECONDS;
+ Types.TimeType timeType = (Types.TimeType) type;
+ // Precision unknown, same fallback as TIMESTAMP above.
+ int precision =
+ timeType.hasPrecisionSet()
+ ? toMySQLFractionalSecondsPrecision(timeType.precision())
+ : TRINO_SECONDS_PRECISION;
+ return TimeType.createTimeType(precision);
} else if (Name.EXTERNAL == type.name()) {
String catalogString = ((Types.ExternalType) type).catalogString();
return MySQLExternalDataType.safeValueOf(catalogString).getTrinoType();
@@ -64,6 +75,18 @@ public class MySQLDataTypeTransformer extends
GeneralDataTypeTransformer {
return super.getTrinoType(type);
}
+ @Override
+ public Optional<io.trino.spi.type.Type>
getSupportedType(io.trino.spi.type.Type type) {
+ if (type instanceof TimeType
+ || type instanceof TimestampType
+ || type instanceof TimestampWithTimeZoneType) {
+ // The column is created with the precision kept by getGravitinoType, at
most 6 for MySQL.
+ io.trino.spi.type.Type supported = getTrinoType(getGravitinoType(type));
+ return supported.equals(type) ? Optional.empty() :
Optional.of(supported);
+ }
+ return super.getSupportedType(type);
+ }
+
@Override
public Type getGravitinoType(io.trino.spi.type.Type type) {
Class<? extends io.trino.spi.type.Type> typeClass = type.getClass();
@@ -104,16 +127,32 @@ public class MySQLDataTypeTransformer extends
GeneralDataTypeTransformer {
} 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);
+ // Same as the native Trino MySQL connector: time(p) is stored as
TIME(p), p capped at 6
+ return Types.TimeType.of(toMySQLFractionalSecondsPrecision(((TimeType)
type).getPrecision()));
} 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);
+ // timestamp(p) is stored as DATETIME(p), p capped at 6
+ return Types.TimestampType.withoutTimeZone(
+ toMySQLFractionalSecondsPrecision(((TimestampType)
type).getPrecision()));
} 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);
+ // timestamp(p) with time zone is stored as TIMESTAMP(p), p capped at 6
+ return Types.TimestampType.withTimeZone(
+ toMySQLFractionalSecondsPrecision(((TimestampWithTimeZoneType)
type).getPrecision()));
}
return super.getGravitinoType(type);
}
+
+ /**
+ * Maps a time/timestamp precision between Gravitino and Trino. The Trino
type must carry the same
+ * precision as the MySQL column, otherwise Trino rejects the values read
from MySQL (for example,
+ * "Expected 0s for digits beyond precision 0").
+ *
+ * @param precision the precision of the Gravitino or Trino type.
+ * @return the precision to use for the other side, capped at the MySQL
maximum of 6.
+ */
+ private static int toMySQLFractionalSecondsPrecision(int precision) {
+ // MySQL supports a fractional seconds precision from 0 to 6 (microseconds
precision), see
+ // https://dev.mysql.com/doc/refman/8.0/en/fractional-seconds.html
+ return Math.min(TRINO_MICROS_PRECISION, precision);
+ }
}
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 90c5222512..069d61f4e0 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
@@ -188,6 +188,18 @@ public class GeneralDataTypeTransformer {
return StructType.of(fields);
}
+ /**
+ * Returns the Trino type a column of the requested type is created with
when it differs from the
+ * requested one. Trino coerces the values written by CREATE TABLE AS SELECT
to this type, so the
+ * pages written by the engine match the column the internal connector reads
and writes.
+ *
+ * @param type the type requested by the user.
+ * @return the type the column is created with, or empty when the requested
type is used as is.
+ */
+ public Optional<Type> getSupportedType(Type type) {
+ return Optional.empty();
+ }
+
/**
* Transforms a Trino datatype to a Gravitino datatype.
*
diff --git
a/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/TestGravitinoMetadataGetSupportedType.java
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/TestGravitinoMetadataGetSupportedType.java
new file mode 100644
index 0000000000..70651c8d33
--- /dev/null
+++
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/TestGravitinoMetadataGetSupportedType.java
@@ -0,0 +1,66 @@
+/*
+ * 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.trino.connector;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import io.trino.spi.connector.ConnectorMetadata;
+import io.trino.spi.connector.ConnectorSession;
+import io.trino.spi.type.TimestampType;
+import io.trino.spi.type.VarcharType;
+import java.util.Collections;
+import java.util.Optional;
+import org.apache.gravitino.trino.connector.catalog.CatalogConnectorMetadata;
+import
org.apache.gravitino.trino.connector.catalog.CatalogConnectorMetadataAdapter;
+import
org.apache.gravitino.trino.connector.catalog.jdbc.mysql.MySQLDataTypeTransformer;
+import org.junit.jupiter.api.Test;
+
+public class TestGravitinoMetadataGetSupportedType {
+
+ @Test
+ public void testDelegatesToDataTypeTransformer() {
+ CatalogConnectorMetadataAdapter metadataAdapter =
mock(CatalogConnectorMetadataAdapter.class);
+ when(metadataAdapter.getDataTypeTransformer()).thenReturn(new
MySQLDataTypeTransformer());
+ ConnectorSession session = mock(ConnectorSession.class);
+ GravitinoMetadata metadata =
+ new StubGravitinoMetadata(
+ mock(CatalogConnectorMetadata.class), metadataAdapter,
mock(ConnectorMetadata.class));
+
+ assertEquals(
+ Optional.of(TimestampType.TIMESTAMP_MICROS),
+ metadata.getSupportedType(session, Collections.emptyMap(),
TimestampType.TIMESTAMP_NANOS));
+ assertEquals(
+ Optional.empty(),
+ metadata.getSupportedType(session, Collections.emptyMap(),
TimestampType.TIMESTAMP_MICROS));
+ assertEquals(
+ Optional.empty(),
+ metadata.getSupportedType(session, Collections.emptyMap(),
VarcharType.VARCHAR));
+ }
+
+ private static final class StubGravitinoMetadata extends GravitinoMetadata {
+ private StubGravitinoMetadata(
+ CatalogConnectorMetadata catalogConnectorMetadata,
+ CatalogConnectorMetadataAdapter metadataAdapter,
+ ConnectorMetadata internalMetadata) {
+ super(catalogConnectorMetadata, metadataAdapter, internalMetadata);
+ }
+ }
+}
diff --git
a/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/jdbc/mysql/TestMySQLDataTypeTransformer.java
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/jdbc/mysql/TestMySQLDataTypeTransformer.java
index 8aee5f8e02..b556eb2aed 100644
---
a/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/jdbc/mysql/TestMySQLDataTypeTransformer.java
+++
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/jdbc/mysql/TestMySQLDataTypeTransformer.java
@@ -26,8 +26,12 @@ import io.trino.spi.type.DateType;
import io.trino.spi.type.DoubleType;
import io.trino.spi.type.IntegerType;
import io.trino.spi.type.RealType;
+import io.trino.spi.type.TimeType;
+import io.trino.spi.type.TimestampType;
+import io.trino.spi.type.TimestampWithTimeZoneType;
import io.trino.spi.type.VarbinaryType;
import io.trino.spi.type.VarcharType;
+import java.util.Optional;
import org.apache.gravitino.rel.types.Type;
import org.apache.gravitino.rel.types.Types;
import org.apache.gravitino.trino.connector.util.GeneralDataTypeTransformer;
@@ -109,6 +113,133 @@ public class TestMySQLDataTypeTransformer {
io.trino.spi.type.DecimalType.createDecimalType(20, 0));
}
+ @Test
+ public void testGravitinoTimestampToTrinoType() {
+ GeneralDataTypeTransformer transformer = new MySQLDataTypeTransformer();
+
+ // MySQL DATETIME/TIMESTAMP default to a fractional seconds precision of 0.
+ Assertions.assertEquals(
+ TimestampType.TIMESTAMP_SECONDS,
+ transformer.getTrinoType(Types.TimestampType.withoutTimeZone()));
+ Assertions.assertEquals(
+ TimestampWithTimeZoneType.TIMESTAMP_TZ_SECONDS,
+ transformer.getTrinoType(Types.TimestampType.withTimeZone()));
+
+ // The Trino type must keep the precision of the MySQL column, otherwise
reading a
+ // DATETIME(3)/TIMESTAMP(6) column fails with "Expected 0s for digits
beyond precision 0".
+ for (int precision = 0; precision <= 6; precision++) {
+ Assertions.assertEquals(
+ TimestampType.createTimestampType(precision),
+
transformer.getTrinoType(Types.TimestampType.withoutTimeZone(precision)));
+ Assertions.assertEquals(
+ TimestampWithTimeZoneType.createTimestampWithTimeZoneType(precision),
+
transformer.getTrinoType(Types.TimestampType.withTimeZone(precision)));
+ }
+
+ // MySQL supports at most microseconds precision.
+ Assertions.assertEquals(
+ TimestampType.TIMESTAMP_MICROS,
+ transformer.getTrinoType(Types.TimestampType.withoutTimeZone(9)));
+ Assertions.assertEquals(
+ TimestampWithTimeZoneType.TIMESTAMP_TZ_MICROS,
+ transformer.getTrinoType(Types.TimestampType.withTimeZone(12)));
+ }
+
+ @Test
+ public void testGravitinoTimeToTrinoType() {
+ GeneralDataTypeTransformer transformer = new MySQLDataTypeTransformer();
+
+ // MySQL TIME defaults to a fractional seconds precision of 0.
+ Assertions.assertEquals(TimeType.TIME_SECONDS,
transformer.getTrinoType(Types.TimeType.get()));
+
+ for (int precision = 0; precision <= 6; precision++) {
+ Assertions.assertEquals(
+ TimeType.createTimeType(precision),
+ transformer.getTrinoType(Types.TimeType.of(precision)));
+ }
+
+ // MySQL supports at most microseconds precision.
+ Assertions.assertEquals(TimeType.TIME_MICROS,
transformer.getTrinoType(Types.TimeType.of(12)));
+ }
+
+ @Test
+ public void testTrinoTimestampToGravitinoType() {
+ GeneralDataTypeTransformer transformer = new MySQLDataTypeTransformer();
+
+ // Same as the native Trino MySQL connector: the precision is kept,
DATETIME(p)/TIMESTAMP(p)
+ for (int precision = 0; precision <= 6; precision++) {
+ Assertions.assertEquals(
+ Types.TimestampType.withoutTimeZone(precision),
+
transformer.getGravitinoType(TimestampType.createTimestampType(precision)));
+ Assertions.assertEquals(
+ Types.TimestampType.withTimeZone(precision),
+ transformer.getGravitinoType(
+
TimestampWithTimeZoneType.createTimestampWithTimeZoneType(precision)));
+ }
+
+ // MySQL supports at most microseconds precision.
+ Assertions.assertEquals(
+ Types.TimestampType.withoutTimeZone(6),
+ transformer.getGravitinoType(TimestampType.TIMESTAMP_NANOS));
+ Assertions.assertEquals(
+ Types.TimestampType.withTimeZone(6),
+
transformer.getGravitinoType(TimestampWithTimeZoneType.TIMESTAMP_TZ_PICOS));
+
+ // Round trip keeps the precision, so a table created through Trino reads
back with the same
+ // types.
+ Assertions.assertEquals(
+ TimestampType.TIMESTAMP_MICROS,
+
transformer.getTrinoType(transformer.getGravitinoType(TimestampType.TIMESTAMP_MICROS)));
+ Assertions.assertEquals(
+ TimestampWithTimeZoneType.TIMESTAMP_TZ_MILLIS,
+ transformer.getTrinoType(
+
transformer.getGravitinoType(TimestampWithTimeZoneType.TIMESTAMP_TZ_MILLIS)));
+ }
+
+ @Test
+ public void testTrinoTimeToGravitinoType() {
+ GeneralDataTypeTransformer transformer = new MySQLDataTypeTransformer();
+
+ for (int precision = 0; precision <= 6; precision++) {
+ Assertions.assertEquals(
+ Types.TimeType.of(precision),
+ transformer.getGravitinoType(TimeType.createTimeType(precision)));
+ }
+
+ // MySQL supports at most microseconds precision.
+ Assertions.assertEquals(
+ Types.TimeType.of(6),
transformer.getGravitinoType(TimeType.TIME_NANOS));
+
+ Assertions.assertEquals(
+ TimeType.TIME_MICROS,
+
transformer.getTrinoType(transformer.getGravitinoType(TimeType.TIME_MICROS)));
+ }
+
+ @Test
+ public void testSupportedType() {
+ GeneralDataTypeTransformer transformer = new MySQLDataTypeTransformer();
+
+ // Up to microseconds the requested type is used as is.
+ Assertions.assertEquals(
+ Optional.empty(),
transformer.getSupportedType(TimestampType.TIMESTAMP_MICROS));
+ Assertions.assertEquals(
+ Optional.empty(),
+
transformer.getSupportedType(TimestampWithTimeZoneType.TIMESTAMP_TZ_SECONDS));
+ Assertions.assertEquals(Optional.empty(),
transformer.getSupportedType(TimeType.TIME_MILLIS));
+ Assertions.assertEquals(
+ Optional.empty(),
transformer.getSupportedType(VarcharType.createVarcharType(10)));
+
+ // Above microseconds the column is created with precision 6 and the
values are coerced.
+ Assertions.assertEquals(
+ Optional.of(TimestampType.TIMESTAMP_MICROS),
+ transformer.getSupportedType(TimestampType.TIMESTAMP_NANOS));
+ Assertions.assertEquals(
+ Optional.of(TimestampWithTimeZoneType.TIMESTAMP_TZ_MICROS),
+
transformer.getSupportedType(TimestampWithTimeZoneType.TIMESTAMP_TZ_PICOS));
+ Assertions.assertEquals(
+ Optional.of(TimeType.TIME_MICROS),
transformer.getSupportedType(TimeType.TIME_NANOS));
+ }
+
@Test
public void testGravitinoExternalTypeToTrinoType() {
GeneralDataTypeTransformer generalDataTypeTransformer = new
MySQLDataTypeTransformer();