Skip to content

Commit be40178

Browse files
feat: supported Date32, Datetime64, Timestamp64, Interval64
1 parent 2e6f428 commit be40178

File tree

18 files changed

+446
-108
lines changed

18 files changed

+446
-108
lines changed

jdbc/src/main/java/tech/ydb/jdbc/YdbJdbcCode.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ public class YdbJdbcCode {
88
private YdbJdbcCode() {
99
}
1010

11-
public static final int DECIMAL_22_9 = 10024;
11+
public static final int DATE_32 = YdbConst.SQL_KIND_PRIMITIVE + 24;
1212

13-
public static final int DECIMAL_31_9 = 10025;
13+
public static final int DATETIME_64 = YdbConst.SQL_KIND_PRIMITIVE + 25;
1414

15-
public static final int DECIMAL_35_9 = 10026;
15+
public static final int TIMESTAMP_64 = YdbConst.SQL_KIND_PRIMITIVE + 26;
16+
17+
public static final int INTERVAL_64 = YdbConst.SQL_KIND_PRIMITIVE + 27;
1618
}

jdbc/src/main/java/tech/ydb/jdbc/common/ColumnInfo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public ColumnInfo(String name, Type type) {
2828
this.isOptional = desc.isOptional();
2929
this.ydbType = desc.ydbType();
3030

31-
this.isTimestamp = ydbType == PrimitiveType.Timestamp;
31+
this.isTimestamp = ydbType == PrimitiveType.Timestamp || ydbType == PrimitiveType.Timestamp64;
3232
this.isNumber = ydbType == PrimitiveType.Int8 || ydbType == PrimitiveType.Uint8
3333
|| ydbType == PrimitiveType.Int16 || ydbType == PrimitiveType.Uint16
3434
|| ydbType == PrimitiveType.Int32 || ydbType == PrimitiveType.Uint32

jdbc/src/main/java/tech/ydb/jdbc/common/MappingGetters.java

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
import tech.ydb.table.values.Value;
3232

3333
public class MappingGetters {
34-
private MappingGetters() { }
34+
private MappingGetters() {
35+
}
3536

3637
@SuppressWarnings("Convert2Lambda")
3738
static Getters buildGetters(Type type) {
@@ -515,6 +516,12 @@ private static ValueToInstant valueToInstant(PrimitiveType id) {
515516
return ValueReader::getTimestamp;
516517
case TzTimestamp:
517518
return v -> v.getTzTimestamp().toInstant();
519+
case Date32:
520+
return v -> Instant.ofEpochSecond(v.getDate32().toEpochDay() * 24 * 60 * 60);
521+
case Datetime64:
522+
return v -> Instant.ofEpochSecond(v.getDatetime64().toEpochSecond(ZoneOffset.UTC));
523+
case Timestamp64:
524+
return ValueReader::getTimestamp64;
518525
default:
519526
return castToInstantNotSupported(id.name());
520527
}
@@ -708,6 +715,14 @@ private static ValueToObject valueToObject(PrimitiveType id) {
708715
return PrimitiveReader::getTzDatetime;
709716
case TzTimestamp:
710717
return PrimitiveReader::getTzTimestamp;
718+
case Date32:
719+
return PrimitiveReader::getDate32;
720+
case Datetime64:
721+
return PrimitiveReader::getDatetime64;
722+
case Timestamp64:
723+
return PrimitiveReader::getTimestamp64;
724+
case Interval64:
725+
return PrimitiveReader::getInterval64;
711726
default:
712727
// DyNumber
713728
return value -> {
@@ -870,6 +885,48 @@ private static ValueToClass valueToClass(PrimitiveType id) {
870885
return builder
871886
.register(Duration.class, ValueReader::getInterval)
872887
.build();
888+
case Date32:
889+
return builder
890+
.register(long.class, v -> v.getDate32().toEpochDay())
891+
.register(Long.class, v -> v.getDate32().toEpochDay())
892+
.register(LocalDate.class, ValueReader::getDate32)
893+
.register(LocalDateTime.class, v -> v.getDate32().atStartOfDay())
894+
.register(java.sql.Date.class, v -> java.sql.Date.valueOf(v.getDate32()))
895+
.register(java.sql.Timestamp.class, v -> java.sql.Timestamp.valueOf(v.getDate32().atStartOfDay()))
896+
.register(Instant.class, v -> v.getDate32().atStartOfDay(ZoneId.systemDefault()).toInstant())
897+
.register(java.util.Date.class, v -> java.util.Date.from(
898+
v.getDate32().atStartOfDay(ZoneId.systemDefault()).toInstant()))
899+
.build();
900+
case Datetime64:
901+
return builder
902+
.register(long.class, v -> v.getDatetime64().toEpochSecond(ZoneOffset.UTC))
903+
.register(Long.class, v -> v.getDatetime64().toEpochSecond(ZoneOffset.UTC))
904+
.register(LocalDate.class, v -> v.getDatetime64().toLocalDate())
905+
.register(LocalDateTime.class, ValueReader::getDatetime64)
906+
.register(java.sql.Date.class, v -> java.sql.Date.valueOf(v.getDatetime64().toLocalDate()))
907+
.register(java.sql.Timestamp.class, v -> java.sql.Timestamp.valueOf(v.getDatetime64()))
908+
.register(Instant.class, v -> v.getDatetime64().atZone(ZoneId.systemDefault()).toInstant())
909+
.register(java.util.Date.class, v -> java.util.Date.from(
910+
v.getDatetime64().atZone(ZoneId.systemDefault()).toInstant()))
911+
.build();
912+
case Timestamp64:
913+
return builder
914+
.register(long.class, v -> v.getTimestamp64().toEpochMilli())
915+
.register(Long.class, v -> v.getTimestamp64().toEpochMilli())
916+
.register(LocalDate.class, v -> v.getTimestamp64().atZone(ZoneId.systemDefault()).toLocalDate())
917+
.register(LocalDateTime.class, v -> v.getTimestamp64()
918+
.atZone(ZoneId.systemDefault()).toLocalDateTime())
919+
.register(java.sql.Date.class, v -> java.sql.Date
920+
.valueOf(v.getTimestamp64().atZone(ZoneId.systemDefault()).toLocalDate()))
921+
.register(java.sql.Timestamp.class, v -> java.sql.Timestamp
922+
.valueOf(v.getTimestamp64().atZone(ZoneId.systemDefault()).toLocalDateTime()))
923+
.register(Instant.class, ValueReader::getTimestamp64)
924+
.register(java.util.Date.class, v -> java.util.Date.from(v.getTimestamp64()))
925+
.build();
926+
case Interval64:
927+
return builder
928+
.register(Duration.class, ValueReader::getInterval64)
929+
.build();
873930
case TzDate:
874931
return builder
875932
.register(ZonedDateTime.class, ValueReader::getTzDate)

jdbc/src/main/java/tech/ydb/jdbc/common/MappingSetters.java

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
public class MappingSetters {
3838
private static final int DEFAULT_BUF_SIZE = 0x800;
3939

40-
private MappingSetters() { }
40+
private MappingSetters() {
41+
}
4142

4243
static Setters buildSetters(Type type) {
4344
return buildToValueImpl(type);
@@ -97,6 +98,14 @@ private static Setters buildToValueImpl(Type type) {
9798
return x -> PrimitiveValue.newTzDatetime(castAsZonedDateTime(id, x));
9899
case TzTimestamp:
99100
return x -> PrimitiveValue.newTzTimestamp(castAsZonedDateTime(id, x));
101+
case Date32:
102+
return x -> castToDate32(id, x);
103+
case Datetime64:
104+
return x -> castToDateTime64(id, x);
105+
case Timestamp64:
106+
return x -> castToTimestamp64(id, x);
107+
case Interval64:
108+
return x -> castToInterval64(id, x);
100109
default:
101110
return x -> {
102111
throw castNotSupported(id, x);
@@ -502,6 +511,108 @@ private static PrimitiveValue castToTimestamp(PrimitiveType type, Object x) thro
502511
throw castNotSupported(type, x);
503512
}
504513

514+
private static PrimitiveValue castToInterval64(PrimitiveType type, Object x) throws SQLException {
515+
if (x instanceof Duration) {
516+
return PrimitiveValue.newInterval64((Duration) x);
517+
} else if (x instanceof Long) {
518+
return PrimitiveValue.newInterval64((Long) x);
519+
} else if (x instanceof String) {
520+
Duration parsed;
521+
try {
522+
parsed = Duration.parse((String) x);
523+
} catch (DateTimeParseException e) {
524+
throw castNotSupported(type, x, e);
525+
}
526+
return PrimitiveValue.newInterval64(parsed);
527+
}
528+
throw castNotSupported(type, x);
529+
}
530+
531+
private static PrimitiveValue castToDate32(PrimitiveType type, Object x) throws SQLException {
532+
if (x instanceof Instant) {
533+
return PrimitiveValue.newDate32(((Instant) x).atZone(ZoneId.systemDefault()).toLocalDate());
534+
} else if (x instanceof LocalDateTime) {
535+
return PrimitiveValue.newDate32(((LocalDateTime) x).toLocalDate());
536+
} else if (x instanceof LocalDate) {
537+
return PrimitiveValue.newDate32((LocalDate) x);
538+
} else if (x instanceof Integer) {
539+
return PrimitiveValue.newDate32(LocalDate.ofEpochDay((Integer) x));
540+
} else if (x instanceof Long) {
541+
return PrimitiveValue.newDate32(LocalDate.ofEpochDay((Long) x));
542+
} else if (x instanceof Timestamp) {
543+
// Normalize date - use system timezone to detect correct date
544+
Instant instant = Instant.ofEpochMilli(((Timestamp) x).getTime());
545+
LocalDate ld = instant.atZone(ZoneId.systemDefault()).toLocalDate();
546+
return PrimitiveValue.newDate32(ld);
547+
} else if (x instanceof Date) {
548+
// Normalize date - use system timezone to detect correct date
549+
Instant instant = Instant.ofEpochMilli(((Date) x).getTime());
550+
LocalDate ld = instant.atZone(ZoneId.systemDefault()).toLocalDate();
551+
return PrimitiveValue.newDate32(ld);
552+
} else if (x instanceof String) {
553+
try {
554+
return PrimitiveValue.newDate32(LocalDate.parse((String) x));
555+
} catch (DateTimeParseException e) {
556+
throw castNotSupported(type, x, e);
557+
}
558+
}
559+
throw castNotSupported(type, x);
560+
}
561+
562+
private static PrimitiveValue castToDateTime64(PrimitiveType type, Object x) throws SQLException {
563+
if (x instanceof Instant) {
564+
return PrimitiveValue.newDatetime64(((Instant) x).atZone(ZoneId.systemDefault()).toLocalDateTime());
565+
} else if (x instanceof LocalDateTime) {
566+
return PrimitiveValue.newDatetime64(((LocalDateTime) x));
567+
} else if (x instanceof LocalDate) {
568+
return PrimitiveValue.newDatetime64(((LocalDate) x).atStartOfDay());
569+
} else if (x instanceof Long) {
570+
return PrimitiveValue.newDatetime64(LocalDateTime.ofEpochSecond((Long) x, 0, ZoneOffset.UTC));
571+
} else if (x instanceof Timestamp) {
572+
// Normalize date - use system timezone to detect correct date
573+
Instant instant = Instant.ofEpochMilli(((Timestamp) x).getTime());
574+
LocalDateTime ldt = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
575+
return PrimitiveValue.newDatetime64(ldt);
576+
} else if (x instanceof Date) {
577+
// Normalize date - use system timezone to detect correct date
578+
Instant instant = Instant.ofEpochMilli(((Date) x).getTime());
579+
LocalDate ld = instant.atZone(ZoneId.systemDefault()).toLocalDate();
580+
return PrimitiveValue.newDatetime64(ld.atStartOfDay());
581+
} else if (x instanceof String) {
582+
try {
583+
return PrimitiveValue.newDatetime64(LocalDateTime.parse((String) x));
584+
} catch (DateTimeParseException e) {
585+
throw castNotSupported(type, x, e);
586+
}
587+
}
588+
throw castNotSupported(type, x);
589+
}
590+
591+
private static PrimitiveValue castToTimestamp64(PrimitiveType type, Object x) throws SQLException {
592+
if (x instanceof Instant) {
593+
return PrimitiveValue.newTimestamp64((Instant) x);
594+
} else if (x instanceof Long) {
595+
return PrimitiveValue.newTimestamp64(Instant.ofEpochMilli((Long) x));
596+
} else if (x instanceof LocalDate) {
597+
return PrimitiveValue.newTimestamp64(((LocalDate) x).atStartOfDay().toInstant(ZoneOffset.UTC));
598+
} else if (x instanceof LocalDateTime) {
599+
long epochSeconds = ((LocalDateTime) x).toEpochSecond(ZoneOffset.UTC);
600+
return PrimitiveValue.newTimestamp64(Instant.ofEpochSecond(epochSeconds));
601+
} else if (x instanceof Timestamp) {
602+
return PrimitiveValue.newTimestamp64(((Timestamp) x).toInstant());
603+
} else if (x instanceof Date) {
604+
Instant instant = ((Date) x).toLocalDate().atStartOfDay().toInstant(ZoneOffset.UTC);
605+
return PrimitiveValue.newTimestamp64(instant);
606+
} else if (x instanceof String) {
607+
try {
608+
return PrimitiveValue.newTimestamp64(Instant.parse((String) x));
609+
} catch (DateTimeParseException e) {
610+
throw castNotSupported(type, x, e);
611+
}
612+
}
613+
throw castNotSupported(type, x);
614+
}
615+
505616
private static DecimalValue validateValue(DecimalType type, DecimalValue value, Object x) throws SQLException {
506617
if (value.isNan()) {
507618
throw new SQLException(String.format(YdbConst.UNABLE_TO_CAST_TO_DECIMAL, type, toString(x), "NaN"));

jdbc/src/main/java/tech/ydb/jdbc/impl/YdbTypes.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.util.UUID;
1818

1919
import tech.ydb.jdbc.YdbConst;
20+
import tech.ydb.jdbc.YdbJdbcCode;
2021
import tech.ydb.table.values.DecimalType;
2122
import tech.ydb.table.values.DecimalValue;
2223
import tech.ydb.table.values.PrimitiveType;
@@ -65,6 +66,11 @@ private YdbTypes() {
6566

6667
typeBySqlType.put(YdbConst.SQL_KIND_PRIMITIVE + 23, PrimitiveType.JsonDocument);
6768

69+
typeBySqlType.put(YdbJdbcCode.DATE_32, PrimitiveType.Date32);
70+
typeBySqlType.put(YdbJdbcCode.DATETIME_64, PrimitiveType.Datetime64);
71+
typeBySqlType.put(YdbJdbcCode.TIMESTAMP_64, PrimitiveType.Timestamp64);
72+
typeBySqlType.put(YdbJdbcCode.INTERVAL_64, PrimitiveType.Interval64);
73+
6874
typeBySqlType.put(Types.VARCHAR, PrimitiveType.Text);
6975
typeBySqlType.put(Types.BIGINT, PrimitiveType.Int64);
7076
typeBySqlType.put(Types.TINYINT, PrimitiveType.Int8);
@@ -192,16 +198,19 @@ private int toSqlTypeImpl(Type type) {
192198
case Int64:
193199
case Uint64:
194200
case Interval:
201+
case Interval64:
195202
return Types.BIGINT;
196203
case Float:
197204
return Types.FLOAT;
198205
case Double:
199206
return Types.DOUBLE;
200207
case Date:
208+
case Date32:
201209
return Types.DATE;
202210
case Datetime:
203-
return Types.TIMESTAMP;
204211
case Timestamp:
212+
case Datetime64:
213+
case Timestamp64:
205214
return Types.TIMESTAMP;
206215
case TzDate:
207216
case TzDatetime:
@@ -294,7 +303,11 @@ private List<Type> getAllDatabaseTypesImpl() {
294303
PrimitiveType.Datetime,
295304
PrimitiveType.Timestamp,
296305
PrimitiveType.Interval,
297-
DecimalType.getDefault());
306+
DecimalType.getDefault(),
307+
PrimitiveType.Date32,
308+
PrimitiveType.Datetime64,
309+
PrimitiveType.Timestamp64,
310+
PrimitiveType.Interval64);
298311
}
299312

300313
private int getSqlPrecisionImpl(PrimitiveType type) {
@@ -314,6 +327,7 @@ private int getSqlPrecisionImpl(PrimitiveType type) {
314327
case Uint64:
315328
case Double:
316329
case Interval:
330+
case Interval64:
317331
return 8;
318332
case Bytes:
319333
case Text:
@@ -324,10 +338,13 @@ private int getSqlPrecisionImpl(PrimitiveType type) {
324338
case Uuid:
325339
return 8 + 8;
326340
case Date:
341+
case Date32:
327342
return "0000-00-00".length();
328343
case Datetime:
344+
case Datetime64:
329345
return "0000-00-00 00:00:00".length();
330346
case Timestamp:
347+
case Timestamp64:
331348
return "0000-00-00T00:00:00.000000".length();
332349
case TzDate:
333350
return "0000-00-00+00:00".length();

0 commit comments

Comments
 (0)