Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
## 0.9.9

### New Features

- **[client-v2]** Added ClickHouse session APIs to `Client.Builder`, `QuerySettings`, `InsertSettings`, and `CommandSettings`: `setSessionId()`, `setSessionCheck()`, `setSessionTimeout()`, `setSessionTimezone()`, and `use(Session)`, plus `getSessionId()`, `getSessionCheck()`, `getSessionTimeout()`, `getSessionTimezone()`, and `clearSession()` on operation settings. `clearSession()` clears session-related request settings. (https://github.com/ClickHouse/clickhouse-java/pull/2810, https://github.com/ClickHouse/clickhouse-java/pull/2836)

### Improvements

- **[client-v2]** Improved HTTP transport error handling: unwrap compressed error responses, read server error bodies reliably, handle common HTTP status
failures (for example 401, 403, 404), and include more context when an `IOException` occurs while reading result data. (https://github.com/ClickHouse/
clickhouse-java/pull/2804)

- **[client-v2]** Refined `HttpEndpoint` construction and validation so hostnames containing underscores are handled consistently with JVM name resolution
behavior. (https://github.com/ClickHouse/clickhouse-java/issues/2792, https://github.com/ClickHouse/clickhouse-java/issues/2753)

- **[jdbc-v2]** Extended the JavaCC SQL utilities keyword list with CENTURY, DECADE, DOW, DOY, EPOCH, ISODOW, ISOYEAR, MILLENNIUM, NATURAL, SOME, ZONE. (https://github.com/ClickHouse/clickhouse-java/pull/2818)

### Bug Fixes

- **[client-v2, jdbc-v2]** Fixed `Statement#cancel()` when the connection uses a ClickHouse server session (`session_id`): the internal `KILL QUERY` is executed with settings copied from the connection defaults but **without** the session id, so the cancel request is not blocked by the same session. (https://github.com/ClickHouse/clickhouse-java/issues/2690)

- **[client-v2]** Fixed decoding of SQL `NULL` values inside `Variant` and `Dynamic` columns in the binary row protocol reader. (https://github.com/ClickHouse/clickhouse-java/issues/2789, https://github.com/ClickHouse/clickhouse-java/issues/2791)

- **[client-v2, jdbc-v2]** Fixed decimal-related conversion and rounding when values pass through `ValueConverters` / serialization helpers (including
integer-to-decimal paths and floating-point edge cases), with expanded regression tests. (https://github.com/ClickHouse/clickhouse-java/issues/2748)

- **[client-v2]** Fixed Basic authentication when no password is configured: the literal `"null"` string is no longer sent as the password value. (https://
github.com/ClickHouse/clickhouse-java/pull/2809)

## 0.9.8

### Improvements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public class ServerException extends ClickHouseException {

public static final int UNKNOWN_SETTING = 115;

public static final int QUERY_CANCELLED = 394;

private final int code;

private final int transportProtocolCode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
return this;
}

public CommandSettings clearSession() {

Check warning on line 31 in client-v2/src/main/java/com/clickhouse/client/api/command/CommandSettings.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add the "@Override" annotation above this method signature

See more on https://sonarcloud.io/project/issues?id=ClickHouse_clickhouse-java&issues=AZ2xj89RBsV3QcQ5IShS&open=AZ2xj89RBsV3QcQ5IShS&pullRequest=2836
super.clearSession();
return this;
}

@Override
public CommandSettings use(Session session) {
super.use(session);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ public String getSessionTimezone() {
return settings.getSessionTimezone();
}

public InsertSettings clearSession() {
settings.clearSession();
return this;
}

public InsertSettings use(Session session) {
settings.use(session);
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.clickhouse.client.api.Client;
import com.clickhouse.client.api.ClientConfigProperties;
import com.clickhouse.client.api.Session;
import com.clickhouse.client.api.http.ClickHouseHttpProto;

import java.time.Duration;
import java.time.temporal.ChronoUnit;
Expand All @@ -18,6 +17,8 @@
*/
public class CommonSettings {

public static final String SESSION_ID_NONE = null;

Comment on lines +20 to +21
private String operationId;
private String logComment;
protected Map<String, Object> settings;
Expand Down Expand Up @@ -93,44 +94,50 @@ public CommonSettings setQueryId(String queryId) {
}

public CommonSettings setSessionId(String sessionId) {
ValidationUtils.checkNonBlank(sessionId, ClickHouseHttpProto.QPARAM_SESSION_ID);
serverSetting(ClickHouseHttpProto.QPARAM_SESSION_ID, sessionId);
ValidationUtils.checkNonBlank(sessionId, ServerSettings.SESSION_ID);
serverSetting(ServerSettings.SESSION_ID, sessionId);
return this;
}

public String getSessionId() {
return (String) settings.get(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_SESSION_ID));
return (String) settings.get(ClientConfigProperties.serverSetting(ServerSettings.SESSION_ID));
}

public CommonSettings setSessionCheck(boolean sessionCheck) {
serverSetting(ClickHouseHttpProto.QPARAM_SESSION_CHECK, sessionCheck ? "1" : "0");
serverSetting(ServerSettings.SESSION_CHECK, sessionCheck ? "1" : "0");
return this;
}

public Boolean getSessionCheck() {
String value = (String) settings.get(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_SESSION_CHECK));
String value = (String) settings.get(ClientConfigProperties.serverSetting(ServerSettings.SESSION_CHECK));
return value == null ? null : ("1".equals(value) || Boolean.parseBoolean(value));
}

public CommonSettings setSessionTimeout(int timeoutInSeconds) {
ValidationUtils.checkPositive(timeoutInSeconds, ClickHouseHttpProto.QPARAM_SESSION_TIMEOUT);
serverSetting(ClickHouseHttpProto.QPARAM_SESSION_TIMEOUT, String.valueOf(timeoutInSeconds));
ValidationUtils.checkPositive(timeoutInSeconds, ServerSettings.SESSION_TIMEOUT);
serverSetting(ServerSettings.SESSION_TIMEOUT, String.valueOf(timeoutInSeconds));
return this;
}

public Integer getSessionTimeout() {
String value = (String) settings.get(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_SESSION_TIMEOUT));
String value = (String) settings.get(ClientConfigProperties.serverSetting(ServerSettings.SESSION_TIMEOUT));
return value == null ? null : Integer.valueOf(value);
}

public CommonSettings setSessionTimezone(String timezone) {
ValidationUtils.checkNonBlank(timezone, ClickHouseHttpProto.QPARAM_SESSION_TIMEZONE);
serverSetting(ClickHouseHttpProto.QPARAM_SESSION_TIMEZONE, timezone);
ValidationUtils.checkNonBlank(timezone, ServerSettings.SESSION_TIMEZONE);
serverSetting(ServerSettings.SESSION_TIMEZONE, timezone);
return this;
}

public String getSessionTimezone() {
return (String) settings.get(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_SESSION_TIMEZONE));
return (String) settings.get(ClientConfigProperties.serverSetting(ServerSettings.SESSION_TIMEZONE));
}

public CommonSettings clearSession() {
serverSetting(ServerSettings.SESSION_ID, (String) null);
serverSetting(ServerSettings.SESSION_CHECK, "0");
return this;
}

public CommonSettings use(Session session) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,12 @@ public final class ServerSettings {
public static final String ASYNC_INSERT = "async_insert";

public static final String WAIT_ASYNC_INSERT = "wait_for_async_insert";

public static final String SESSION_ID = "session_id";

public static final String SESSION_CHECK = "session_check";

public static final String SESSION_TIMEOUT = "session_timeout";

public static final String SESSION_TIMEZONE = "session_timezone";
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.TimeZone;

Expand All @@ -24,7 +25,7 @@ public class QuerySettings {
private final CommonSettings settings;

public QuerySettings(Map<String, Object> settings) {
this.settings = new CommonSettings();
this();
for (Map.Entry<String, Object> entry : settings.entrySet()) {
this.settings.setOption(entry.getKey(), entry.getValue());
}
Expand All @@ -34,6 +35,10 @@ public QuerySettings() {
this.settings = new CommonSettings();
}

public QuerySettings(QuerySettings settings) {
this(settings == null ? Collections.emptyMap() : settings.getAllSettings());
}

private QuerySettings(CommonSettings settings) {
this.settings = settings;
}
Expand Down Expand Up @@ -139,6 +144,11 @@ public QuerySettings use(Session session) {
return this;
}

public QuerySettings clearSession() {
settings.clearSession();
return this;
}

/**
* Read buffer is used for reading data from a server. Size is in bytes.
* Minimal value is {@value MINIMAL_READ_BUFFER_SIZE} bytes.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.clickhouse.client.api.command;

import org.testng.Assert;
import org.testng.annotations.Test;

public class CommandSettingsTest {
@Test
public void testClearSession() {
CommandSettings settings = new CommandSettings();
settings.setSessionId("test-session");
settings.setSessionCheck(true);

Assert.assertEquals(settings.getSessionId(), "test-session");
Assert.assertEquals(settings.getSessionCheck(), Boolean.TRUE);

settings.clearSession();

Assert.assertNull(settings.getSessionId());
Assert.assertEquals(settings.getSessionCheck(), Boolean.FALSE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.clickhouse.client.api.insert;

import org.testng.Assert;
import org.testng.annotations.Test;

public class InsertSettingsTest {
@Test(groups = {"integration"})
public void testClearSession() {
InsertSettings settings = new InsertSettings();
settings.setSessionId("test-session");
settings.setSessionCheck(true);

Assert.assertEquals(settings.getSessionId(), "test-session");
Assert.assertEquals(settings.getSessionCheck(), Boolean.TRUE);

settings.clearSession();

Assert.assertNull(settings.getSessionId());
Assert.assertEquals(settings.getSessionCheck(), Boolean.FALSE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.clickhouse.client.api.internal;

import org.testng.Assert;
import org.testng.annotations.Test;

public class CommonSettingsTest {
@Test(groups = {"integration"})
public void testClearSession() {
CommonSettings settings = new CommonSettings();
settings.setSessionId("test-session");
settings.setSessionCheck(true);

Assert.assertEquals(settings.getSessionId(), "test-session");
Assert.assertEquals(settings.getSessionCheck(), Boolean.TRUE);

settings.clearSession();

Assert.assertNull(settings.getSessionId());
Assert.assertEquals(settings.getSessionCheck(), Boolean.FALSE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.clickhouse.client.api.query;

import org.testng.Assert;
import org.testng.annotations.Test;

public class QuerySettingsTest {
@Test(groups = {"integration"})
public void testClearSession() {
QuerySettings settings = new QuerySettings();
Comment on lines +7 to +9
settings.setSessionId("test-session");
settings.setSessionCheck(true);

Assert.assertEquals(settings.getSessionId(), "test-session");
Assert.assertEquals(settings.getSessionCheck(), Boolean.TRUE);

settings.clearSession();

Assert.assertNull(settings.getSessionId());
Assert.assertEquals(settings.getSessionCheck(), Boolean.FALSE);
}

@Test(groups = {"integration"})
public void testCopyConstructor() {
QuerySettings settings = new QuerySettings();
settings.setSessionId("test-session");
settings.setSessionCheck(true);

QuerySettings copy = new QuerySettings(settings);
Assert.assertEquals(copy.getSessionId(), "test-session");
Assert.assertEquals(copy.getSessionCheck(), Boolean.TRUE);

QuerySettings nullCopy = new QuerySettings((QuerySettings) null);
Assert.assertNull(nullCopy.getSessionId());
}
}
11 changes: 8 additions & 3 deletions jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,14 @@ public void cancel() throws SQLException {
return;
}

try (QueryResponse response = connection.getClient().query(String.format("KILL QUERY%sWHERE query_id = '%s'",
connection.onCluster ? " ON CLUSTER " + connection.cluster + " " : " ",
lastQueryId), connection.getDefaultQuerySettings()).get()){
final String sql = "KILL QUERY "
+ (connection.onCluster ? "ON CLUSTER " + connection.cluster + " " : "")
+ "WHERE query_id = '" + lastQueryId + "'";

String queryId = queryIdGenerator == null ? UUID.randomUUID().toString() : queryIdGenerator.get();
// Clear session settings to prevent KILL QUERY from using the locked session
try (QueryResponse response = connection.getClient().query(sql,
new QuerySettings(connection.getDefaultQuerySettings()).setQueryId(queryId).clearSession()).get()){
LOG.debug("Query {} was killed by {}", lastQueryId, response.getQueryId());
} catch (Exception e) {
throw new SQLException(e);
Expand Down
Loading
Loading