From 05e1dd5aac0da231a37fa6e4c28aa09dfef3f398 Mon Sep 17 00:00:00 2001 From: Sergey Chernov Date: Mon, 20 Apr 2026 12:29:57 -0700 Subject: [PATCH 1/8] removed String.format --- .../src/main/java/com/clickhouse/jdbc/StatementImpl.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java index f50546393..22b08afae 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java @@ -333,9 +333,10 @@ 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%sWHERE query_id = '" + lastQueryId +"'" + + (connection.onCluster ? " ON CLUSTER " + connection.cluster: ""); + + try (QueryResponse response = connection.getClient().query(sql, connection.getDefaultQuerySettings()).get()){ LOG.debug("Query {} was killed by {}", lastQueryId, response.getQueryId()); } catch (Exception e) { throw new SQLException(e); From edf105f47ce62b776b74d9d7a5bb0abbf867b452 Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Mon, 20 Apr 2026 19:39:42 +0000 Subject: [PATCH 2/8] Fix KILL QUERY to avoid session lock error - Fixed string concatenation bug (removed %s placeholder, added proper spacing) - Create QuerySettings without session ID for KILL QUERY execution - Prevents "Session is locked by a concurrent client" error when canceling queries Fixes #2690 Agent-Logs-Url: https://github.com/ClickHouse/clickhouse-java/sessions/aa2444cc-087c-4438-b99e-e9a3b6b2d8d7 Co-authored-by: chernser <827456+chernser@users.noreply.github.com> --- .../java/com/clickhouse/jdbc/StatementImpl.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java index 22b08afae..5dcd89615 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java @@ -333,10 +333,19 @@ public void cancel() throws SQLException { return; } - final String sql = "KILL QUERY%sWHERE query_id = '" + lastQueryId +"'" - + (connection.onCluster ? " ON CLUSTER " + connection.cluster: ""); + final String sql = "KILL QUERY " + + (connection.onCluster ? "ON CLUSTER " + connection.cluster + " " : "") + + "WHERE query_id = '" + lastQueryId + "'"; + + // Create query settings without session to avoid "Session is locked by a concurrent client" error + QuerySettings killQuerySettings = new QuerySettings(); + if (connection.getDefaultQuerySettings() != null) { + killQuerySettings.getAllSettings().putAll(connection.getDefaultQuerySettings().getAllSettings()); + } + killQuerySettings.setSessionId(null); + killQuerySettings.setSessionCheck(false); - try (QueryResponse response = connection.getClient().query(sql, connection.getDefaultQuerySettings()).get()){ + try (QueryResponse response = connection.getClient().query(sql, killQuerySettings).get()){ LOG.debug("Query {} was killed by {}", lastQueryId, response.getQueryId()); } catch (Exception e) { throw new SQLException(e); From fc382b0bf1a7df08b27d49ca709102a0a55e2838 Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Mon, 20 Apr 2026 19:44:03 +0000 Subject: [PATCH 3/8] Simplify KILL QUERY settings to always use empty QuerySettings Use a fresh QuerySettings instance instead of copying default settings and removing session-related ones. This is simpler, more stable, and KILL QUERY doesn't need any special settings to execute. Agent-Logs-Url: https://github.com/ClickHouse/clickhouse-java/sessions/46c2fb43-ed16-4140-82e1-e9ecacc80711 Co-authored-by: chernser <827456+chernser@users.noreply.github.com> --- .../main/java/com/clickhouse/jdbc/StatementImpl.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java index 5dcd89615..7acc9734a 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java @@ -337,15 +337,9 @@ public void cancel() throws SQLException { + (connection.onCluster ? "ON CLUSTER " + connection.cluster + " " : "") + "WHERE query_id = '" + lastQueryId + "'"; - // Create query settings without session to avoid "Session is locked by a concurrent client" error - QuerySettings killQuerySettings = new QuerySettings(); - if (connection.getDefaultQuerySettings() != null) { - killQuerySettings.getAllSettings().putAll(connection.getDefaultQuerySettings().getAllSettings()); - } - killQuerySettings.setSessionId(null); - killQuerySettings.setSessionCheck(false); - - try (QueryResponse response = connection.getClient().query(sql, killQuerySettings).get()){ + // Use empty QuerySettings to avoid "Session is locked by a concurrent client" error + // KILL QUERY doesn't need any special settings and should execute without session context + try (QueryResponse response = connection.getClient().query(sql, new QuerySettings()).get()){ LOG.debug("Query {} was killed by {}", lastQueryId, response.getQueryId()); } catch (Exception e) { throw new SQLException(e); From ae19ccd6719be6da88e74c278a8cc39dd9b0aae7 Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Mon, 20 Apr 2026 19:47:08 +0000 Subject: [PATCH 4/8] Fix KILL QUERY to avoid session lock error Copy default connection settings (like ASYNC_INSERT=0) but explicitly clear session-related settings (session_id, session_check, session_timeout) to prevent "Session is locked by a concurrent client" error. This preserves important connection settings while avoiding the session lock. Fixes #2690 Agent-Logs-Url: https://github.com/ClickHouse/clickhouse-java/sessions/2979914f-7677-4eb9-82c8-0f28b7a7276f Co-authored-by: chernser <827456+chernser@users.noreply.github.com> --- .../java/com/clickhouse/jdbc/StatementImpl.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java index 7acc9734a..ecaf214df 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java @@ -337,9 +337,18 @@ public void cancel() throws SQLException { + (connection.onCluster ? "ON CLUSTER " + connection.cluster + " " : "") + "WHERE query_id = '" + lastQueryId + "'"; - // Use empty QuerySettings to avoid "Session is locked by a concurrent client" error - // KILL QUERY doesn't need any special settings and should execute without session context - try (QueryResponse response = connection.getClient().query(sql, new QuerySettings()).get()){ + // Create query settings without session to avoid "Session is locked by a concurrent client" error + // We copy default connection settings but explicitly clear session-related settings + QuerySettings killQuerySettings = new QuerySettings(); + if (connection.getDefaultQuerySettings() != null) { + killQuerySettings.getAllSettings().putAll(connection.getDefaultQuerySettings().getAllSettings()); + } + // Clear session settings to prevent KILL QUERY from using the locked session + killQuerySettings.setSessionId(null); + killQuerySettings.setSessionCheck(false); + killQuerySettings.setSessionTimeout(0); + + try (QueryResponse response = connection.getClient().query(sql, killQuerySettings).get()){ LOG.debug("Query {} was killed by {}", lastQueryId, response.getQueryId()); } catch (Exception e) { throw new SQLException(e); From 4679b0dfede6cb48da0daee8f3f244e9be22fd55 Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Mon, 20 Apr 2026 20:33:08 +0000 Subject: [PATCH 5/8] Remove unnecessary setSessionTimeout(0) call Setting session timeout to 0 is unnecessary since we're clearing the session ID. When session ID is null, the timeout is irrelevant. Also, setSessionTimeout requires a positive value, so 0 could cause validation errors. The session settings are now properly cleared by setting sessionId to null and sessionCheck to false, which is sufficient. Agent-Logs-Url: https://github.com/ClickHouse/clickhouse-java/sessions/eaae4605-a781-4789-96f5-44b3cbde2d47 Co-authored-by: chernser <827456+chernser@users.noreply.github.com> --- jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java index ecaf214df..fcacf1e48 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java @@ -346,7 +346,6 @@ public void cancel() throws SQLException { // Clear session settings to prevent KILL QUERY from using the locked session killQuerySettings.setSessionId(null); killQuerySettings.setSessionCheck(false); - killQuerySettings.setSessionTimeout(0); try (QueryResponse response = connection.getClient().query(sql, killQuerySettings).get()){ LOG.debug("Query {} was killed by {}", lastQueryId, response.getQueryId()); From 99a8abfff7f23633e1c08c2fd8555d836456f3dc Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Mon, 20 Apr 2026 20:39:39 +0000 Subject: [PATCH 6/8] Add test for cancel() with active session Tests issue #2690 - verifies that Statement.cancel() works correctly when using a session ID without throwing "Session is locked by a concurrent client" error. The test: - Creates a connection with a session ID - Starts a long-running query - Calls cancel() which should work without session lock errors - Verifies the statement is still usable after cancellation Agent-Logs-Url: https://github.com/ClickHouse/clickhouse-java/sessions/4d760f6b-2f1d-4a50-afb1-0875cf881959 Co-authored-by: chernser <827456+chernser@users.noreply.github.com> --- .../com/clickhouse/jdbc/StatementTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java index 575dde388..a00c270ad 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java @@ -611,6 +611,43 @@ public void testConcurrentCancel() throws Exception { } } + @Test(groups = {"integration"}) + public void testCancelWithSessionId() throws Exception { + // Test for issue #2690 - Cancel statement should work with active session + Properties props = new Properties(); + props.put("clickhouse_setting_session_id", java.util.UUID.randomUUID().toString()); + + try (Connection conn = getJdbcConnection(props)) { + try (StatementImpl stmt = (StatementImpl) conn.createStatement()) { + // Start a long-running query in a separate thread + Thread queryThread = new Thread(() -> { + try { + stmt.executeQuery("SELECT sleep(10), number FROM system.numbers LIMIT 1000000"); + } catch (SQLException e) { + // Expected to be cancelled + log.debug("Query was cancelled as expected", e); + } + }); + queryThread.start(); + + // Give the query time to start + Thread.sleep(500); + + // This should not throw "Session is locked by a concurrent client" error + stmt.cancel(); + + // Wait for the thread to finish + queryThread.join(5000); + + // Verify statement is still usable after cancel + try (ResultSet rs = stmt.executeQuery("SELECT 1")) { + assertTrue(rs.next()); + assertEquals(rs.getInt(1), 1); + } + } + } + } + @Test(groups = {"integration"}) public void testTextFormatInResponse() throws Exception { try (Connection conn = getJdbcConnection(); From c1f111425e2704328e679788360f8855713fc43a Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Mon, 20 Apr 2026 20:47:22 +0000 Subject: [PATCH 7/8] Compact test and verify session settings - Use try-with-resources for cleaner code structure - Replace Thread.sleep(500) with CountDownLatch for better synchronization - Reduce arbitrary wait time from 500ms to 100ms - Add session verification by executing a query before the cancel test - Store sessionId in variable for potential future verification Co-authored-by: chernser <827456+chernser@users.noreply.github.com> --- .../com/clickhouse/jdbc/StatementTest.java | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java index a00c270ad..ab5bc7da6 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java @@ -614,36 +614,40 @@ public void testConcurrentCancel() throws Exception { @Test(groups = {"integration"}) public void testCancelWithSessionId() throws Exception { // Test for issue #2690 - Cancel statement should work with active session + String sessionId = java.util.UUID.randomUUID().toString(); Properties props = new Properties(); - props.put("clickhouse_setting_session_id", java.util.UUID.randomUUID().toString()); + props.put("clickhouse_setting_session_id", sessionId); - try (Connection conn = getJdbcConnection(props)) { - try (StatementImpl stmt = (StatementImpl) conn.createStatement()) { - // Start a long-running query in a separate thread - Thread queryThread = new Thread(() -> { - try { - stmt.executeQuery("SELECT sleep(10), number FROM system.numbers LIMIT 1000000"); - } catch (SQLException e) { - // Expected to be cancelled - log.debug("Query was cancelled as expected", e); - } - }); - queryThread.start(); + try (Connection conn = getJdbcConnection(props); + StatementImpl stmt = (StatementImpl) conn.createStatement()) { - // Give the query time to start - Thread.sleep(500); + // Verify session is applied from connection settings by checking system.query_log + // Execute a simple query first to ensure session is used + try (ResultSet rs = stmt.executeQuery("SELECT 1")) { + assertTrue(rs.next()); + } - // This should not throw "Session is locked by a concurrent client" error - stmt.cancel(); + final CountDownLatch queryStarted = new CountDownLatch(1); + Thread queryThread = new Thread(() -> { + try { + queryStarted.countDown(); + stmt.executeQuery("SELECT sleep(10), number FROM system.numbers LIMIT 1000000"); + } catch (SQLException e) { + log.debug("Query was cancelled as expected", e); + } + }); + queryThread.start(); + queryStarted.await(); + Thread.sleep(100); // Brief wait for query to start executing - // Wait for the thread to finish - queryThread.join(5000); + // This should not throw "Session is locked by a concurrent client" error + stmt.cancel(); + queryThread.join(5000); - // Verify statement is still usable after cancel - try (ResultSet rs = stmt.executeQuery("SELECT 1")) { - assertTrue(rs.next()); - assertEquals(rs.getInt(1), 1); - } + // Verify statement is still usable after cancel + try (ResultSet rs = stmt.executeQuery("SELECT 1")) { + assertTrue(rs.next()); + assertEquals(rs.getInt(1), 1); } } } From 8efa77ab409371ca8969b2d3103254ab2d3f1aaf Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Mon, 20 Apr 2026 20:54:50 +0000 Subject: [PATCH 8/8] Add actual query_log check for session verification Agent-Logs-Url: https://github.com/ClickHouse/clickhouse-java/sessions/2774cf39-d06e-4d4e-8eb3-86c8e0fd1138 Co-authored-by: chernser <827456+chernser@users.noreply.github.com> --- .../test/java/com/clickhouse/jdbc/StatementTest.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java index ab5bc7da6..9ad6773ad 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java @@ -622,9 +622,13 @@ public void testCancelWithSessionId() throws Exception { StatementImpl stmt = (StatementImpl) conn.createStatement()) { // Verify session is applied from connection settings by checking system.query_log - // Execute a simple query first to ensure session is used - try (ResultSet rs = stmt.executeQuery("SELECT 1")) { - assertTrue(rs.next()); + stmt.executeQuery("SELECT 1").close(); + String queryId = stmt.getLastQueryId(); + stmt.execute("SYSTEM FLUSH LOGS"); + + try (ResultSet rs = stmt.executeQuery("SELECT session_id FROM system.query_log WHERE query_id = '" + queryId + "'")) { + assertTrue("Query should be in query_log", rs.next()); + assertEquals("Session ID should match", sessionId, rs.getString("session_id")); } final CountDownLatch queryStarted = new CountDownLatch(1);