Skip to content

Commit e1d46ff

Browse files
authored
Merge pull request #2501 from enqueue/datetime64
Instant / DateTime64 via parameter
2 parents 2f3b741 + 516ffb3 commit e1d46ff

File tree

10 files changed

+744
-37
lines changed

10 files changed

+744
-37
lines changed

client-v2/src/main/java/com/clickhouse/client/api/Client.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import com.clickhouse.client.api.data_formats.RowBinaryFormatReader;
88
import com.clickhouse.client.api.data_formats.RowBinaryWithNamesAndTypesFormatReader;
99
import com.clickhouse.client.api.data_formats.RowBinaryWithNamesFormatReader;
10-
import com.clickhouse.client.api.data_formats.internal.AbstractBinaryFormatReader;
1110
import com.clickhouse.client.api.data_formats.internal.BinaryStreamReader;
1211
import com.clickhouse.client.api.data_formats.internal.MapBackedRecord;
1312
import com.clickhouse.client.api.data_formats.internal.ProcessParser;
@@ -37,7 +36,6 @@
3736
import com.clickhouse.client.api.transport.Endpoint;
3837
import com.clickhouse.client.api.transport.HttpEndpoint;
3938
import com.clickhouse.client.config.ClickHouseClientOption;
40-
import com.clickhouse.config.ClickHouseOption;
4139
import com.clickhouse.data.ClickHouseColumn;
4240
import com.clickhouse.data.ClickHouseDataType;
4341
import com.clickhouse.data.ClickHouseFormat;
@@ -1575,7 +1573,7 @@ public CompletableFuture<QueryResponse> query(String sqlQuery, Map<String, Objec
15751573
Supplier<QueryResponse> responseSupplier;
15761574

15771575
if (queryParams != null) {
1578-
settings.setOption("statement_params", queryParams);
1576+
settings.setOption(HttpAPIClientHelper.KEY_STATEMENT_PARAMS, queryParams);
15791577
}
15801578
final QuerySettings finalSettings = new QuerySettings(buildRequestSettings(settings.getAllSettings()));
15811579
responseSupplier = () -> {
@@ -2027,6 +2025,7 @@ protected int getOperationTimeout() {
20272025
* @return - set of endpoints
20282026
* @deprecated
20292027
*/
2028+
@Deprecated
20302029
public Set<String> getEndpoints() {
20312030
return endpoints.stream().map(Endpoint::getBaseURL).collect(Collectors.toSet());
20322031
}
@@ -2100,4 +2099,5 @@ private Map<String, Object> buildRequestSettings(Map<String, Object> opSettings)
21002099
requestSettings.putAll(opSettings);
21012100
return requestSettings;
21022101
}
2102+
21032103
}

client-v2/src/main/java/com/clickhouse/client/api/DataTypeUtils.java

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
package com.clickhouse.client.api;
22

3+
import java.time.Instant;
4+
import java.time.ZoneId;
35
import java.time.format.DateTimeFormatter;
6+
import java.time.format.DateTimeFormatterBuilder;
7+
import java.time.temporal.ChronoField;
8+
import java.util.Objects;
9+
10+
import com.clickhouse.data.ClickHouseDataType;
411

512
public class DataTypeUtils {
613

@@ -19,4 +26,91 @@ public class DataTypeUtils {
1926
*/
2027
public static DateTimeFormatter DATETIME_WITH_NANOS_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.nnnnnnnnn");
2128

29+
private static final DateTimeFormatter INSTANT_FORMATTER = new DateTimeFormatterBuilder()
30+
.appendValue(ChronoField.INSTANT_SECONDS)
31+
.appendFraction(ChronoField.NANO_OF_SECOND, 9, 9, true)
32+
.toFormatter();
33+
34+
/**
35+
* Formats an {@link Instant} object for use in SQL statements or as query
36+
* parameter.
37+
*
38+
* @param instant
39+
* the Java object to format
40+
* @return a suitable String representation of {@code instant}
41+
* @throws NullPointerException
42+
* if {@code instant} is null
43+
*/
44+
public static String formatInstant(Instant instant) {
45+
return formatInstant(instant, null);
46+
}
47+
48+
/**
49+
* Formats an {@link Instant} object for use in SQL statements or as query
50+
* parameter.
51+
*
52+
* This method uses the {@code dataTypeHint} parameter to find the best
53+
* suitable format for the instant.
54+
*
55+
* @param instant
56+
* the Java object to format
57+
* @param dataTypeHint
58+
* the ClickHouse data type {@code instant} should be used for
59+
* @return a suitable String representation of {@code instant}
60+
* @throws NullPointerException
61+
* if {@code instant} is null
62+
*/
63+
public static String formatInstant(Instant instant, ClickHouseDataType dataTypeHint) {
64+
return formatInstant(instant, dataTypeHint, null);
65+
}
66+
67+
/**
68+
* Formats an {@link Instant} object for use in SQL statements or as query
69+
* parameter.
70+
*
71+
* This method uses the {@code dataTypeHint} parameter to find the best
72+
* suitable format for the instant.
73+
*
74+
* For <em>some</em> formatting operations, providing a {@code timeZone} is
75+
* mandatory, e.g. for {@link ClickHouseDataType#Date}.
76+
*
77+
* @param instant
78+
* the Java object to format
79+
* @param dataTypeHint
80+
* the ClickHouse data type {@code object} should be used for
81+
* @param timeZone
82+
* the time zone to be used when formatting the instant for use
83+
* in non-time-zone-based ClickHouse data types
84+
* @return a suitable String representation of {@code object}, or the empty
85+
* String for {@code null} objects
86+
* @throws NullPointerException
87+
* if {@code instant} is null
88+
*/
89+
public static String formatInstant(Instant instant, ClickHouseDataType dataTypeHint,
90+
ZoneId timeZone)
91+
{
92+
Objects.requireNonNull(instant, "Instant required for formatInstant");
93+
if (dataTypeHint == null) {
94+
return formatInstantDefault(instant);
95+
}
96+
switch (dataTypeHint) {
97+
case Date:
98+
case Date32:
99+
Objects.requireNonNull(
100+
timeZone,
101+
"TimeZone required for formatting Instant for '" + dataTypeHint + "' use");
102+
return DATE_FORMATTER.format(
103+
instant.atZone(timeZone).toLocalDate());
104+
case DateTime:
105+
case DateTime32:
106+
return String.valueOf(instant.getEpochSecond());
107+
default:
108+
return formatInstantDefault(instant);
109+
}
110+
}
111+
112+
private static String formatInstantDefault(Instant instant) {
113+
return INSTANT_FORMATTER.format(instant);
114+
}
115+
22116
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.clickhouse.client.api.data_formats.internal.SerializerUtils;
44
import com.clickhouse.data.ClickHouseColumn;
55
import com.clickhouse.data.ClickHouseDataType;
6-
import com.clickhouse.data.ClickHouseFormat;
76
import com.clickhouse.data.format.BinaryStreamUtils;
87

98
import java.io.IOException;
@@ -132,7 +131,7 @@ public void writeFixedString(String value, int len) throws IOException {
132131
}
133132

134133
public void writeDate(ZonedDateTime value) throws IOException {
135-
SerializerUtils.writeDate(out, value, ZoneId.of("UTC"));
134+
SerializerUtils.writeDate(out, value, value.getZone());
136135
}
137136

138137
public void writeDate32(ZonedDateTime value, ZoneId targetTz) throws IOException {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public <T> T readValue(ClickHouseColumn column, Class<?> typeHint) throws IOExce
104104
if (column.isNullable()) {
105105
int isNull = readByteOrEOF(input);
106106
if (isNull == 1) { // is Null?
107-
return (T) null;
107+
return null;
108108
}
109109
}
110110

@@ -588,7 +588,7 @@ public static byte[] readNBytesLE(InputStream input, byte[] buffer, int offset,
588588

589589
return bytes;
590590
}
591-
591+
592592
/**
593593
* Reads a array into an ArrayValue object.
594594
* @param column - column information
@@ -964,7 +964,7 @@ private ZonedDateTime readDateTime32(TimeZone tz) throws IOException {
964964
*/
965965
public static ZonedDateTime readDateTime32(InputStream input, byte[] buff, TimeZone tz) throws IOException {
966966
long time = readUnsignedIntLE(input, buff);
967-
return LocalDateTime.ofInstant(Instant.ofEpochSecond(Math.max(time, 0L)), tz.toZoneId()).atZone(tz.toZoneId());
967+
return Instant.ofEpochSecond(Math.max(time, 0L)).atZone(tz.toZoneId());
968968
}
969969

970970
/**

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

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@
1717
import org.objectweb.asm.MethodVisitor;
1818
import org.objectweb.asm.Opcodes;
1919
import org.objectweb.asm.Type;
20-
import org.slf4j.Logger;
21-
import org.slf4j.LoggerFactory;
22-
2320
import java.io.IOException;
2421
import java.io.OutputStream;
2522
import java.lang.reflect.Array;
@@ -29,7 +26,14 @@
2926
import java.net.Inet4Address;
3027
import java.net.Inet6Address;
3128
import java.sql.Timestamp;
32-
import java.time.*;
29+
import java.time.Duration;
30+
import java.time.Instant;
31+
import java.time.LocalDate;
32+
import java.time.LocalDateTime;
33+
import java.time.OffsetDateTime;
34+
import java.time.Period;
35+
import java.time.ZoneId;
36+
import java.time.ZonedDateTime;
3337
import java.util.Arrays;
3438
import java.util.Collections;
3539
import java.util.HashMap;
@@ -54,8 +58,6 @@
5458

5559
public class SerializerUtils {
5660

57-
private static final Logger LOG = LoggerFactory.getLogger(SerializerUtils.class);
58-
5961
public static void serializeData(OutputStream stream, Object value, ClickHouseColumn column) throws IOException {
6062
//Serialize the value to the stream based on the data type
6163
switch (column.getDataType()) {
@@ -1070,6 +1072,9 @@ public static void writeDate(OutputStream output, Object value, ZoneId targetTz)
10701072
} else if (value instanceof ZonedDateTime) {
10711073
ZonedDateTime dt = (ZonedDateTime) value;
10721074
epochDays = (int)dt.withZoneSameInstant(targetTz).toLocalDate().toEpochDay();
1075+
} else if (value instanceof OffsetDateTime) {
1076+
OffsetDateTime dt = (OffsetDateTime) value;
1077+
epochDays = (int) dt.atZoneSameInstant(targetTz).toLocalDate().toEpochDay();
10731078
} else {
10741079
throw new IllegalArgumentException("Cannot convert " + value + " to Long");
10751080
}

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@
9898
import java.util.regex.Pattern;
9999

100100
public class HttpAPIClientHelper {
101-
private static final Logger LOG = LoggerFactory.getLogger(Client.class);
101+
102+
public static final String KEY_STATEMENT_PARAMS = "statement_params";
103+
104+
private static final Logger LOG = LoggerFactory.getLogger(HttpAPIClientHelper.class);
102105

103106
private static final int ERROR_BODY_BUFFER_SIZE = 1024; // Error messages are usually small
104107

@@ -567,11 +570,9 @@ private void addQueryParams(URIBuilder req, Map<String, Object> requestConfig) {
567570
if (requestConfig.containsKey(ClientConfigProperties.QUERY_ID.getKey())) {
568571
req.addParameter(ClickHouseHttpProto.QPARAM_QUERY_ID, requestConfig.get(ClientConfigProperties.QUERY_ID.getKey()).toString());
569572
}
570-
if (requestConfig.containsKey("statement_params")) {
571-
Map<String, Object> params = (Map<String, Object>) requestConfig.get("statement_params");
572-
for (Map.Entry<String, Object> entry : params.entrySet()) {
573-
req.addParameter("param_" + entry.getKey(), String.valueOf(entry.getValue()));
574-
}
573+
if (requestConfig.containsKey(KEY_STATEMENT_PARAMS)) {
574+
Map<?, ?> params = (Map<?, ?>) requestConfig.get(KEY_STATEMENT_PARAMS);
575+
params.forEach((k, v) -> req.addParameter("param_" + k, String.valueOf(v)));
575576
}
576577

577578
boolean clientCompression = ClientConfigProperties.COMPRESS_CLIENT_REQUEST.getOrDefault(requestConfig);

0 commit comments

Comments
 (0)