Skip to content

Commit 9fda40b

Browse files
committed
feat: support time/datetime/timestamp precision
Signed-off-by: zacsun <zacsun@lilith.com>
1 parent 0e41493 commit 9fda40b

File tree

12 files changed

+306
-43
lines changed

12 files changed

+306
-43
lines changed

api/src/main/java/org/apache/gravitino/rel/types/Types.java

+77-7
Original file line numberDiff line numberDiff line change
@@ -330,14 +330,30 @@ public String simpleString() {
330330

331331
/** The time type in Gravitino. */
332332
public static class TimeType extends Type.DateTimeType {
333-
private static final TimeType INSTANCE = new TimeType();
333+
private static final TimeType INSTANCE = new TimeType(0);
334334

335335
/** @return The singleton instance of {@link TimeType}. */
336336
public static TimeType get() {
337337
return INSTANCE;
338338
}
339339

340-
private TimeType() {}
340+
/**
341+
* @param precision The precision of the time type.
342+
* @return A {@link TimeType} with the given precision.
343+
*/
344+
public static TimeType of(int precision) {
345+
return new TimeType(precision);
346+
}
347+
348+
private final int precision;
349+
350+
private TimeType(int precision) {
351+
this.precision = precision;
352+
}
353+
354+
public int precision() {
355+
return precision;
356+
}
341357

342358
@Override
343359
public Name name() {
@@ -346,14 +362,27 @@ public Name name() {
346362

347363
@Override
348364
public String simpleString() {
349-
return "time";
365+
return precision == 0 ? "time" : String.format("time(%d)", precision);
366+
}
367+
368+
@Override
369+
public boolean equals(Object o) {
370+
if (this == o) return true;
371+
if (!(o instanceof TimeType)) return false;
372+
TimeType that = (TimeType) o;
373+
return precision == that.precision;
374+
}
375+
376+
@Override
377+
public int hashCode() {
378+
return Objects.hash(precision);
350379
}
351380
}
352381

353382
/** The timestamp type in Gravitino. */
354383
public static class TimestampType extends Type.DateTimeType {
355-
private static final TimestampType INSTANCE_WITHOUT_TIME_ZONE = new TimestampType(false);
356-
private static final TimestampType INSTANCE_WITH_TIME_ZONE = new TimestampType(true);
384+
private static final TimestampType INSTANCE_WITHOUT_TIME_ZONE = new TimestampType(false, 0);
385+
private static final TimestampType INSTANCE_WITH_TIME_ZONE = new TimestampType(true, 0);
357386

358387
/** @return A {@link TimestampType} with time zone. */
359388
public static TimestampType withTimeZone() {
@@ -365,17 +394,40 @@ public static TimestampType withoutTimeZone() {
365394
return INSTANCE_WITHOUT_TIME_ZONE;
366395
}
367396

397+
/**
398+
* @param precision The precision of the timestamp type.
399+
* @return A {@link TimestampType} with the given precision and time zone.
400+
*/
401+
public static TimestampType withTimeZone(int precision) {
402+
return new TimestampType(true, precision);
403+
}
404+
405+
/**
406+
* @param precision The precision of the timestamp type.
407+
* @return A {@link TimestampType} with the given precision and without time zone.
408+
*/
409+
public static TimestampType withoutTimeZone(int precision) {
410+
return new TimestampType(false, precision);
411+
}
412+
368413
private final boolean withTimeZone;
414+
private final int precision;
369415

370-
private TimestampType(boolean withTimeZone) {
416+
private TimestampType(boolean withTimeZone, int precision) {
371417
this.withTimeZone = withTimeZone;
418+
this.precision = precision;
372419
}
373420

374421
/** @return True if the timestamp type has time zone, false otherwise. */
375422
public boolean hasTimeZone() {
376423
return withTimeZone;
377424
}
378425

426+
/** @return The precision of the timestamp type. */
427+
public int precision() {
428+
return precision;
429+
}
430+
379431
@Override
380432
public Name name() {
381433
return Name.TIMESTAMP;
@@ -384,7 +436,25 @@ public Name name() {
384436
/** @return The simple string representation of the timestamp type. */
385437
@Override
386438
public String simpleString() {
387-
return withTimeZone ? "timestamp_tz" : "timestamp";
439+
if (precision == 0) {
440+
return withTimeZone ? "timestamp_tz" : "timestamp";
441+
}
442+
return withTimeZone
443+
? String.format("timestamp_tz(%d)", precision)
444+
: String.format("timestamp(%d)", precision);
445+
}
446+
447+
@Override
448+
public boolean equals(Object o) {
449+
if (this == o) return true;
450+
if (!(o instanceof TimestampType)) return false;
451+
TimestampType that = (TimestampType) o;
452+
return withTimeZone == that.withTimeZone && precision == that.precision;
453+
}
454+
455+
@Override
456+
public int hashCode() {
457+
return Objects.hash(withTimeZone, precision);
388458
}
389459
}
390460

catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/converter/JdbcTypeConverter.java

+16-2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public static class JdbcTypeBean {
4040
/** Scale. For example: 2 in decimal (10,2). */
4141
private Integer scale;
4242

43+
private Integer datetimePrecision;
44+
4345
public JdbcTypeBean(String typeName) {
4446
this.typeName = typeName;
4547
}
@@ -68,19 +70,28 @@ public void setScale(Integer scale) {
6870
this.scale = scale;
6971
}
7072

73+
public Integer getDatetimePrecision() {
74+
return datetimePrecision;
75+
}
76+
77+
public void setDatetimePrecision(Integer datetimePrecision) {
78+
this.datetimePrecision = datetimePrecision;
79+
}
80+
7181
@Override
7282
public boolean equals(Object o) {
7383
if (this == o) return true;
7484
if (!(o instanceof JdbcTypeBean)) return false;
7585
JdbcTypeBean typeBean = (JdbcTypeBean) o;
7686
return Objects.equals(typeName, typeBean.typeName)
7787
&& Objects.equals(columnSize, typeBean.columnSize)
78-
&& Objects.equals(scale, typeBean.scale);
88+
&& Objects.equals(scale, typeBean.scale)
89+
&& Objects.equals(datetimePrecision, typeBean.datetimePrecision);
7990
}
8091

8192
@Override
8293
public int hashCode() {
83-
return Objects.hash(typeName, columnSize, scale);
94+
return Objects.hash(typeName, columnSize, scale, datetimePrecision);
8495
}
8596

8697
@Override
@@ -95,6 +106,9 @@ public String toString() {
95106
+ ", scale='"
96107
+ scale
97108
+ '\''
109+
+ ", datetimePrecision='"
110+
+ datetimePrecision
111+
+ '\''
98112
+ '}';
99113
}
100114
}

catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/operation/JdbcTableOperations.java

+18-3
Original file line numberDiff line numberDiff line change
@@ -585,10 +585,14 @@ protected JdbcTable.Builder getBasicJdbcTableInfo(ResultSet table) throws SQLExc
585585
}
586586

587587
protected JdbcColumn.Builder getBasicJdbcColumnInfo(ResultSet column) throws SQLException {
588-
JdbcTypeConverter.JdbcTypeBean typeBean =
589-
new JdbcTypeConverter.JdbcTypeBean(column.getString("TYPE_NAME"));
590-
typeBean.setColumnSize(column.getInt("COLUMN_SIZE"));
588+
String typeName = column.getString("TYPE_NAME");
589+
int columnSize = column.getInt("COLUMN_SIZE");
590+
JdbcTypeConverter.JdbcTypeBean typeBean = new JdbcTypeConverter.JdbcTypeBean(typeName);
591+
typeBean.setColumnSize(columnSize);
591592
typeBean.setScale(column.getInt("DECIMAL_DIGITS"));
593+
int datetimePrecision = calculateDatetimePrecision(typeName, columnSize);
594+
typeBean.setDatetimePrecision(datetimePrecision);
595+
592596
String comment = column.getString("REMARKS");
593597
boolean nullable = column.getBoolean("NULLABLE");
594598

@@ -604,4 +608,15 @@ protected JdbcColumn.Builder getBasicJdbcColumnInfo(ResultSet column) throws SQL
604608
.withNullable(nullable)
605609
.withDefaultValue(defaultValue);
606610
}
611+
612+
/**
613+
* Calculate the precision for time/datetime/timestamp types.
614+
*
615+
* @param typeName the type name from database
616+
* @param columnSize the column size from database
617+
* @return the precision of the time/datetime/timestamp type
618+
*/
619+
public int calculateDatetimePrecision(String typeName, int columnSize) {
620+
return 0;
621+
}
607622
}

catalogs/catalog-jdbc-mysql/src/main/java/org/apache/gravitino/catalog/mysql/converter/MysqlColumnDefaultValueConverter.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ public Expression toGravitino(
4949
}
5050

5151
if (isExpression) {
52-
if (columnDefaultValue.equals(CURRENT_TIMESTAMP)) {
52+
if (columnDefaultValue.equals(CURRENT_TIMESTAMP)
53+
|| columnDefaultValue.startsWith(CURRENT_TIMESTAMP + "(")) {
5354
return DEFAULT_VALUE_OF_CURRENT_TIMESTAMP;
5455
}
5556
// The parsing of MySQL expressions is complex, so we are not currently undertaking the
@@ -88,6 +89,7 @@ public Expression toGravitino(
8889
case JdbcTypeConverter.TIMESTAMP:
8990
case MysqlTypeConverter.DATETIME:
9091
return CURRENT_TIMESTAMP.equals(columnDefaultValue)
92+
|| columnDefaultValue.startsWith(CURRENT_TIMESTAMP + "(")
9193
? DEFAULT_VALUE_OF_CURRENT_TIMESTAMP
9294
: Literals.timestampLiteral(
9395
LocalDateTime.parse(columnDefaultValue, DATE_TIME_FORMATTER));

catalogs/catalog-jdbc-mysql/src/main/java/org/apache/gravitino/catalog/mysql/converter/MysqlTypeConverter.java

+11-4
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,18 @@ public Type toGravitino(JdbcTypeBean typeBean) {
7272
case DATE:
7373
return Types.DateType.get();
7474
case TIME:
75-
return Types.TimeType.get();
75+
return Types.TimeType.of(
76+
typeBean.getDatetimePrecision() != null ? typeBean.getDatetimePrecision() : 0);
7677
// MySQL converts TIMESTAMP values from the current time zone to UTC for storage, and back
7778
// from UTC to the current time zone for retrieval. (This does not occur for other types
7879
// such as DATETIME.) see more details:
7980
// https://dev.mysql.com/doc/refman/8.0/en/datetime.html
8081
case TIMESTAMP:
81-
return Types.TimestampType.withTimeZone();
82+
return Types.TimestampType.withTimeZone(
83+
typeBean.getDatetimePrecision() != null ? typeBean.getDatetimePrecision() : 0);
8284
case DATETIME:
83-
return Types.TimestampType.withoutTimeZone();
85+
return Types.TimestampType.withoutTimeZone(
86+
typeBean.getDatetimePrecision() != null ? typeBean.getDatetimePrecision() : 0);
8487
case DECIMAL:
8588
return Types.DecimalType.of(typeBean.getColumnSize(), typeBean.getScale());
8689
case VARCHAR:
@@ -136,7 +139,11 @@ public String fromGravitino(Type type) {
136139
// MySQL converts TIMESTAMP values from the current time zone to UTC for storage, and back
137140
// from UTC to the current time zone for retrieval. (This does not occur for other types
138141
// such as DATETIME.) see more details: https://dev.mysql.com/doc/refman/8.0/en/datetime.html
139-
return ((Types.TimestampType) type).hasTimeZone() ? TIMESTAMP : DATETIME;
142+
Types.TimestampType timestampType = (Types.TimestampType) type;
143+
String baseType = timestampType.hasTimeZone() ? TIMESTAMP : DATETIME;
144+
return timestampType.precision() > 0
145+
? String.format("%s(%d)", baseType, timestampType.precision())
146+
: baseType;
140147
} else if (type instanceof Types.DecimalType) {
141148
return type.simpleString();
142149
} else if (type instanceof Types.VarCharType) {

catalogs/catalog-jdbc-mysql/src/main/java/org/apache/gravitino/catalog/mysql/operation/MysqlTableOperations.java

+13
Original file line numberDiff line numberDiff line change
@@ -672,4 +672,17 @@ private static void validateIndexes(Index[] indexes, JdbcColumn[] columns) {
672672
}
673673
}
674674
}
675+
676+
@Override
677+
public int calculateDatetimePrecision(String typeName, int columnSize) {
678+
String upperTypeName = typeName.toUpperCase();
679+
switch (upperTypeName) {
680+
case "TIME":
681+
return columnSize > 8 ? columnSize - 9 : 0;
682+
case "TIMESTAMP":
683+
case "DATETIME":
684+
return columnSize > 19 ? columnSize - 20 : 0;
685+
}
686+
return 0;
687+
}
675688
}

catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/converter/TestMysqlTypeConverter.java

+36-18
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,34 @@ public class TestMysqlTypeConverter {
4747

4848
@Test
4949
public void testToGravitinoType() {
50-
checkJdbcTypeToGravitinoType(Types.ByteType.get(), TINYINT, null, null);
51-
checkJdbcTypeToGravitinoType(Types.IntegerType.get(), INT, null, null);
52-
checkJdbcTypeToGravitinoType(Types.LongType.get(), BIGINT, null, null);
53-
checkJdbcTypeToGravitinoType(Types.FloatType.get(), FLOAT, null, null);
54-
checkJdbcTypeToGravitinoType(Types.DoubleType.get(), DOUBLE, null, null);
55-
checkJdbcTypeToGravitinoType(Types.DateType.get(), DATE, null, null);
56-
checkJdbcTypeToGravitinoType(Types.TimeType.get(), TIME, null, null);
57-
checkJdbcTypeToGravitinoType(Types.TimestampType.withoutTimeZone(), DATETIME, null, null);
58-
checkJdbcTypeToGravitinoType(Types.TimestampType.withTimeZone(), TIMESTAMP, null, null);
59-
checkJdbcTypeToGravitinoType(Types.DecimalType.of(10, 2), DECIMAL, 10, 2);
60-
checkJdbcTypeToGravitinoType(Types.VarCharType.of(20), VARCHAR, 20, null);
61-
checkJdbcTypeToGravitinoType(Types.FixedCharType.of(20), CHAR, 20, null);
62-
checkJdbcTypeToGravitinoType(Types.StringType.get(), TEXT, null, null);
63-
checkJdbcTypeToGravitinoType(Types.BinaryType.get(), BINARY, null, null);
50+
checkJdbcTypeToGravitinoType(Types.ByteType.get(), TINYINT, null, null, 0);
51+
checkJdbcTypeToGravitinoType(Types.IntegerType.get(), INT, null, null, 0);
52+
checkJdbcTypeToGravitinoType(Types.LongType.get(), BIGINT, null, null, 0);
53+
checkJdbcTypeToGravitinoType(Types.FloatType.get(), FLOAT, null, null, 0);
54+
checkJdbcTypeToGravitinoType(Types.DoubleType.get(), DOUBLE, null, null, 0);
55+
checkJdbcTypeToGravitinoType(Types.DateType.get(), DATE, null, null, 0);
56+
checkJdbcTypeToGravitinoType(Types.TimeType.get(), TIME, null, null, 0);
57+
checkJdbcTypeToGravitinoType(Types.TimeType.of(0), TIME, 8, null, 0);
58+
checkJdbcTypeToGravitinoType(Types.TimeType.of(3), TIME, 12, null, 3);
59+
checkJdbcTypeToGravitinoType(Types.TimeType.of(6), TIME, 15, null, 6);
60+
checkJdbcTypeToGravitinoType(Types.TimeType.of(9), TIME, 18, null, 9);
61+
checkJdbcTypeToGravitinoType(Types.TimestampType.withoutTimeZone(), DATETIME, null, null, 0);
62+
checkJdbcTypeToGravitinoType(Types.TimestampType.withoutTimeZone(0), DATETIME, 19, null, 0);
63+
checkJdbcTypeToGravitinoType(Types.TimestampType.withoutTimeZone(3), DATETIME, 23, null, 3);
64+
checkJdbcTypeToGravitinoType(Types.TimestampType.withoutTimeZone(6), DATETIME, 26, null, 6);
65+
checkJdbcTypeToGravitinoType(Types.TimestampType.withoutTimeZone(9), DATETIME, 29, null, 9);
66+
checkJdbcTypeToGravitinoType(Types.TimestampType.withTimeZone(), TIMESTAMP, null, null, 0);
67+
checkJdbcTypeToGravitinoType(Types.TimestampType.withTimeZone(0), TIMESTAMP, null, null, 0);
68+
checkJdbcTypeToGravitinoType(Types.TimestampType.withTimeZone(3), TIMESTAMP, 23, null, 3);
69+
checkJdbcTypeToGravitinoType(Types.TimestampType.withTimeZone(6), TIMESTAMP, 26, null, 6);
70+
checkJdbcTypeToGravitinoType(Types.TimestampType.withTimeZone(9), TIMESTAMP, 29, null, 9);
71+
checkJdbcTypeToGravitinoType(Types.DecimalType.of(10, 2), DECIMAL, 10, 2, 0);
72+
checkJdbcTypeToGravitinoType(Types.VarCharType.of(20), VARCHAR, 20, null, 0);
73+
checkJdbcTypeToGravitinoType(Types.FixedCharType.of(20), CHAR, 20, null, 0);
74+
checkJdbcTypeToGravitinoType(Types.StringType.get(), TEXT, null, null, 0);
75+
checkJdbcTypeToGravitinoType(Types.BinaryType.get(), BINARY, null, null, 0);
6476
checkJdbcTypeToGravitinoType(
65-
Types.ExternalType.of(USER_DEFINED_TYPE), USER_DEFINED_TYPE, null, null);
77+
Types.ExternalType.of(USER_DEFINED_TYPE), USER_DEFINED_TYPE, null, null, 0);
6678
}
6779

6880
@Test
@@ -92,17 +104,23 @@ protected void checkGravitinoTypeToJdbcType(String jdbcTypeName, Type gravitinoT
92104
}
93105

94106
protected void checkJdbcTypeToGravitinoType(
95-
Type gravitinoType, String jdbcTypeName, Integer columnSize, Integer scale) {
96-
JdbcTypeConverter.JdbcTypeBean typeBean = createTypeBean(jdbcTypeName, columnSize, scale);
107+
Type gravitinoType,
108+
String jdbcTypeName,
109+
Integer columnSize,
110+
Integer scale,
111+
Integer datetimePrecision) {
112+
JdbcTypeConverter.JdbcTypeBean typeBean =
113+
createTypeBean(jdbcTypeName, columnSize, scale, datetimePrecision);
97114
Assertions.assertEquals(gravitinoType, MYSQL_TYPE_CONVERTER.toGravitino(typeBean));
98115
}
99116

100117
protected static JdbcTypeConverter.JdbcTypeBean createTypeBean(
101-
String typeName, Integer columnSize, Integer scale) {
118+
String typeName, Integer columnSize, Integer scale, Integer datetimePrecision) {
102119
return new JdbcTypeConverter.JdbcTypeBean(typeName) {
103120
{
104121
setColumnSize(columnSize);
105122
setScale(scale);
123+
setDatetimePrecision(datetimePrecision);
106124
}
107125
};
108126
}

0 commit comments

Comments
 (0)