Repository navigation
Description
Description
In jdbc-v2, some Connection methods do not check if the connection is closed. After close(), these methods return normally:
getNetworkTimeout()returns a value.getCatalog()returnsnull.setClientInfo(String, String)andsetClientInfo(Properties)return normally.setClientInfo("ApplicationName", …)also callsconfig.updateUserClient(value, client)on the closed client.- With
jdbc_ignore_unsupported_values=true,commit()androllback()return normally.
The java.sql.Connection javadoc for each of these methods says that it throws SQLException (SQLClientInfoException for setClientInfo) "if this method is called on a closed connection". Most other methods in the same class do this: getSchema(), getAutoCommit(), getClientInfo(String), setNetworkTimeout(...) throw SQLException: Connection is closed (SQLState 08000). The jdbc v1 driver also checks: ClickHouseConnectionImpl#getNetworkTimeout() calls ensureOpen().
Code that uses the exception to find a dead connection does not get it from these methods.
Root cause
jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java (main 771b8bcca, same code in v0.11.0). These methods do not call ensureOpen() (line 757):
getNetworkTimeout(), line 737getCatalog(), line 337setClientInfo(String, String), line 576, andsetClientInfo(Properties), line 585commit(), line 286, androllback(), line 291. They only callfeatureManager.unsupportedFeatureThrow(...), which does nothing whenjdbc_ignore_unsupported_values=true.
Suggested fix
Call ensureOpen() first in each of these methods.
setClientInfocan only throwSQLClientInfoException. Thus it must convert the closed check intoSQLClientInfoException(for example,new SQLClientInfoException("Connection is closed", ExceptionUtils.SQL_STATE_CONNECTION_EXCEPTION, Collections.emptyMap())).setClientInfo(Properties)can do the check one time before its loops.- In
commit()/rollback(), callensureOpen()beforeunsupportedFeatureThrow. - Keep the current behavior of these methods, which the javadoc allows on a closed connection:
close()andabort(...)do nothing a second time,isClosed()returnstrue, andisValid(...)returnsfalse. - On an open connection, behavior does not change.
Note: open PR #3266 (for #3260) changes the body of getNetworkTimeout(). It does not add the closed check. The two changes touch the same method.
Steps to reproduce
- Open a jdbc-v2 connection.
- Call
close(). - Call
getNetworkTimeout(),getCatalog(), orsetClientInfo("ApplicationName", "x").
Error Log or Exception StackTrace
Output of the code example below (server 26.9.1.1629, main 771b8bcca):
== defaults; server version 26.9.1.1629
after close(), isClosed()=true:
getNetworkTimeout() -> returned 0 (NO EXCEPTION)
getCatalog() -> returned null (NO EXCEPTION)
setClientInfo(ApplicationName, x) -> returned void (NO EXCEPTION)
setClientInfo(Properties) -> returned void (NO EXCEPTION)
commit() -> threw SQLFeatureNotSupportedException: commit() is not supported. [SQLState=0A000]
rollback() -> threw SQLFeatureNotSupportedException: rollback() is not supported. [SQLState=0A000]
contrast (methods that check):
getSchema() -> threw SQLException: Connection is closed [SQLState=08000]
getAutoCommit() -> threw SQLException: Connection is closed [SQLState=08000]
getClientInfo(ApplicationName) -> threw SQLException: Connection is closed [SQLState=08000]
setNetworkTimeout(exec, 1000) -> threw SQLException: Connection is closed [SQLState=08000]
isValid(1) -> returned false (NO EXCEPTION)
== jdbc_ignore_unsupported_values=true; server version 26.9.1.1629
after close(), isClosed()=true:
getNetworkTimeout() -> returned 0 (NO EXCEPTION)
getCatalog() -> returned null (NO EXCEPTION)
setClientInfo(ApplicationName, x) -> returned void (NO EXCEPTION)
setClientInfo(Properties) -> returned void (NO EXCEPTION)
commit() -> returned void (NO EXCEPTION)
rollback() -> returned void (NO EXCEPTION)
The result is the same with socket_timeout=30000.
Expected Behaviour
After close(), getNetworkTimeout(), getCatalog(), commit() and rollback() throw SQLException ("Connection is closed", SQLState 08000), the same as getSchema() and setNetworkTimeout(...). Both setClientInfo overloads throw SQLClientInfoException. This is what the java.sql.Connection javadoc says, for example for getNetworkTimeout():
Throws: SQLException - if a database access error occurs or this method is called on a closed Connection
Code Example
Properties p = new Properties();
p.setProperty("user", "default");
p.setProperty("password", "<password>");
Connection c = new com.clickhouse.jdbc.Driver().connect("jdbc:clickhouse://localhost:8123/default", p);
c.close();
c.getNetworkTimeout(); // returns 0, expected SQLException
c.getCatalog(); // returns null, expected SQLException
c.setClientInfo("ApplicationName", "x"); // returns normally, expected SQLClientInfoException
c.getSchema(); // throws SQLException: Connection is closed (correct)Configuration
Client Configuration
// defaults; also tested with socket_timeout=30000 and jdbc_ignore_unsupported_values=trueEnvironment
- Cloud
- Client version: jdbc-v2 from
mainat771b8bcca(0.12.0-rc1-SNAPSHOT). The code is the same inv0.11.0. - Language version: OpenJDK 17
- OS: Linux (Docker)
ClickHouse Server
- ClickHouse Server version: 26.9.1.1629
- ClickHouse Server non-default settings, if any: none that apply
CREATE TABLEstatements for tables involved: none- Sample data for all these tables: none
Found by automated analysis of the client (Polyglot AI) while working on #3260. Verified against a live server, not only by reading the code.
Activity
fix(jdbc-v2): throw on closed connection in commit, rollback, getCata…