From 358fb7d42453c7f416d10823c8d827b8c2c1649d Mon Sep 17 00:00:00 2001 From: itsmehotpants Date: Fri, 24 Jul 2026 18:12:55 +0000 Subject: [PATCH] Fix: forward Hikari data-source-properties to JDBC driver createConnection() used 'new Properties(info)', which chains info as *default* properties rather than copying its entries. Properties#getProperty() consults defaults, but raw Hashtable-style access (get, entrySet, keySet) - which many JDBC drivers use internally to enumerate connection args - does not. As a result, properties supplied via Hikari's data-source-properties (e.g. rewriteBatchedStatements, profileSQL) were silently dropped. Switched to 'new Properties()' + 'putAll(info)' so info's entries become real entries in the merged Properties object. Fixes #1537 --- .../containers/JdbcDatabaseContainer.java | 3 +- .../containers/JdbcDatabaseContainerTest.java | 85 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index cf6c995528f..1b26b494888 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -263,7 +263,8 @@ public Connection createConnection(String queryString) throws SQLException, NoDr */ public Connection createConnection(String queryString, Properties info) throws SQLException, NoDriverFoundException { - Properties properties = new Properties(info); + Properties properties = new Properties(); + properties.putAll(info); properties.put("user", this.getUsername()); properties.put("password", this.getPassword()); final String url = constructUrlForConnection(queryString); diff --git a/modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java b/modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java index ca41c3f5d1c..6a06b0c2c85 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java @@ -5,8 +5,13 @@ import org.slf4j.Logger; import java.sql.Connection; +import java.sql.Driver; +import java.sql.DriverPropertyInfo; import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.Properties; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.Mockito.mock; @@ -20,6 +25,86 @@ void anExceptionIsThrownIfJdbcIsNotAvailable() { assertThatExceptionOfType(IllegalStateException.class).isThrownBy(jdbcContainer::waitUntilContainerStarted); } + @Test + void createConnectionForwardsInfoPropertiesToTheDriverAsRealEntriesNotJustDefaults() throws Exception { + CapturingDriver.lastReceivedProperties = null; + + JdbcDatabaseContainerStub jdbcContainer = new JdbcDatabaseContainerStub("mysql:latest") { + @Override + public String getDriverClassName() { + return CapturingDriver.class.getName(); + } + + @Override + public String getUsername() { + return "test"; + } + + @Override + public String getPassword() { + return "test"; + } + }; + + Properties dataSourceProperties = new Properties(); + dataSourceProperties.setProperty("rewriteBatchedStatements", "true"); + dataSourceProperties.setProperty("profileSQL", "true"); + + jdbcContainer.createConnection("", dataSourceProperties); + + // Raw Hashtable-style access (what many real JDBC drivers use) must see the values - + // not just Properties#getProperty(), which would also find them via the old defaults-chain bug. + assertThat(CapturingDriver.lastReceivedProperties.get("rewriteBatchedStatements")).isEqualTo("true"); + assertThat(CapturingDriver.lastReceivedProperties.get("profileSQL")).isEqualTo("true"); + assertThat(CapturingDriver.lastReceivedProperties.keySet()) + .contains("rewriteBatchedStatements", "profileSQL", "user", "password"); + } + + /** + * A minimal fake {@link Driver} that just records the {@link Properties} it was asked to connect with, + * so tests can assert on raw Hashtable-style access rather than the defaults-aware {@code getProperty()}. + */ + public static class CapturingDriver implements Driver { + + static volatile Properties lastReceivedProperties; + + @Override + public Connection connect(String url, Properties info) { + lastReceivedProperties = info; + return mock(Connection.class); + } + + @Override + public boolean acceptsURL(String url) { + return true; + } + + @Override + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) { + return new DriverPropertyInfo[0]; + } + + @Override + public int getMajorVersion() { + return 1; + } + + @Override + public int getMinorVersion() { + return 0; + } + + @Override + public boolean jdbcCompliant() { + return false; + } + + @Override + public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException { + throw new SQLFeatureNotSupportedException(); + } + } + static class JdbcDatabaseContainerStub extends JdbcDatabaseContainer { public JdbcDatabaseContainerStub(@NonNull String dockerImageName) {