Skip to content

Commit 70217f5

Browse files
committed
Merge branch 'main' into 06/22/26/extract_request_logic
2 parents 7b2d5c1 + a5e9d08 commit 70217f5

51 files changed

Lines changed: 6821 additions & 702 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ public static Map<Class<?>, Integer> buildVariantMapping(List<ClickHouseDataType
177177
return variantMapping;
178178
}
179179

180-
static final Map<ClickHouseDataType, Set<Class<?>>> DATA_TYPE_TO_CLASS = dataTypeClassMap();
180+
public static final Map<ClickHouseDataType, Set<Class<?>>> DATA_TYPE_TO_CLASS = Collections.unmodifiableMap(dataTypeClassMap());
181181
static Map<ClickHouseDataType, Set<Class<?>>> dataTypeClassMap() {
182182
Map<ClickHouseDataType, Set<Class<?>>> map = new HashMap<>();
183183

client-v2/pom.xml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,26 @@
8888
<dependency>
8989
<groupId>com.fasterxml.jackson.core</groupId>
9090
<artifactId>jackson-databind</artifactId>
91-
<scope>test</scope>
9291
<version>${jackson.version}</version>
92+
<scope>provided</scope>
93+
</dependency>
94+
<dependency>
95+
<groupId>com.fasterxml.jackson.core</groupId>
96+
<artifactId>jackson-core</artifactId>
97+
<version>${jackson.version}</version>
98+
<scope>provided</scope>
99+
</dependency>
100+
<dependency>
101+
<groupId>com.fasterxml.jackson.core</groupId>
102+
<artifactId>jackson-annotations</artifactId>
103+
<version>${jackson.version}</version>
104+
<scope>provided</scope>
105+
</dependency>
106+
<dependency>
107+
<groupId>com.google.code.gson</groupId>
108+
<artifactId>gson</artifactId>
109+
<version>${gson.version}</version>
110+
<scope>provided</scope>
93111
</dependency>
94112
<dependency>
95113
<groupId>${project.parent.groupId}</groupId>

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

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.clickhouse.client.api.data_formats.internal.ProcessParser;
1313
import com.clickhouse.client.api.enums.Protocol;
1414
import com.clickhouse.client.api.enums.ProxyType;
15+
import com.clickhouse.client.api.enums.SSLMode;
1516
import com.clickhouse.client.api.http.ClickHouseHttpProto;
1617
import com.clickhouse.client.api.insert.InsertResponse;
1718
import com.clickhouse.client.api.insert.InsertSettings;
@@ -756,6 +757,34 @@ public Builder setClientKey(String path) {
756757
return this;
757758
}
758759

760+
/**
761+
* Defines how strictly the client verifies a server identity on secure connections.
762+
*
763+
* <p>Supported modes:</p>
764+
* <ul>
765+
* <li>{@link SSLMode#DISABLED} - SSL is not used; only meaningful with plain protocols</li>
766+
* <li>{@link SSLMode#TRUST} - encrypt, but accept any server certificate and skip
767+
* hostname verification; a configured trust store or CA certificate is ignored (a warning
768+
* is logged), while a client certificate/key is still applied for mTLS</li>
769+
* <li>{@link SSLMode#VERIFY_CA} - validate the server certificate chain, but skip
770+
* hostname verification</li>
771+
* <li>{@link SSLMode#STRICT} - full verification of the certificate chain and the
772+
* hostname (default)</li>
773+
* </ul>
774+
*
775+
* <p>The mode applies only when a secure protocol is in use - for the HTTP transport that
776+
* means an {@code https://} endpoint. Setting any mode does <b>not</b> make the client use
777+
* encryption on a plain HTTP endpoint: the endpoint scheme always decides whether the
778+
* connection is encrypted.</p>
779+
*
780+
* @param sslMode ssl mode
781+
* @return same instance of the builder
782+
*/
783+
public Builder setSSLMode(SSLMode sslMode) {
784+
this.configuration.put(ClientConfigProperties.SSL_MODE.getKey(), sslMode.name());
785+
return this;
786+
}
787+
759788
/**
760789
* Configure client to use server timezone for date/datetime columns. Default is true.
761790
* If this options is selected then server timezone should be set as well.
@@ -1141,6 +1170,36 @@ public Client build() {
11411170
throw new ClientMisconfigurationException("Trust store and certificates cannot be used together");
11421171
}
11431172

1173+
// A trust store and a CA certificate are not rejected here: for VERIFY_CA/STRICT the trust
1174+
// store takes precedence and the CA certificate is ignored with a warning (see createSSLContext).
1175+
1176+
// Resolve ssl_mode case-insensitively and normalize it to the canonical enum name so that
1177+
// downstream parsing is consistent and an unknown value is reported as a misconfiguration
1178+
// here instead of failing later with a generic enum-parsing error.
1179+
String sslModeValue = configuration.get(ClientConfigProperties.SSL_MODE.getKey());
1180+
if (sslModeValue != null) {
1181+
SSLMode sslMode;
1182+
try {
1183+
sslMode = SSLMode.fromValue(sslModeValue);
1184+
} catch (IllegalArgumentException e) {
1185+
throw new ClientMisconfigurationException("Invalid value '" + sslModeValue + "' for '"
1186+
+ ClientConfigProperties.SSL_MODE.getKey() + "'", e);
1187+
}
1188+
configuration.put(ClientConfigProperties.SSL_MODE.getKey(), sslMode.name());
1189+
1190+
// SSLMode.DISABLED does not turn encryption off - the endpoint scheme decides that. So it
1191+
// contradicts a secure (https) endpoint and must be rejected here, before the client is created.
1192+
if (sslMode == SSLMode.DISABLED) {
1193+
for (Endpoint endpoint : this.endpoints) {
1194+
if ("https".equalsIgnoreCase(endpoint.getURI().getScheme())) {
1195+
throw new ClientMisconfigurationException("SSL mode '" + SSLMode.DISABLED
1196+
+ "' cannot be used with a secure (https) endpoint. Use '" + SSLMode.TRUST
1197+
+ "' to trust all certificates or use plain HTTP.");
1198+
}
1199+
}
1200+
}
1201+
}
1202+
11441203
// Check timezone settings
11451204
String useTimeZoneValue = this.configuration.get(ClientConfigProperties.USE_TIMEZONE.getKey());
11461205
String serverTimeZoneValue = this.configuration.get(ClientConfigProperties.SERVER_TIMEZONE.getKey());
@@ -1641,6 +1700,7 @@ public CompletableFuture<QueryResponse> query(String sqlQuery, Map<String, Objec
16411700
if (requestSettings.getFormat() == null) {
16421701
requestSettings.setFormat(ClickHouseFormat.RowBinaryWithNamesAndTypes);
16431702
}
1703+
applyFormatSpecificSettings(requestSettings);
16441704
ClientStatisticsHolder clientStats = new ClientStatisticsHolder();
16451705
clientStats.start(ClientMetrics.OP_DURATION);
16461706

@@ -2226,6 +2286,30 @@ private Map<String, Object> buildRequestSettings(Map<String, Object> opSettings)
22262286
return requestSettings;
22272287
}
22282288

2289+
/**
2290+
* Applies format-specific server-side settings to the already merged request settings.
2291+
* Must be called after {@link #buildRequestSettings(Map)} and after the request format has been resolved
2292+
* (either provided by the caller or defaulted), so that the inspected format reflects the final value.
2293+
*
2294+
* <p>For {@link ClickHouseFormat#JSONEachRow}, callers may opt in to plain JSON numbers by setting
2295+
* {@link ClientConfigProperties#JSON_DISABLE_NUMBER_QUOTING}. Explicit server settings are otherwise
2296+
* left untouched.</p>
2297+
* <ul>
2298+
* <li>{@code output_format_json_quote_64bit_integers}</li>
2299+
* <li>{@code output_format_json_quote_64bit_floats}</li>
2300+
* <li>{@code output_format_json_quote_decimals}</li>
2301+
* </ul>
2302+
*/
2303+
private static void applyFormatSpecificSettings(QuerySettings requestSettings) {
2304+
boolean disableNumberQuoting = ClientConfigProperties.JSON_DISABLE_NUMBER_QUOTING
2305+
.getOrDefault(requestSettings.getAllSettings());
2306+
if (requestSettings.getFormat() == ClickHouseFormat.JSONEachRow && disableNumberQuoting) {
2307+
requestSettings.serverSetting("output_format_json_quote_64bit_integers", "0");
2308+
requestSettings.serverSetting("output_format_json_quote_64bit_floats", "0");
2309+
requestSettings.serverSetting("output_format_json_quote_decimals", "0");
2310+
}
2311+
}
2312+
22292313
private Duration durationSince(long sinceNanos) {
22302314
return Duration.ofNanos(System.nanoTime() - sinceNanos);
22312315
}

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

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

33
import com.clickhouse.client.api.data_formats.internal.AbstractBinaryFormatReader;
4+
import com.clickhouse.client.api.enums.SSLMode;
45
import com.clickhouse.client.api.internal.ClickHouseLZ4OutputStream;
56
import com.clickhouse.data.ClickHouseDataType;
67
import com.clickhouse.data.ClickHouseFormat;
@@ -115,6 +116,8 @@ public enum ClientConfigProperties {
115116

116117
SSL_CERTIFICATE("sslcert", String.class),
117118

119+
SSL_MODE("ssl_mode", SSLMode.class, SSLMode.STRICT.name()),
120+
118121
RETRY_ON_FAILURE("retry", Integer.class, "3"),
119122

120123
INPUT_OUTPUT_FORMAT("format", ClickHouseFormat.class),
@@ -190,6 +193,11 @@ public Object parseValue(String value) {
190193
*/
191194
HTTP_SEND_PARAMS_IN_BODY("client.http.use_form_request_for_query", Boolean.class, "false"),
192195

196+
/**
197+
* When enabled for JSONEachRow queries, asks ClickHouse to emit large integer,
198+
* floating-point, and decimal values as JSON numbers instead of quoted strings.
199+
*/
200+
JSON_DISABLE_NUMBER_QUOTING("json_disable_number_quoting", Boolean.class, "false"),
193201

194202
/**
195203
* Prefix for custom settings. Should be aligned with server configuration.

0 commit comments

Comments
 (0)