Skip to content

Commit 17b8770

Browse files
committed
added interval serialization/deserialization
1 parent 296d676 commit 17b8770

File tree

12 files changed

+238
-130
lines changed

12 files changed

+238
-130
lines changed

clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseDataType.java

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
package com.clickhouse.data;
22

3-
import java.lang.reflect.Array;
43
import java.math.BigDecimal;
54
import java.math.BigInteger;
65
import java.net.Inet4Address;
76
import java.net.Inet6Address;
87
import java.time.LocalDate;
98
import java.time.LocalDateTime;
109
import java.time.ZonedDateTime;
11-
import java.util.ArrayList;
10+
import java.time.temporal.ChronoUnit;
11+
import java.time.temporal.TemporalUnit;
1212
import java.util.Arrays;
13-
import java.util.Collection;
1413
import java.util.Collections;
1514
import java.util.Comparator;
16-
import java.util.EnumSet;
1715
import java.util.HashMap;
1816
import java.util.HashSet;
1917
import java.util.LinkedList;
@@ -247,36 +245,39 @@ private static Set<Class<?>> setOf(Class<?>... args) {
247245

248246
public static final byte TUPLE_WITH_NAMES_BIN_TAG = 0x20;
249247

250-
public enum IntervalKindBinTag {
251-
Nanosecond(IntervalNanosecond, 0x00),
252-
Microsecond(IntervalMicrosecond, 0x01),
248+
public enum IntervalKind {
249+
Nanosecond(IntervalNanosecond, ChronoUnit.NANOS, 0x00),
250+
Microsecond(IntervalMicrosecond, ChronoUnit.MICROS, 0x01),
253251

254-
Millisecond(IntervalMillisecond, 0x02),
252+
Millisecond(IntervalMillisecond, ChronoUnit.MILLIS, 0x02),
255253

256-
Second(IntervalSecond, 0x03),
254+
Second(IntervalSecond, ChronoUnit.SECONDS, 0x03),
257255

258-
Minute(IntervalMinute, 0x04),
256+
Minute(IntervalMinute, ChronoUnit.MINUTES, 0x04),
259257

260-
Hour(IntervalHour, 0x05),
258+
Hour(IntervalHour, ChronoUnit.HOURS, 0x05),
261259

262-
Day(IntervalDay, 0x06),
260+
Day(IntervalDay, ChronoUnit.DAYS, 0x06),
263261

264-
Week(IntervalWeek, 0x07),
262+
Week(IntervalWeek, ChronoUnit.WEEKS, 0x07),
265263

266-
Month(IntervalMonth, 0x08),
264+
Month(IntervalMonth, ChronoUnit.MONTHS, 0x08),
267265

268-
Quarter(IntervalQuarter, 0x09),
266+
Quarter(IntervalQuarter, null, 0x09),
269267

270-
Year(IntervalYear, 0x1A) // why 1A ?
268+
Year(IntervalYear, ChronoUnit.YEARS, 0x1A) // why 1A ?
271269

272270
;
273271

274272
private ClickHouseDataType intervalType;
275273

274+
private TemporalUnit temporalUnit;
275+
276276
byte tag;
277-
IntervalKindBinTag(ClickHouseDataType clickHouseDataType, int tag) {
277+
IntervalKind(ClickHouseDataType clickHouseDataType, TemporalUnit temporalUnit, int tag) {
278278
this.intervalType = clickHouseDataType;
279279
this.tag = (byte) tag;
280+
this.temporalUnit = temporalUnit;
280281
}
281282

282283
public ClickHouseDataType getIntervalType() {
@@ -286,6 +287,8 @@ public ClickHouseDataType getIntervalType() {
286287
public byte getTag() {
287288
return tag;
288289
}
290+
291+
public TemporalUnit getTemporalUnit() { return temporalUnit; }
289292
}
290293

291294

@@ -303,7 +306,7 @@ public byte getTag() {
303306

304307
public static final Map<Byte, ClickHouseDataType> intervalKind2Type;
305308

306-
public static final Map<ClickHouseDataType, Byte> intervalType2Kind;
309+
public static final Map<ClickHouseDataType, ClickHouseDataType.IntervalKind> intervalType2Kind;
307310

308311
static {
309312
Set<String> set = new TreeSet<>();
@@ -341,10 +344,10 @@ public byte getTag() {
341344
binTag2Type = Collections.unmodifiableMap(tmpbinTag2Type);
342345

343346
Map<Byte, ClickHouseDataType> tmpIntervalKind2Type = new HashMap<>();
344-
Map<ClickHouseDataType, Byte > tmpIntervalType2Kind = new HashMap<>();
345-
for (IntervalKindBinTag kind : IntervalKindBinTag.values()) {
347+
Map<ClickHouseDataType, ClickHouseDataType.IntervalKind > tmpIntervalType2Kind = new HashMap<>();
348+
for (IntervalKind kind : IntervalKind.values()) {
346349
tmpIntervalKind2Type.put(kind.getTag(), kind.getIntervalType());
347-
tmpIntervalType2Kind.put(kind.getIntervalType(), kind.tag);
350+
tmpIntervalType2Kind.put(kind.getIntervalType(), kind);
348351
}
349352
intervalKind2Type = Collections.unmodifiableMap(tmpIntervalKind2Type);
350353
intervalType2Kind = Collections.unmodifiableMap(tmpIntervalType2Kind);

client-v2/src/main/java/com/clickhouse/client/api/data_formats/ClickHouseBinaryFormatReader.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.net.Inet4Address;
1313
import java.net.Inet6Address;
1414
import java.time.*;
15+
import java.time.temporal.TemporalAmount;
1516
import java.util.List;
1617
import java.util.Map;
1718
import java.util.UUID;
@@ -540,4 +541,8 @@ public interface ClickHouseBinaryFormatReader extends AutoCloseable {
540541
ClickHouseBitmap getClickHouseBitmap(String colName);
541542

542543
ClickHouseBitmap getClickHouseBitmap(int index);
544+
545+
TemporalAmount getTemporalAmount(int index);
546+
547+
TemporalAmount getTemporalAmount(String colName);
543548
}

client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/AbstractBinaryFormatReader.java

Lines changed: 14 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.time.*;
3131
import java.time.format.DateTimeFormatter;
3232
import java.time.temporal.ChronoUnit;
33+
import java.time.temporal.TemporalAmount;
3334
import java.util.*;
3435
import java.util.concurrent.ConcurrentHashMap;
3536
import java.util.concurrent.atomic.AtomicBoolean;
@@ -414,38 +415,13 @@ public ZonedDateTime getZonedDateTime(String colName) {
414415

415416
@Override
416417
public Duration getDuration(String colName) {
417-
int colIndex = schema.nameToIndex(colName);
418-
ClickHouseColumn column = schema.getColumns().get(colIndex);
419-
BigInteger value = readValue(colName);
420-
try {
421-
switch (column.getDataType()) {
422-
case IntervalYear:
423-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.YEARS);
424-
case IntervalQuarter:
425-
return Duration.of(value.longValue() * 3, java.time.temporal.ChronoUnit.MONTHS);
426-
case IntervalMonth:
427-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.MONTHS);
428-
case IntervalWeek:
429-
return Duration.of(value.longValue(), ChronoUnit.WEEKS);
430-
case IntervalDay:
431-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.DAYS);
432-
case IntervalHour:
433-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.HOURS);
434-
case IntervalMinute:
435-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.MINUTES);
436-
case IntervalSecond:
437-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.SECONDS);
438-
case IntervalMicrosecond:
439-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.MICROS);
440-
case IntervalMillisecond:
441-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.MILLIS);
442-
case IntervalNanosecond:
443-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.NANOS);
444-
}
445-
} catch (ArithmeticException e) {
446-
throw new ClientException("Stored value is bigger then Long.MAX_VALUE and it cannot be converted to Duration without information loss", e);
447-
}
448-
throw new ClientException("Column of type " + column.getDataType() + " cannot be converted to Duration");
418+
TemporalAmount temporalAmount = getTemporalAmount(colName);
419+
return Duration.from(temporalAmount);
420+
}
421+
422+
@Override
423+
public TemporalAmount getTemporalAmount(String colName) {
424+
return readValue(colName);
449425
}
450426

451427
@Override
@@ -606,7 +582,12 @@ public ZonedDateTime getZonedDateTime(int index) {
606582

607583
@Override
608584
public Duration getDuration(int index) {
609-
return readValue(index);
585+
return getDuration(schema.columnIndexToName(index));
586+
}
587+
588+
@Override
589+
public TemporalAmount getTemporalAmount(int index) {
590+
return getTemporalAmount(schema.columnIndexToName(index));
610591
}
611592

612593
@Override

client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/BinaryReaderBackedRecord.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.net.Inet4Address;
1212
import java.net.Inet6Address;
1313
import java.time.*;
14+
import java.time.temporal.TemporalAmount;
1415
import java.util.List;
1516
import java.util.Map;
1617
import java.util.UUID;
@@ -89,6 +90,11 @@ public Duration getDuration(String colName) {
8990
return reader.getDuration(colName);
9091
}
9192

93+
@Override
94+
public TemporalAmount getTemporalAmount(String colName) {
95+
return reader.getTemporalAmount(colName);
96+
}
97+
9298
@Override
9399
public Inet4Address getInet4Address(String colName) {
94100
return reader.getInet4Address(colName);
@@ -234,6 +240,11 @@ public Duration getDuration(int index) {
234240
return reader.getDuration(index);
235241
}
236242

243+
@Override
244+
public TemporalAmount getTemporalAmount(int index) {
245+
return reader.getTemporalAmount(index);
246+
}
247+
237248
@Override
238249
public Inet4Address getInet4Address(int index) {
239250
return reader.getInet4Address(index);

client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/BinaryStreamReader.java

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@
1717
import java.net.Inet4Address;
1818
import java.net.Inet6Address;
1919
import java.nio.charset.StandardCharsets;
20+
import java.time.Duration;
2021
import java.time.Instant;
2122
import java.time.LocalDate;
2223
import java.time.LocalDateTime;
24+
import java.time.Period;
2325
import java.time.ZonedDateTime;
26+
import java.time.temporal.TemporalAmount;
2427
import java.util.ArrayList;
2528
import java.util.Arrays;
2629
import java.util.Collections;
@@ -189,7 +192,7 @@ public <T> T readValue(ClickHouseColumn column, Class<?> typeHint) throws IOExce
189192
case IntervalMicrosecond:
190193
case IntervalMillisecond:
191194
case IntervalNanosecond:
192-
return (T) readBigIntegerLE(8, true);
195+
return (T) readIntervalValue(dataType, input);
193196
case IPv4:
194197
// https://clickhouse.com/docs/en/sql-reference/data-types/ipv4
195198
return (T) Inet4Address.getByAddress(readNBytesLE(input, 4));
@@ -218,7 +221,6 @@ public <T> T readValue(ClickHouseColumn column, Class<?> typeHint) throws IOExce
218221
return convertArray(readArray(actualColumn), typeHint);
219222
case Map:
220223
return (T) readMap(actualColumn);
221-
// case Nested:
222224
case Tuple:
223225
return (T) readTuple(actualColumn);
224226
case Nothing:
@@ -242,6 +244,37 @@ public <T> T readValue(ClickHouseColumn column, Class<?> typeHint) throws IOExce
242244
}
243245
}
244246

247+
private TemporalAmount readIntervalValue(ClickHouseDataType dataType, InputStream input) throws IOException {
248+
BigInteger v = readBigIntegerLE(8, true);
249+
250+
switch (dataType) {
251+
case IntervalYear:
252+
return Period.ofYears(v.intValue());
253+
case IntervalQuarter:
254+
return Period.ofMonths(4 * v.intValue());
255+
case IntervalMonth:
256+
return Period.ofMonths(v.intValue());
257+
case IntervalWeek:
258+
return Period.ofWeeks(v.intValue());
259+
case IntervalDay:
260+
return Period.ofDays(v.intValue());
261+
case IntervalHour:
262+
return Duration.ofHours(v.longValue());
263+
case IntervalMinute:
264+
return Duration.ofMinutes(v.longValue());
265+
case IntervalSecond:
266+
return Duration.ofSeconds(v.longValue());
267+
case IntervalMicrosecond:
268+
return Duration.ofNanos(1000 * v.longValue());
269+
case IntervalMillisecond:
270+
return Duration.ofMillis(v.longValue());
271+
case IntervalNanosecond:
272+
return Duration.ofNanos(v.longValue());
273+
default:
274+
throw new ClientException("Unsupported interval type: " + dataType);
275+
}
276+
}
277+
245278
private static <T> T convertDateTime(ZonedDateTime value, Class<?> typeHint) {
246279
if (typeHint == null) {
247280
return (T) value;
@@ -925,10 +958,7 @@ public static ZonedDateTime readDateTime64(InputStream input, byte[] buff, int s
925958
}
926959
}
927960

928-
// Instant.ofEpochSecond()
929961
return Instant.ofEpochSecond(value, nanoSeconds).atZone(tz.toZoneId());
930-
// return LocalDateTime.ofInstant(Instant.ofEpochSecond(value, nanoSeconds), tz.toZoneId())
931-
// .atZone(tz.toZoneId());
932962
}
933963

934964
/**

client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/MapBackedRecord.java

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.clickhouse.client.api.query.GenericRecord;
66
import com.clickhouse.client.api.query.NullValueException;
77
import com.clickhouse.data.ClickHouseColumn;
8+
import com.clickhouse.data.ClickHouseDataType;
89
import com.clickhouse.data.value.*;
910

1011
import java.math.BigDecimal;
@@ -13,6 +14,7 @@
1314
import java.net.Inet6Address;
1415
import java.time.*;
1516
import java.time.temporal.ChronoUnit;
17+
import java.time.temporal.TemporalAmount;
1618
import java.util.HashMap;
1719
import java.util.List;
1820
import java.util.Map;
@@ -150,37 +152,12 @@ public ZonedDateTime getZonedDateTime(String colName) {
150152

151153
@Override
152154
public Duration getDuration(String colName) {
153-
ClickHouseColumn column = schema.getColumnByName(colName);
154-
BigInteger value = readValue(colName);
155-
try {
156-
switch (column.getDataType()) {
157-
case IntervalYear:
158-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.YEARS);
159-
case IntervalQuarter:
160-
return Duration.of(value.longValue() * 3, java.time.temporal.ChronoUnit.MONTHS);
161-
case IntervalMonth:
162-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.MONTHS);
163-
case IntervalWeek:
164-
return Duration.of(value.longValue(), ChronoUnit.WEEKS);
165-
case IntervalDay:
166-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.DAYS);
167-
case IntervalHour:
168-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.HOURS);
169-
case IntervalMinute:
170-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.MINUTES);
171-
case IntervalSecond:
172-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.SECONDS);
173-
case IntervalMicrosecond:
174-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.MICROS);
175-
case IntervalMillisecond:
176-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.MILLIS);
177-
case IntervalNanosecond:
178-
return Duration.of(value.longValue(), java.time.temporal.ChronoUnit.NANOS);
179-
}
180-
} catch (ArithmeticException e) {
181-
throw new ClientException("Stored value is bigger then Long.MAX_VALUE and it cannot be converted to Duration without information loss", e);
182-
}
183-
throw new ClientException("Column of type " + column.getDataType() + " cannot be converted to Duration");
155+
return readValue(colName);
156+
}
157+
158+
@Override
159+
public TemporalAmount getTemporalAmount(String colName) {
160+
return readValue(colName);
184161
}
185162

186163
@Override
@@ -339,6 +316,11 @@ public Duration getDuration(int index) {
339316
return readValue(index);
340317
}
341318

319+
@Override
320+
public TemporalAmount getTemporalAmount(int index) {
321+
return readValue(index);
322+
}
323+
342324
@Override
343325
public Inet4Address getInet4Address(int index) {
344326
return readValue(index);

0 commit comments

Comments
 (0)