From ae4b6d6dca6bfc522adca875057c202a22bd62a6 Mon Sep 17 00:00:00 2001 From: hutiefang76 <137664623+hutiefang76@users.noreply.github.com> Date: Fri, 9 Oct 2026 08:30:48 +0800 Subject: [PATCH] Reject JDBC factories on closed connections Signed-off-by: hutiefang76 <137664623+hutiefang76@users.noreply.github.com> --- .../java/org/duckdb/DuckDBConnection.java | 3 ++ src/test/java/org/duckdb/TestClosure.java | 52 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/main/java/org/duckdb/DuckDBConnection.java b/src/main/java/org/duckdb/DuckDBConnection.java index f83cf0796..8f1b4c372 100644 --- a/src/main/java/org/duckdb/DuckDBConnection.java +++ b/src/main/java/org/duckdb/DuckDBConnection.java @@ -311,6 +311,7 @@ public DuckDBPreparedStatement prepare(String sql) throws SQLException { } public DatabaseMetaData getMetaData() throws SQLException { + checkOpen(); return new DuckDBDatabaseMetaData(this); } @@ -505,10 +506,12 @@ public Properties getClientInfo() throws SQLException { } public Array createArrayOf(String typeName, Object[] elements) throws SQLException { + checkOpen(); return new DuckDBUserArray(typeName, elements); } public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + checkOpen(); return new DuckDBUserStruct(typeName, attributes); } diff --git a/src/test/java/org/duckdb/TestClosure.java b/src/test/java/org/duckdb/TestClosure.java index 01748e8bc..076a77270 100644 --- a/src/test/java/org/duckdb/TestClosure.java +++ b/src/test/java/org/duckdb/TestClosure.java @@ -672,6 +672,58 @@ public static void test_prepared_stmt_query_timeout() throws Exception { } } + // JDBC contract (java.sql.Connection): getMetaData(), createArrayOf() and createStruct() must + // keep working on an open connection and must throw SQLException once it is closed. + public static void test_open_connection_factories_return_objects() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL)) { + assertNotNull(conn.getMetaData()); + assertNotNull(conn.createArrayOf("INTEGER", new Object[] {1, 2})); + assertNotNull(conn.createStruct("STRUCT(a INTEGER, b INTEGER)", new Object[] {1, 2})); + } + } + + public static void test_closed_connection_getMetaData_throws() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL)) { + conn.close(); + assertTrue(conn.isClosed()); + try { + conn.getMetaData(); + fail("getMetaData() on a closed connection should throw SQLException"); + } catch (SQLException e) { + assertEquals(e.getMessage(), "Connection was closed"); + assertEquals(e.getSQLState(), "08003"); + } + } + } + + public static void test_closed_connection_createArrayOf_throws() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL)) { + conn.close(); + assertTrue(conn.isClosed()); + try { + conn.createArrayOf("INTEGER", new Object[] {1, 2}); + fail("createArrayOf() on a closed connection should throw SQLException"); + } catch (SQLException e) { + assertEquals(e.getMessage(), "Connection was closed"); + assertEquals(e.getSQLState(), "08003"); + } + } + } + + public static void test_closed_connection_createStruct_throws() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL)) { + conn.close(); + assertTrue(conn.isClosed()); + try { + conn.createStruct("STRUCT(a INTEGER, b INTEGER)", new Object[] {1, 2}); + fail("createStruct() on a closed connection should throw SQLException"); + } catch (SQLException e) { + assertEquals(e.getMessage(), "Connection was closed"); + assertEquals(e.getSQLState(), "08003"); + } + } + } + public static void manual_test_set_query_timeout_wo_scheduler() throws Exception { assertTrue(DuckDBDriver.shutdownQueryCancelScheduler()); assertFalse(DuckDBDriver.shutdownQueryCancelScheduler());