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..fcacf1e48 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,21 @@ 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 + "'"; + + // 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); + + 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); 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..9ad6773ad 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,51 @@ 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", sessionId); + + try (Connection conn = getJdbcConnection(props); + StatementImpl stmt = (StatementImpl) conn.createStatement()) { + + // Verify session is applied from connection settings by checking system.query_log + 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); + 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 + + // 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); + } + } + } + @Test(groups = {"integration"}) public void testTextFormatInResponse() throws Exception { try (Connection conn = getJdbcConnection();