Skip to content
71 changes: 69 additions & 2 deletions client-v2/src/main/java/com/clickhouse/client/api/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public class Client implements AutoCloseable {
private Client(Collection<Endpoint> endpoints, Map<String,String> configuration,
ExecutorService sharedOperationExecutor, ColumnToMethodMatchingStrategy columnToMethodMatchingStrategy,
Object metricsRegistry, Supplier<String> queryIdGenerator) {
this.configuration = ClientConfigProperties.parseConfigMap(configuration);
this.configuration = Collections.synchronizedMap(ClientConfigProperties.parseConfigMap(configuration));
this.readOnlyConfig = Collections.unmodifiableMap(configuration);
this.metricsRegistry = metricsRegistry;
Comment thread
chernser marked this conversation as resolved.
this.queryIdGenerator = queryIdGenerator;
Expand Down Expand Up @@ -955,6 +955,37 @@ public Builder serverSetting(String name, Collection<String> values) {
return this;
}

/**
* Sets ClickHouse session id to be sent with each request.
*/
public Builder setSessionId(String sessionId) {
ValidationUtils.checkNonBlank(sessionId, ClickHouseHttpProto.QPARAM_SESSION_ID);
return serverSetting(ClickHouseHttpProto.QPARAM_SESSION_ID, sessionId);
}

/**
* Sets ClickHouse session check flag to be sent with each request.
*/
public Builder setSessionCheck(boolean sessionCheck) {
return serverSetting(ClickHouseHttpProto.QPARAM_SESSION_CHECK, sessionCheck ? "1" : "0");
}

/**
* Sets ClickHouse session timeout in seconds to be sent with each request.
*/
public Builder setSessionTimeout(int timeoutInSeconds) {
ValidationUtils.checkPositive(timeoutInSeconds, ClickHouseHttpProto.QPARAM_SESSION_TIMEOUT);
return serverSetting(ClickHouseHttpProto.QPARAM_SESSION_TIMEOUT, String.valueOf(timeoutInSeconds));
}

/**
* Sets ClickHouse session timezone to be sent with each request.
*/
public Builder setSessionTimezone(String timezone) {
ValidationUtils.checkNonBlank(timezone, ClickHouseHttpProto.QPARAM_SESSION_TIMEZONE);
return serverSetting(ClickHouseHttpProto.QPARAM_SESSION_TIMEZONE, timezone);
}

/**
* Sets column to method matching strategy. It is used while registering POJO serializers and deserializers.
* Default is {@link DefaultColumnToMethodMatchingStrategy}.
Expand Down Expand Up @@ -2138,6 +2169,40 @@ public void updateClientName(String name) {
this.configuration.put(ClientConfigProperties.CLIENT_NAME.getKey(), name);
}

/**
* Updates ClickHouse session id for all subsequent requests created by this client.
*/
public void updateSessionId(String sessionId) {
ValidationUtils.checkNonBlank(sessionId, ClickHouseHttpProto.QPARAM_SESSION_ID);
this.configuration.put(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_SESSION_ID), sessionId);
}

/**
* Updates ClickHouse session check flag for all subsequent requests created by this client.
*/
public void updateSessionCheck(boolean sessionCheck) {
this.configuration.put(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_SESSION_CHECK),
sessionCheck ? "1" : "0");
}

/**
* Updates ClickHouse session timeout (seconds) for all subsequent requests created by this client.
*/
public void updateSessionTimeout(int timeoutInSeconds) {
ValidationUtils.checkPositive(timeoutInSeconds, ClickHouseHttpProto.QPARAM_SESSION_TIMEOUT);
this.configuration.put(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_SESSION_TIMEOUT),
String.valueOf(timeoutInSeconds));
}
Comment thread
chernser marked this conversation as resolved.

/**
* Updates ClickHouse session timezone for all subsequent requests created by this client.
*/
public void updateSessionTimezone(String timezone) {
ValidationUtils.checkNonBlank(timezone, ClickHouseHttpProto.QPARAM_SESSION_TIMEZONE);
this.configuration.put(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_SESSION_TIMEZONE),
timezone);
}

public static final String clientVersion =
ClickHouseClientOption.readVersionFromResource("client-v2-version.properties");
public static final String CLIENT_USER_AGENT = "clickhouse-java-v2/";
Expand Down Expand Up @@ -2171,7 +2236,9 @@ private Endpoint getNextAliveNode() {
*/
private Map<String, Object> buildRequestSettings(Map<String, Object> opSettings) {
Map<String, Object> requestSettings = new HashMap<>();
requestSettings.putAll(configuration);
synchronized (configuration) {
requestSettings.putAll(configuration);
}
requestSettings.putAll(opSettings);
return requestSettings;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,27 @@
import com.clickhouse.client.api.query.QuerySettings;

public class CommandSettings extends QuerySettings {
@Override
public CommandSettings setSessionId(String sessionId) {
super.setSessionId(sessionId);
return this;
}

@Override
public CommandSettings setSessionCheck(boolean sessionCheck) {
super.setSessionCheck(sessionCheck);
return this;
}

@Override
public CommandSettings setSessionTimeout(int timeoutInSeconds) {
super.setSessionTimeout(timeoutInSeconds);
return this;
}

@Override
public CommandSettings setSessionTimezone(String timezone) {
super.setSessionTimezone(timezone);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,26 @@ public class ClickHouseHttpProto {
*/
public static final String QPARAM_QUERY_ID = "query_id";

/**
* Query parameter to specify a session id.
*/
public static final String QPARAM_SESSION_ID = "session_id";

/**
* Query parameter to check session status (1/0).
*/
public static final String QPARAM_SESSION_CHECK = "session_check";

/**
* Query parameter to specify session timeout in seconds.
*/
public static final String QPARAM_SESSION_TIMEOUT = "session_timeout";

/**
* Query parameter to specify session timezone.
*/
public static final String QPARAM_SESSION_TIMEZONE = "session_timezone";

public static final String QPARAM_ROLE = "role";

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,54 @@ public InsertSettings setQueryId(String queryId) {
return this;
}

/**
* Sets ClickHouse session id for this operation.
*/
public InsertSettings setSessionId(String sessionId) {
settings.setSessionId(sessionId);
return this;
}

public String getSessionId() {
return settings.getSessionId();
}

/**
* Sets ClickHouse session check flag for this operation.
*/
public InsertSettings setSessionCheck(boolean sessionCheck) {
settings.setSessionCheck(sessionCheck);
return this;
}

public Boolean getSessionCheck() {
return settings.getSessionCheck();
}

/**
* Sets ClickHouse session timeout (seconds) for this operation.
*/
public InsertSettings setSessionTimeout(int timeoutInSeconds) {
settings.setSessionTimeout(timeoutInSeconds);
return this;
}

public Integer getSessionTimeout() {
return settings.getSessionTimeout();
}

/**
* Sets ClickHouse session timezone for this operation.
*/
public InsertSettings setSessionTimezone(String timezone) {
settings.setSessionTimezone(timezone);
return this;
}

public String getSessionTimezone() {
return settings.getSessionTimezone();
}

public int getInputStreamCopyBufferSize() {
return this.inputStreamCopyBufferSize;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.clickhouse.client.api.Client;
import com.clickhouse.client.api.ClientConfigProperties;
import com.clickhouse.client.api.http.ClickHouseHttpProto;

import java.time.Duration;
import java.time.temporal.ChronoUnit;
Expand Down Expand Up @@ -90,6 +91,47 @@ public CommonSettings setQueryId(String queryId) {
return this;
}

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

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

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

public Boolean getSessionCheck() {
String value = (String) settings.get(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_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));
return this;
}

public Integer getSessionTimeout() {
String value = (String) settings.get(ClientConfigProperties.serverSetting(ClickHouseHttpProto.QPARAM_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);
return this;
}

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

/**
* Operation id. Used internally to register new operation.
* Should not be called directly.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,54 @@ public String getQueryId() {
return settings.getQueryId();
}

/**
* Sets ClickHouse session id for this operation.
*/
public QuerySettings setSessionId(String sessionId) {
settings.setSessionId(sessionId);
return this;
}

public String getSessionId() {
return settings.getSessionId();
}

/**
* Sets ClickHouse session check flag for this operation.
*/
public QuerySettings setSessionCheck(boolean sessionCheck) {
settings.setSessionCheck(sessionCheck);
return this;
}

public Boolean getSessionCheck() {
return settings.getSessionCheck();
}

/**
* Sets ClickHouse session timeout (seconds) for this operation.
*/
public QuerySettings setSessionTimeout(int timeoutInSeconds) {
settings.setSessionTimeout(timeoutInSeconds);
return this;
}

public Integer getSessionTimeout() {
return settings.getSessionTimeout();
}

/**
* Sets ClickHouse session timezone for this operation.
*/
public QuerySettings setSessionTimezone(String timezone) {
settings.setSessionTimezone(timezone);
return this;
}

public String getSessionTimezone() {
return settings.getSessionTimezone();
}

/**
* 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
51 changes: 51 additions & 0 deletions client-v2/src/test/java/com/clickhouse/client/ClientTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.clickhouse.client.api.ClientMisconfigurationException;
import com.clickhouse.client.api.ConnectionReuseStrategy;
import com.clickhouse.client.api.ServerException;
import com.clickhouse.client.api.command.CommandSettings;
import com.clickhouse.client.api.enums.Protocol;
import com.clickhouse.client.api.insert.InsertSettings;
import com.clickhouse.client.api.internal.ClickHouseLZ4OutputStream;
Expand Down Expand Up @@ -144,6 +145,46 @@ public void testRawSettings() {
}
}

@Test(groups = {"integration"})
public void testTemporaryTablesAreBoundToSession() throws Exception {
if (isCloud()) {
return; // HTTP sessions require server affinity
Comment thread
chernser marked this conversation as resolved.
}

String session1 = "session_1_" + UUID.randomUUID().toString().replace("-", "");
Comment thread
chernser marked this conversation as resolved.
Outdated
String session2 = "session_2_" + UUID.randomUUID().toString().replace("-", "");
String table1 = "tmp_session_1_" + UUID.randomUUID().toString().replace("-", "");
String table2 = "tmp_session_2_" + UUID.randomUUID().toString().replace("-", "");

CommandSettings session1CommandSettings = new CommandSettings()
.setSessionId(session1)
.setSessionTimeout(60);
CommandSettings session2CommandSettings = new CommandSettings()
.setSessionId(session2)
.setSessionTimeout(60);
QuerySettings session1QuerySettings = new QuerySettings()
.setSessionId(session1)
.setSessionTimeout(60);
QuerySettings session2QuerySettings = new QuerySettings()
.setSessionId(session2)
.setSessionTimeout(60);

try (Client client = newClient().build()) {
client.execute("CREATE TEMPORARY TABLE " + table1 + " (value UInt8)", session1CommandSettings).get().close();
client.execute("INSERT INTO " + table1 + " VALUES (1)", session1CommandSettings).get().close();
client.execute("CREATE TEMPORARY TABLE " + table2 + " (value UInt8)", session2CommandSettings).get().close();
client.execute("INSERT INTO " + table2 + " VALUES (2)", session2CommandSettings).get().close();

Assert.assertEquals(client.queryAll("SELECT value FROM " + table1, session1QuerySettings).get(0).getInteger(1),
Integer.valueOf(1));
Assert.assertEquals(client.queryAll("SELECT value FROM " + table2, session2QuerySettings).get(0).getInteger(1),
Integer.valueOf(2));

assertTableNotFoundInSession(client, table2, session1QuerySettings);
assertTableNotFoundInSession(client, table1, session2QuerySettings);
}
}

@Test(groups = {"integration"})
public void testCustomSettings() {
if (isCloud()) {
Expand Down Expand Up @@ -574,6 +615,16 @@ public boolean isVersionMatch(String versionExpression, Client client) {
return ClickHouseVersion.of(serverVersion.get(0).getString(1)).check(versionExpression);
}

private void assertTableNotFoundInSession(Client client, String tableName, QuerySettings settings) {
try {
client.queryAll("SELECT * FROM " + tableName, settings);
Assert.fail("Expected table to be unavailable in session: " + tableName);
} catch (ClientException e) {
Assert.assertTrue(e.getCause() instanceof ServerException, "Expected ServerException but got " + e.getCause());
Assert.assertEquals(((ServerException) e.getCause()).getCode(), ServerException.TABLE_NOT_FOUND);
}
}

protected Client.Builder newClient() {
ClickHouseNode node = getServer(ClickHouseProtocol.HTTP);
boolean isSecure = isCloud();
Expand Down
Loading
Loading