Skip to content

Commit 9cff77a

Browse files
authored
Merge pull request #2829 from ClickHouse/04/16/26/jdbc_override_settings
[jdbc-v2] Fixed overriding server settings in JDBC Connection
2 parents e9a4219 + c6b95e1 commit 9cff77a

8 files changed

Lines changed: 137 additions & 26 deletions

File tree

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
## 0.10.9
22

3+
### Breaking Changes
4+
5+
- **[jdbc-v2]** The driver no longer hardcodes the server settings `async_insert=0` and `wait_end_of_query=0` on every JDBC
6+
connection. This unblocks two scenarios that previously did not work: overriding these settings per connection or per
7+
statement, and using the driver against read-only profiles that disallow `SETTINGS` overrides. There are two consequences:
8+
- The driver now follows the server-side defaults for these settings (note: starting with ClickHouse 26.3, `async_insert`
9+
defaults to `1`). Removing the explicit `wait_end_of_query=0` is a no-op against server defaults but lets users opt in to
10+
`wait_end_of_query=1`.
11+
- The row count returned by `java.sql.Statement.executeUpdate(java.lang.String)` (and the matching `PreparedStatement`
12+
method) is no longer guaranteed to be accurate for INSERT statements when the server runs them asynchronously, and
13+
parsing/data errors in the INSERT body may not surface synchronously as a `SQLException`. Previously these were
14+
accurate because inserts were forced to be synchronous (see also https://github.com/ClickHouse/ClickHouse/issues/57768).
15+
To restore the previous behavior, set `async_insert=0` (or `wait_for_async_insert=1`) per connection or statement.
16+
Read more about asynchronous insert: https://clickhouse.com/docs/optimize/asynchronous-inserts.
17+
18+
(https://github.com/ClickHouse/clickhouse-java/issues/2652, https://github.com/ClickHouse/clickhouse-java/issues/2825)
19+
320
### New Features
421

522
- **[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)

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,9 @@ public final class ServerSettings {
4646
public static final String ASYNC_INSERT = "async_insert";
4747

4848
public static final String WAIT_ASYNC_INSERT = "wait_for_async_insert";
49+
50+
// Misc
51+
public static final String ON = "1";
52+
53+
public static final String OFF = "0";
4954
}

docs/features.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,4 @@ Compatibility-sensitive traits:
8282
- `getString()` formatting for temporal values is stable output: `Date` uses `yyyy-MM-dd`, `DateTime` uses `yyyy-MM-dd HH:mm:ss`, and `DateTime64` preserves fractional precision, all interpreted in server timezone context where applicable.
8383
- Date and timestamp setters with `Calendar` are timezone-sensitive by design. Preserving the current day-shift and instant-preserving behavior is important for compatibility.
8484
- `setObject()` temporal behavior is specific and should not drift: `LocalDateTime` and `Instant` are rendered through `fromUnixTimestamp64Nano(...)`, while `Timestamp` and `Date` use quoted textual forms.
85+
- INSERT result semantics depend on server-side `async_insert` and `wait_for_async_insert`. The driver does not override these settings, so it follows whatever the server profile or user configuration sets. When `async_insert=1` and `wait_for_async_insert=0`, `Statement.executeUpdate(...)` and `PreparedStatement.executeUpdate(...)` may return `0` (or an under-counted value), and parsing/data errors in the INSERT body may not be reported synchronously as a `SQLException`. Set `async_insert=0` (or `wait_for_async_insert=1`) per connection or statement to restore synchronous row counts and error reporting.

jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import com.clickhouse.client.api.Client;
44
import com.clickhouse.client.api.ClientConfigProperties;
5-
import com.clickhouse.client.api.internal.ServerSettings;
65
import com.clickhouse.client.api.metadata.TableSchema;
76
import com.clickhouse.client.api.query.GenericRecord;
87
import com.clickhouse.client.api.query.QuerySettings;
@@ -111,9 +110,7 @@ public ConnectionImpl(String url, Properties info) throws SQLException {
111110
this.client.loadServerInfo();
112111
}
113112
this.schema = client.getDefaultDatabase();
114-
this.defaultQuerySettings = new QuerySettings()
115-
.serverSetting(ServerSettings.ASYNC_INSERT, "0")
116-
.serverSetting(ServerSettings.WAIT_END_OF_QUERY, "0");
113+
this.defaultQuerySettings = new QuerySettings();
117114

118115
this.metadata = new DatabaseMetaDataImpl(this, false, url);
119116
this.defaultCalendar = Calendar.getInstance();

jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcConfiguration.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public boolean isIgnoreUnsupportedRequests() {
7474

7575
/**
7676
* Parses URL to get property and target host.
77-
* Properties that are passed in the {@code info} parameter will override that are set in the {@code url}.
77+
* Properties that are passed in the {@code url} will override the {@code info} ones.
7878
* @param url - JDBC url
7979
* @param info - Driver and Client properties.
8080
*/
@@ -89,7 +89,7 @@ public JdbcConfiguration(String url, Properties info) throws SQLException {
8989

9090
Map<String, String> urlProperties = parseUrl(url);
9191
String tmpConnectionUrl = urlProperties.remove(PARSE_URL_CONN_URL_PROP);
92-
initProperties(urlProperties, props);
92+
buildFinalProperties(urlProperties, props);
9393

9494
// after initializing all properties - set final connection URL
9595
boolean useSSLInfo = Boolean.parseBoolean(props.getProperty(DriverProperties.SECURE_CONNECTION.getKey(), "false"));
@@ -266,7 +266,12 @@ private Map<String, String> parseUrl(String url) throws SQLException {
266266
return properties;
267267
}
268268

269-
private void initProperties(Map<String, String> urlProperties, Properties providedProperties) {
269+
/**
270+
* Combines url properties and provided ones via {@link java.sql.Driver#connect(String, Properties)}
271+
* @param urlProperties - properties parsed from URL
272+
* @param providedProperties - properties object provided by application
273+
*/
274+
private void buildFinalProperties(Map<String, String> urlProperties, Properties providedProperties) {
270275

271276
// Copy provided properties
272277
Map<String, String> props = new HashMap<>();

jdbc-v2/src/test/java/com/clickhouse/jdbc/JdbcIntegrationTest.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,24 @@
44
import com.clickhouse.client.ClickHouseProtocol;
55
import com.clickhouse.client.ClickHouseServerForTest;
66
import com.clickhouse.client.api.ClientConfigProperties;
7-
import com.clickhouse.client.api.query.GenericRecord;
7+
import com.clickhouse.client.api.internal.ServerSettings;
88
import com.clickhouse.data.ClickHouseVersion;
99
import com.clickhouse.logging.Logger;
1010
import com.clickhouse.logging.LoggerFactory;
1111

1212
import java.sql.Connection;
1313
import java.sql.SQLException;
1414
import java.sql.Statement;
15-
import java.util.List;
15+
import java.util.Map;
1616
import java.util.Properties;
1717

1818
public abstract class JdbcIntegrationTest extends BaseIntegrationTest {
1919
private static final Logger LOGGER = LoggerFactory.getLogger(JdbcIntegrationTest.class);
2020

21+
public static final String WAIT_ASYNC_SETTING_KEY = DriverProperties.serverSetting(ServerSettings.WAIT_ASYNC_INSERT);
22+
public static final String WAIT_QUERY_SETTING_KEY = DriverProperties.serverSetting(ServerSettings.WAIT_END_OF_QUERY);
23+
public static final String ASYNC_INSERT_SETTING_KEY = DriverProperties.serverSetting(ServerSettings.ASYNC_INSERT);
24+
2125
public String getEndpointString() {
2226
return getEndpointString(isCloud());
2327
}
@@ -28,7 +32,15 @@ public String getEndpointString(boolean includeDbName) {
2832
}
2933

3034
public Connection getJdbcConnection() throws SQLException {
31-
return getJdbcConnection(null);
35+
return getJdbcConnection((Properties) null);
36+
}
37+
38+
public Connection getJdbcConnection(Map<String, Object> propertiesMap) throws SQLException {
39+
Properties config = new Properties();
40+
if (propertiesMap != null && !propertiesMap.isEmpty()) {
41+
config.putAll(propertiesMap);
42+
}
43+
return getJdbcConnection(config);
3244
}
3345

3446
public Connection getJdbcConnection(Properties properties) throws SQLException {

jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java

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

33
import com.clickhouse.client.api.DataTypeUtils;
4+
import com.clickhouse.client.api.internal.ServerSettings;
45
import com.clickhouse.data.ClickHouseColumn;
56
import com.clickhouse.data.ClickHouseDataType;
67
import com.clickhouse.data.ClickHouseVersion;
@@ -956,7 +957,7 @@ void testBatchInsertTextStatement(String sql) throws Exception {
956957
String table = "test_batch_text";
957958
long seed = System.currentTimeMillis();
958959
Random rnd = new Random(seed);
959-
try (Connection conn = getJdbcConnection()) {
960+
try (Connection conn = getJdbcConnection(Map.of(ASYNC_INSERT_SETTING_KEY, ServerSettings.OFF))) {
960961

961962
try (Statement stmt = conn.createStatement()) {
962963
stmt.execute("CREATE TABLE IF NOT EXISTS " + table +
@@ -1007,7 +1008,7 @@ void testBatchInsertNoValuesReuse() throws Exception {
10071008
String sql = "INSERT INTO %s (v1, v2) VALUES (?, ?)";
10081009
long seed = System.currentTimeMillis();
10091010
Random rnd = new Random(seed);
1010-
try (Connection conn = getJdbcConnection()) {
1011+
try (Connection conn = getJdbcConnection(Map.of(ASYNC_INSERT_SETTING_KEY, ServerSettings.OFF))) {
10111012

10121013
try (Statement stmt = conn.createStatement()) {
10131014
stmt.execute("CREATE TABLE IF NOT EXISTS " + table +
@@ -1062,7 +1063,7 @@ void testBatchInsertValuesReuse() throws Exception {
10621063
String sql = "INSERT INTO %s (v1, v2) VALUES (1, ?)";
10631064
long seed = System.currentTimeMillis();
10641065
Random rnd = new Random(seed);
1065-
try (Connection conn = getJdbcConnection()) {
1066+
try (Connection conn = getJdbcConnection(Map.of(ASYNC_INSERT_SETTING_KEY, ServerSettings.OFF))) {
10661067

10671068
try (Statement stmt = conn.createStatement()) {
10681069
stmt.execute("CREATE TABLE IF NOT EXISTS " + table +
@@ -1266,7 +1267,7 @@ public void testJdbcEscapeSyntax() throws Exception {
12661267

12671268
@Test(groups = {"integration "})
12681269
public void testStatementsWithDatabaseInTableIdentifier() throws Exception {
1269-
try (Connection conn = getJdbcConnection()) {
1270+
try (Connection conn = getJdbcConnection(Map.of(ASYNC_INSERT_SETTING_KEY, ServerSettings.OFF))) {
12701271
final String db1Name = conn.getSchema() + "_db1";
12711272
final String table1Name = "table1";
12721273
try (Statement stmt = conn.createStatement()) {
@@ -1296,7 +1297,7 @@ public void testStatementsWithDatabaseInTableIdentifier() throws Exception {
12961297

12971298
@Test(groups = {"integration "})
12981299
public void testNullValues() throws Exception {
1299-
try (Connection conn = getJdbcConnection()) {
1300+
try (Connection conn = getJdbcConnection(Map.of(ASYNC_INSERT_SETTING_KEY, ServerSettings.OFF))) {
13001301
final String table = "test_null_values";
13011302
try (Statement stmt = conn.createStatement()) {
13021303
stmt.execute("DROP TABLE IF EXISTS " + table);

0 commit comments

Comments
 (0)