Skip to content

Commit b565db0

Browse files
committed
Merge branch 'main' into 06/10/26/jsonl_support
2 parents 9f4287b + 2c1a01d commit b565db0

27 files changed

Lines changed: 1436 additions & 126 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929

3030
### New Features
3131

32+
- **[client-v2]** Added `Session` API to encapsulate and manage ClickHouse session settings (`session_id`, `session_check`, `session_timeout`, `session_timezone`) as a reusable object. The `Session` instance can be applied to any request settings using `applyTo()`, and session state can be cleared via `clearSession()`. Additionally, added `resetOption(String)` to `InsertSettings`, `QuerySettings`, and `CommonSettings` to allow removing specific settings. Settings explicitly set to `null` will not be sent to the server, which is useful for overriding global settings.
33+
3234
- **[client-v2]** Added runtime credential update APIs on `Client`: `updateUserAndPassword(String, String)`, `updateAccessToken(String)`, and `updateBearerToken(String)`. Subsequent requests on the same `Client` instance use the new credentials without rebuilding the client. The authentication method is fixed at construction time; calling a runtime updater that does not match the configured method throws `ClientMisconfigurationException`. See `docs/authentication.md` for details and migration guidance.
3335

3436
- **[jdbc-v2]** Added `cluster_name` configuration property to specify a target cluster for statements like `KILL QUERY` that require an `ON CLUSTER` clause to execute across all nodes. (https://github.com/ClickHouse/clickhouse-java/issues/2837)
@@ -39,7 +41,7 @@
3941

4042
### Bug Fixes
4143

42-
- **[jdbc-v2]** Fixed `Statement.cancel()` throwing `SESSION_IS_LOCKED` when the statement was running inside a ClickHouse session (e.g. via `clickhouse_setting_session_id`). The `KILL QUERY` request issued by `cancel()` now runs outside the session, so it no longer contends with the running query for the session lock. (https://github.com/ClickHouse/clickhouse-java/issues/2690)
44+
- **[jdbc-v2]** Fixed `Statement.cancel()` throwing `SESSION_IS_LOCKED` when the statement was running inside a ClickHouse session. The driver now accepts `session_id`, `session_check`, and `session_timeout` as first-class connection properties and correctly suppresses them when issuing a `KILL QUERY` during cancellation. This ensures the cancellation request runs outside the session and no longer contends with the running query for the session lock. (https://github.com/ClickHouse/clickhouse-java/issues/2690, https://github.com/ClickHouse/clickhouse-java/issues/2881)
4345

4446
- **[client-v2]** Fixed inconsistent use of `executionTimeout` parameter in `Client` component. The timeout was previously set in milliseconds but mistakenly retrieved and used in seconds in some places. Now it correctly uses milliseconds consistently. (https://github.com/ClickHouse/clickhouse-java/issues/2358)
4547

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

Lines changed: 60 additions & 7 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;
@@ -755,6 +756,34 @@ public Builder setClientKey(String path) {
755756
return this;
756757
}
757758

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

1172+
// A trust store and a CA certificate are not rejected here: for VERIFY_CA/STRICT the trust
1173+
// store takes precedence and the CA certificate is ignored with a warning (see createSSLContext).
1174+
1175+
// Resolve ssl_mode case-insensitively and normalize it to the canonical enum name so that
1176+
// downstream parsing is consistent and an unknown value is reported as a misconfiguration
1177+
// here instead of failing later with a generic enum-parsing error.
1178+
String sslModeValue = configuration.get(ClientConfigProperties.SSL_MODE.getKey());
1179+
if (sslModeValue != null) {
1180+
SSLMode sslMode;
1181+
try {
1182+
sslMode = SSLMode.fromValue(sslModeValue);
1183+
} catch (IllegalArgumentException e) {
1184+
throw new ClientMisconfigurationException("Invalid value '" + sslModeValue + "' for '"
1185+
+ ClientConfigProperties.SSL_MODE.getKey() + "'", e);
1186+
}
1187+
configuration.put(ClientConfigProperties.SSL_MODE.getKey(), sslMode.name());
1188+
1189+
// SSLMode.DISABLED does not turn encryption off - the endpoint scheme decides that. So it
1190+
// contradicts a secure (https) endpoint and must be rejected here, before the client is created.
1191+
if (sslMode == SSLMode.DISABLED) {
1192+
for (Endpoint endpoint : this.endpoints) {
1193+
if ("https".equalsIgnoreCase(endpoint.getURI().getScheme())) {
1194+
throw new ClientMisconfigurationException("SSL mode '" + SSLMode.DISABLED
1195+
+ "' cannot be used with a secure (https) endpoint. Use '" + SSLMode.TRUST
1196+
+ "' to trust all certificates or use plain HTTP.");
1197+
}
1198+
}
1199+
}
1200+
}
1201+
11431202
// Check timezone settings
11441203
String useTimeZoneValue = this.configuration.get(ClientConfigProperties.USE_TIMEZONE.getKey());
11451204
String serverTimeZoneValue = this.configuration.get(ClientConfigProperties.SERVER_TIMEZONE.getKey());
@@ -1671,15 +1730,9 @@ public CompletableFuture<QueryResponse> query(String sqlQuery, Map<String, Objec
16711730
for (int i = 0; i <= retries; i++) {
16721731
ClassicHttpResponse httpResponse = null;
16731732
try {
1674-
boolean useMultipart = ClientConfigProperties.HTTP_SEND_PARAMS_IN_BODY.getOrDefault(requestSettings.getAllSettings());
1675-
if (queryParams != null && useMultipart) {
1676-
httpResponse = httpClientHelper.executeMultiPartRequest(selectedEndpoint,
1677-
requestSettings.getAllSettings(), sqlQuery);
1678-
} else {
1679-
httpResponse = httpClientHelper.executeRequest(selectedEndpoint,
1733+
httpResponse = httpClientHelper.executeRequest(selectedEndpoint,
16801734
requestSettings.getAllSettings(),
16811735
sqlQuery);
1682-
}
16831736
// Check response
16841737
if (httpResponse.getCode() == HttpStatus.SC_SERVICE_UNAVAILABLE) {
16851738
LOG.warn("Failed to get response. Server returned {}. Retrying. (Duration: {})", httpResponse.getCode(), durationSince(startTime));

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

Lines changed: 3 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),

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public synchronized void updateSessionId(String sessionId) {
8383
setSessionId(sessionId);
8484
}
8585

86-
public synchronized void applyTo(Map<String, Object> requestSettings) {
86+
public synchronized void applyTo(Map<? super String, Object> requestSettings) {
8787
putIfSet(requestSettings, ClickHouseHttpProto.QPARAM_SESSION_ID, sessionId);
8888
putIfSet(requestSettings, ClickHouseHttpProto.QPARAM_SESSION_CHECK,
8989
sessionCheck == null ? null : (sessionCheck ? "1" : "0"));
@@ -92,7 +92,15 @@ public synchronized void applyTo(Map<String, Object> requestSettings) {
9292
putIfSet(requestSettings, ClickHouseHttpProto.QPARAM_SESSION_TIMEZONE, sessionTimezone);
9393
}
9494

95-
private static void putIfSet(Map<String, Object> settings, String key, String value) {
95+
public static void clearSession(Map<String, Object> settings) {
96+
settings.put(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_SESSION_ID), null);
97+
settings.put(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_SESSION_TIMEOUT), null);
98+
settings.put(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_SESSION_CHECK), null);
99+
// Do not clean `session_timezone` setting because it is not related to session management and used to
100+
// set timezone for consequent queries in some multi-user applications.
101+
}
102+
103+
private static void putIfSet(Map<? super String, Object> settings, String key, String value) {
96104
if (value != null) {
97105
settings.put(ClientConfigProperties.serverSetting(key), value);
98106
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.clickhouse.client.api.enums;
2+
3+
/**
4+
* Defines how strictly the client verifies a server identity when a secure protocol is used.
5+
*
6+
* <p>The mode affects only connections that are already using a secure transport (for example,
7+
* an {@code https://} endpoint). It does <b>not</b> enable encryption for plain protocols - an
8+
* {@code http://} endpoint stays unencrypted whatever the mode is.</p>
9+
*
10+
* <p>Modes from the least to the most strict:</p>
11+
* <ul>
12+
* <li>{@link #DISABLED} - SSL is not used. Plain protocols only.</li>
13+
* <li>{@link #TRUST} - the hostname is not verified and any server certificate is accepted, which
14+
* is susceptible to MITM attacks - use that only for testing or in fully trusted environments. A
15+
* configured trust store or CA certificate has no effect in this mode and is ignored (a warning is
16+
* logged); a configured client certificate/key is still applied for mTLS.</li>
17+
* <li>{@link #VERIFY_CA} - the server certificate chain is validated against the trust material
18+
* (default JVM trust store, configured trust store, or a CA certificate), but the hostname is
19+
* not checked against the certificate.</li>
20+
* <li>{@link #STRICT} - full verification (default): certificate chain is validated and the
21+
* hostname must match the certificate.</li>
22+
* </ul>
23+
*/
24+
public enum SSLMode {
25+
26+
/**
27+
* SSL is not used. Connection is not encrypted. Doesn't work with HTTPS.
28+
* Reserved for TCP where protocol doesn't define encryption.
29+
*/
30+
DISABLED,
31+
32+
/**
33+
* The hostname is not verified and any server certificate is accepted. A configured trust store or
34+
* CA certificate has no effect in this mode and is ignored (a warning is logged). A configured
35+
* client certificate/key is still applied for mTLS.
36+
*/
37+
TRUST,
38+
39+
/**
40+
* Server certificate chain is validated, but the hostname is not verified.
41+
*/
42+
VERIFY_CA,
43+
44+
/**
45+
* Full verification: certificate chain is validated and the hostname must match
46+
* the certificate. Default mode for HTTPs.
47+
*/
48+
STRICT;
49+
50+
/**
51+
* Case-insensitive variant of {@link #valueOf(String)}.
52+
*
53+
* @param value mode name in any case
54+
* @return matching mode
55+
* @throws IllegalArgumentException when the value does not match any mode
56+
*/
57+
public static SSLMode fromValue(String value) {
58+
for (SSLMode mode : values()) {
59+
if (mode.name().equalsIgnoreCase(value)) {
60+
return mode;
61+
}
62+
}
63+
throw new IllegalArgumentException("Unknown SSL mode '" + value + "'");
64+
}
65+
}

client-v2/src/main/java/com/clickhouse/client/api/insert/InsertSettings.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ public InsertSettings setOption(String option, Object value) {
6060
return this;
6161
}
6262

63+
/**
64+
* Removes options from the settings.
65+
* @param option - configuration option name
66+
*/
67+
public InsertSettings resetOption(String option) {
68+
settings.resetOption(option);
69+
return this;
70+
}
71+
6372
/**
6473
* Get all settings as an unmodifiable map.
6574
*

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,7 @@ public CommonSettings use(Session session) {
140140
}
141141

142142
public void clearSession() {
143-
resetOption(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_SESSION_ID));
144-
resetOption(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_SESSION_CHECK));
145-
resetOption(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_SESSION_TIMEOUT));
146-
// Do not clean `session_timezone` setting because it is not related to session management and used to
147-
// set timezone for consequent queries in some multi-user applications.
143+
Session.clearSession(settings);
148144
}
149145

150146
/**

0 commit comments

Comments
 (0)