From 74b6d652ee2d7a8204dc4af9d9e140b7e5685e49 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 30 Oct 2024 22:13:41 +1300 Subject: [PATCH 1/8] Use pool schema/catalog for original values, lazy initialise originalSchema if required --- .../io/ebean/datasource/DataSourceBuilder.java | 10 ++++++++++ .../io/ebean/datasource/DataSourceConfig.java | 17 +++++++++++++++++ .../ebean/datasource/DataSourceConfigTest.java | 4 ++++ .../src/test/resources/example.properties | 1 + .../src/test/resources/example2.properties | 1 + .../src/test/resources/example3.properties | 1 + .../ebean/datasource/pool/ConnectionPool.java | 10 ++++++++++ .../datasource/pool/PooledConnection.java | 18 ++++++++++++------ 8 files changed, 56 insertions(+), 6 deletions(-) diff --git a/ebean-datasource-api/src/main/java/io/ebean/datasource/DataSourceBuilder.java b/ebean-datasource-api/src/main/java/io/ebean/datasource/DataSourceBuilder.java index a489d70..d59c98b 100644 --- a/ebean-datasource-api/src/main/java/io/ebean/datasource/DataSourceBuilder.java +++ b/ebean-datasource-api/src/main/java/io/ebean/datasource/DataSourceBuilder.java @@ -249,6 +249,11 @@ default DataSourceBuilder schema(String schema) { return setSchema(schema); } + /** + * Set the default database catalog to use. + */ + DataSourceBuilder catalog(String catalog); + /** * @deprecated - migrate to {@link #driver(String)}. */ @@ -812,6 +817,11 @@ interface Settings extends DataSourceBuilder { */ String getSchema(); + /** + * Return the database catalog. + */ + String catalog(); + /** * Return the driver instance to use. */ diff --git a/ebean-datasource-api/src/main/java/io/ebean/datasource/DataSourceConfig.java b/ebean-datasource-api/src/main/java/io/ebean/datasource/DataSourceConfig.java index 24cecb0..23a5084 100644 --- a/ebean-datasource-api/src/main/java/io/ebean/datasource/DataSourceConfig.java +++ b/ebean-datasource-api/src/main/java/io/ebean/datasource/DataSourceConfig.java @@ -36,6 +36,7 @@ public class DataSourceConfig implements DataSourceBuilder.Settings { private String password; private String password2; private String schema; + private String catalog; private Driver driver; private Class driverClass; private String driverClassName; @@ -103,6 +104,7 @@ public DataSourceConfig copy() { copy.password = password; copy.password2 = password2; copy.schema = schema; + copy.catalog = catalog; copy.platform = platform; copy.ownerUsername = ownerUsername; copy.ownerPassword = ownerPassword; @@ -171,6 +173,9 @@ public DataSourceConfig setDefaults(DataSourceBuilder builder) { if (schema == null) { schema = other.getSchema(); } + if (catalog == null) { + catalog = other.catalog(); + } if (minConnections == 2 && other.getMinConnections() < 2) { minConnections = other.getMinConnections(); } @@ -307,6 +312,17 @@ public DataSourceConfig setSchema(String schema) { return this; } + @Override + public String catalog() { + return catalog; + } + + @Override + public DataSourceConfig catalog(String catalog) { + this.catalog = catalog; + return this; + } + @Override public String getDriver() { return driverClassName; @@ -740,6 +756,7 @@ private void loadSettings(ConfigPropertiesHelper properties) { password = properties.get("password", password); password2 = properties.get("password2", password2); schema = properties.get("schema", schema); + catalog = properties.get("catalog", catalog); platform = properties.get("platform", platform); ownerUsername = properties.get("ownerUsername", ownerUsername); ownerPassword = properties.get("ownerPassword", ownerPassword); diff --git a/ebean-datasource-api/src/test/java/io/ebean/datasource/DataSourceConfigTest.java b/ebean-datasource-api/src/test/java/io/ebean/datasource/DataSourceConfigTest.java index 30de897..b0ccb60 100644 --- a/ebean-datasource-api/src/test/java/io/ebean/datasource/DataSourceConfigTest.java +++ b/ebean-datasource-api/src/test/java/io/ebean/datasource/DataSourceConfigTest.java @@ -77,6 +77,7 @@ public void copy() { source.setUrl("url"); source.setReadOnlyUrl("readOnlyUrl"); source.setSchema("sch"); + source.catalog("cat"); Map customSource = new LinkedHashMap<>(); customSource.put("a","a"); @@ -90,6 +91,7 @@ public void copy() { assertEquals("url", copy.getUrl()); assertEquals("readOnlyUrl", copy.getReadOnlyUrl()); assertEquals("sch", copy.getSchema()); + assertEquals("cat", copy.catalog()); assertEquals(42, copy.getMinConnections()); assertEquals(45, copy.getMaxConnections()); @@ -113,6 +115,7 @@ public void defaults() { assertThat(readOnly.getUsername()).isEqualTo(config.getUsername()); assertThat(readOnly.getPassword()).isEqualTo(config.getPassword()); assertThat(readOnly.getSchema()).isEqualTo(config.getSchema()); + assertThat(readOnly.catalog()).isEqualTo(config.catalog()); assertThat(readOnly.getMinConnections()).isEqualTo(config.getMinConnections()); assertThat(readOnly.getCustomProperties()).containsKeys("useSSL"); } @@ -258,6 +261,7 @@ private static void assertConfigValues(DataSourceBuilder.Settings config) { assertThat(config.getUsername()).isEqualTo("myusername"); assertThat(config.getPassword()).isEqualTo("mypassword"); assertThat(config.getSchema()).isEqualTo("myschema"); + assertThat(config.catalog()).isEqualTo("mycat"); assertThat(config.getApplicationName()).isEqualTo("myApp"); Properties clientInfo = config.getClientInfo(); assertThat(clientInfo.getProperty("ClientUser")).isEqualTo("ciu"); diff --git a/ebean-datasource-api/src/test/resources/example.properties b/ebean-datasource-api/src/test/resources/example.properties index 15b3299..0947aac 100644 --- a/ebean-datasource-api/src/test/resources/example.properties +++ b/ebean-datasource-api/src/test/resources/example.properties @@ -1,6 +1,7 @@ datasource.foo.username=myusername datasource.foo.password=mypassword datasource.foo.schema=myschema +datasource.foo.catalog=mycat datasource.foo.url=myUrl datasource.foo.readOnlyUrl=myReadOnlyUrl datasource.foo.applicationName=myApp diff --git a/ebean-datasource-api/src/test/resources/example2.properties b/ebean-datasource-api/src/test/resources/example2.properties index 1282b1c..e078c72 100644 --- a/ebean-datasource-api/src/test/resources/example2.properties +++ b/ebean-datasource-api/src/test/resources/example2.properties @@ -1,6 +1,7 @@ bar.username=myusername bar.password=mypassword bar.schema=myschema +bar.catalog=mycat bar.url=myUrl bar.readOnlyUrl=myReadOnlyUrl bar.applicationName=myApp diff --git a/ebean-datasource-api/src/test/resources/example3.properties b/ebean-datasource-api/src/test/resources/example3.properties index 5283681..2255d0c 100644 --- a/ebean-datasource-api/src/test/resources/example3.properties +++ b/ebean-datasource-api/src/test/resources/example3.properties @@ -1,6 +1,7 @@ username=myusername password=mypassword schema=myschema +catalog=mycat url=myUrl readOnlyUrl=myReadOnlyUrl applicationName=myApp diff --git a/ebean-datasource/src/main/java/io/ebean/datasource/pool/ConnectionPool.java b/ebean-datasource/src/main/java/io/ebean/datasource/pool/ConnectionPool.java index 1e46a55..14d8b59 100644 --- a/ebean-datasource/src/main/java/io/ebean/datasource/pool/ConnectionPool.java +++ b/ebean-datasource/src/main/java/io/ebean/datasource/pool/ConnectionPool.java @@ -42,6 +42,7 @@ final class ConnectionPool implements DataSourcePool { private final List initSql; private final String user; private final String schema; + private final String catalog; private final String heartbeatSql; private final int heartbeatFreqSecs; private final int heartbeatTimeoutSeconds; @@ -119,6 +120,7 @@ final class ConnectionPool implements DataSourcePool { this.clientInfo = params.getClientInfo(); this.queue = new PooledConnectionQueue(this); this.schema = params.getSchema(); + this.catalog = params.catalog(); this.user = params.getUsername(); this.shutdownOnJvmExit = params.isShutdownOnJvmExit(); this.source = DriverDataSource.of(name, params); @@ -251,6 +253,14 @@ public int size() { return size.get(); } + String schema() { + return schema; + } + + String catalog() { + return catalog; + } + /** * Increment the current pool size. */ diff --git a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java index fba3005..f5315b3 100644 --- a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java +++ b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java @@ -86,8 +86,8 @@ final class PooledConnection extends ConnectionDelegator { private boolean resetCatalog; private String currentSchema; private String currentCatalog; - private final String originalSchema; - private final String originalCatalog; + private String originalSchema; + private String originalCatalog; private long startUseTime; private long lastUseTime; @@ -118,12 +118,12 @@ final class PooledConnection extends ConnectionDelegator { this.pool = pool; this.connection = connection; this.name = pool.name() + uniqueId; + this.originalSchema = pool.schema(); + this.originalCatalog = pool.catalog(); this.pstmtCache = new PstmtCache(pool.pstmtCacheSize()); this.maxStackTrace = pool.maxStackTraceSize(); this.creationTime = System.currentTimeMillis(); this.lastUseTime = creationTime; - this.currentSchema = this.originalSchema = connection.getSchema(); - this.currentCatalog = this.originalCatalog = connection.getCatalog(); pool.inc(); } @@ -139,8 +139,6 @@ final class PooledConnection extends ConnectionDelegator { this.maxStackTrace = 0; this.creationTime = System.currentTimeMillis(); this.lastUseTime = creationTime; - this.currentSchema = this.originalSchema = "DEFAULT"; - this.currentCatalog = this.originalCatalog = "DEFAULT"; } /** @@ -700,6 +698,10 @@ public void setSchema(String schema) throws SQLException { if (status == STATUS_IDLE) { throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setSchema()"); } + if (originalSchema == null) { + // lazily initialise the originalSchema + originalSchema = getSchema(); + } currentSchema = schema; resetSchema = true; connection.setSchema(schema); @@ -710,6 +712,10 @@ public void setCatalog(String catalog) throws SQLException { if (status == STATUS_IDLE) { throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setCatalog()"); } + if (originalCatalog == null) { + // lazily initialise the originalCatalog + originalCatalog = getCatalog(); + } currentCatalog = catalog; resetCatalog = true; connection.setCatalog(catalog); From f9466b14f7a10d0b802b021080f70ce5d5c81fa2 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 30 Oct 2024 22:23:45 +1300 Subject: [PATCH 2/8] Reset the currentSchema and currentCatalog back to null on reset --- .../main/java/io/ebean/datasource/pool/PooledConnection.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java index f5315b3..f4a5fbf 100644 --- a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java +++ b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java @@ -422,11 +422,13 @@ public void close() throws SQLException { if (resetSchema) { connection.setSchema(originalSchema); + currentSchema = null; resetSchema = false; } if (resetCatalog) { connection.setCatalog(originalCatalog); + currentCatalog = null; resetCatalog = false; } From b3077ee1f6fee020f3af634a43a6cdf0a78d05bf Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Thu, 31 Oct 2024 17:55:48 +1300 Subject: [PATCH 3/8] Add initialisedSchema and initialisedCatalog to handle the null schema/catalog case --- .../io/ebean/datasource/pool/PooledConnection.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java index f4a5fbf..5dbac4f 100644 --- a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java +++ b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java @@ -84,6 +84,8 @@ final class PooledConnection extends ConnectionDelegator { private boolean resetAutoCommit; private boolean resetSchema; private boolean resetCatalog; + private boolean initialisedSchema; + private boolean initialisedCatalog; private String currentSchema; private String currentCatalog; private String originalSchema; @@ -120,6 +122,8 @@ final class PooledConnection extends ConnectionDelegator { this.name = pool.name() + uniqueId; this.originalSchema = pool.schema(); this.originalCatalog = pool.catalog(); + this.initialisedSchema = originalSchema != null; + this.initialisedCatalog = originalCatalog != null; this.pstmtCache = new PstmtCache(pool.pstmtCacheSize()); this.maxStackTrace = pool.maxStackTraceSize(); this.creationTime = System.currentTimeMillis(); @@ -700,9 +704,10 @@ public void setSchema(String schema) throws SQLException { if (status == STATUS_IDLE) { throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setSchema()"); } - if (originalSchema == null) { + if (!initialisedSchema) { // lazily initialise the originalSchema originalSchema = getSchema(); + initialisedSchema = true; } currentSchema = schema; resetSchema = true; @@ -714,9 +719,9 @@ public void setCatalog(String catalog) throws SQLException { if (status == STATUS_IDLE) { throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setCatalog()"); } - if (originalCatalog == null) { - // lazily initialise the originalCatalog + if (!initialisedCatalog) { originalCatalog = getCatalog(); + initialisedCatalog = true; } currentCatalog = catalog; resetCatalog = true; From 536d15e4e29af20d64cb23a95b2cecb426e05f51 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Fri, 1 Nov 2024 16:11:16 +1300 Subject: [PATCH 4/8] Use "@default" for currentSchema and currentCatalog (when it could be null). The problematic case that this change addresses is: Imagine the connection is "fresh", and the connection-string defines something like schema=S1 So the current schema of the connection would be S1, but currentSchema is still null. So we prepare a statement for S1, but the cache key contains null. If we then invoke setSchema(null) the connection is in schema-less state. Preparing the same statement would retrieve the one from above, which is prepared for S1. --- The FIX for this case, use "@default" instead of null, such that the cache key isn't problematic for the above case. --- .../main/java/io/ebean/datasource/pool/PooledConnection.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java index 5dbac4f..cd51fb7 100644 --- a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java +++ b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java @@ -124,6 +124,8 @@ final class PooledConnection extends ConnectionDelegator { this.originalCatalog = pool.catalog(); this.initialisedSchema = originalSchema != null; this.initialisedCatalog = originalCatalog != null; + this.currentSchema = originalSchema != null ? originalSchema : "@default"; + this.currentCatalog = originalCatalog != null ? originalCatalog : "@default"; this.pstmtCache = new PstmtCache(pool.pstmtCacheSize()); this.maxStackTrace = pool.maxStackTraceSize(); this.creationTime = System.currentTimeMillis(); From b4277006b1829acb6f4da5a6935bfde690bf1a30 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Tue, 5 Nov 2024 08:12:06 +0100 Subject: [PATCH 5/8] Fix: Missing catalog initialization --- .../src/main/java/io/ebean/datasource/pool/ConnectionPool.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ebean-datasource/src/main/java/io/ebean/datasource/pool/ConnectionPool.java b/ebean-datasource/src/main/java/io/ebean/datasource/pool/ConnectionPool.java index 14d8b59..6106625 100644 --- a/ebean-datasource/src/main/java/io/ebean/datasource/pool/ConnectionPool.java +++ b/ebean-datasource/src/main/java/io/ebean/datasource/pool/ConnectionPool.java @@ -398,6 +398,9 @@ private Connection initConnection(Connection conn) throws SQLException { if (readOnly) { conn.setReadOnly(true); } + if (catalog != null) { + conn.setCatalog(catalog); + } if (schema != null) { conn.setSchema(schema); } From 921b428310a742ae14bc8851fd9aee2a022c220b Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Tue, 5 Nov 2024 08:21:53 +0100 Subject: [PATCH 6/8] make suggestion with state variables --- .../datasource/pool/PooledConnection.java | 98 ++++++++++++++----- 1 file changed, 71 insertions(+), 27 deletions(-) diff --git a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java index cd51fb7..6adcd6a 100644 --- a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java +++ b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java @@ -57,6 +57,32 @@ final class PooledConnection extends ConnectionDelegator { private static final int RO_MYSQL_1290 = 1290; + /** + * Constant for schema/catalog, when we are in SCHEMA_CATALOG_UNKNOWN state + * This is used for correct cache key computation. (We cannot use 'null' + * here, as we might get a collision in edge cases) + */ + private static final String UNKNOWN = "@unknown"; + + /** + * The schema/catalog is unknown, that means, we have not yet touched the + * value on the underlying connection. + * We do not have to restore it. + */ + private static final int SCHEMA_CATALOG_UNKNOWN = 0; + + /** + * The schema/catalog is changed. The original value has to be restored on + * close() + */ + private static final int SCHEMA_CATALOG_CHANGED = 1; + + /** + * We know the original value of the underlying connection, but there is no + * demand to restore it. + */ + private static final int SCHEMA_CATALOG_KNOWN = 2; + private final String name; private final ConnectionPool pool; private final Connection connection; @@ -82,12 +108,14 @@ final class PooledConnection extends ConnectionDelegator { */ private boolean failoverToReadOnly; private boolean resetAutoCommit; - private boolean resetSchema; - private boolean resetCatalog; - private boolean initialisedSchema; - private boolean initialisedCatalog; - private String currentSchema; - private String currentCatalog; + private int schemaState = SCHEMA_CATALOG_UNKNOWN; + private int catalogState = SCHEMA_CATALOG_UNKNOWN; + + // this is used for cache computation + private String cacheKeySchema = UNKNOWN; + private String cacheKeyCatalog = UNKNOWN; + + // original values are lazily initialized and restored on close() private String originalSchema; private String originalCatalog; @@ -122,10 +150,17 @@ final class PooledConnection extends ConnectionDelegator { this.name = pool.name() + uniqueId; this.originalSchema = pool.schema(); this.originalCatalog = pool.catalog(); - this.initialisedSchema = originalSchema != null; - this.initialisedCatalog = originalCatalog != null; - this.currentSchema = originalSchema != null ? originalSchema : "@default"; - this.currentCatalog = originalCatalog != null ? originalCatalog : "@default"; + if (originalSchema != null) { + schemaState = SCHEMA_CATALOG_KNOWN; + this.cacheKeySchema = originalSchema; + // if schema & catalog is defined, we can be sure, that connection is initialized properly + assert originalSchema.equals(connection.getSchema()) : "Connection is in the wrong schema: " + connection.getSchema() + ", expected: " + originalSchema; + } + if (originalCatalog != null) { + catalogState = SCHEMA_CATALOG_KNOWN; + this.cacheKeyCatalog = originalCatalog; + assert originalCatalog.equals(connection.getCatalog()) : "Connection is in the wrong catalog: " + connection.getCatalog() + ", expected: " + originalCatalog; + } this.pstmtCache = new PstmtCache(pool.pstmtCacheSize()); this.maxStackTrace = pool.maxStackTraceSize(); this.creationTime = System.currentTimeMillis(); @@ -287,7 +322,7 @@ void returnPreparedStatement(ExtendedPreparedStatement pstmt) { */ @Override public PreparedStatement prepareStatement(String sql, int returnKeysFlag) throws SQLException { - String key = sql + ':' + currentSchema + ':' + currentCatalog + ':' + returnKeysFlag; + String key = sql + ':' + cacheKeySchema + ':' + cacheKeyCatalog + ':' + returnKeysFlag; return prepareStatement(sql, true, returnKeysFlag, key); } @@ -296,7 +331,7 @@ public PreparedStatement prepareStatement(String sql, int returnKeysFlag) throws */ @Override public PreparedStatement prepareStatement(String sql) throws SQLException { - String key = sql + ':' + currentSchema + ':' + currentCatalog; + String key = sql + ':' + cacheKeySchema + ':' + cacheKeyCatalog; return prepareStatement(sql, false, 0, key); } @@ -350,12 +385,21 @@ public PreparedStatement prepareStatement(String sql, int resultSetType, int res * Reset the connection for returning to the client. Resets the status, * startUseTime and hadErrors. */ - void resetForUse() { + void resetForUse() throws SQLException { this.status = STATUS_ACTIVE; this.startUseTime = System.currentTimeMillis(); this.createdByMethod = null; this.lastStatement = null; this.hadErrors = false; + // CHECKME: Shoud we keep the asserts here or should we even reset schema/catalog here + assert schemaState != SCHEMA_CATALOG_CHANGED : "connection is in the wrong state (not properly closed)"; + if (schemaState == SCHEMA_CATALOG_KNOWN) { + assert originalSchema.equals(getSchema()) : "connection is in the wrong schema: " + getSchema() + ", expected: " + originalSchema; + } + assert catalogState != SCHEMA_CATALOG_CHANGED : "connection is in the wrong state (not properly closed)"; + if (catalogState == SCHEMA_CATALOG_KNOWN) { + assert originalCatalog.equals(getCatalog()) : "connection is in the wrong catalog: " + getCatalog() + ", expected: " + originalCatalog; + } } /** @@ -426,16 +470,17 @@ public void close() throws SQLException { resetIsolationReadOnlyRequired = false; } - if (resetSchema) { + if (schemaState == SCHEMA_CATALOG_CHANGED) { connection.setSchema(originalSchema); - currentSchema = null; - resetSchema = false; + // we can use original value for cache computation from now on + cacheKeySchema = originalSchema; + schemaState = SCHEMA_CATALOG_KNOWN; } - if (resetCatalog) { + if (catalogState == SCHEMA_CATALOG_CHANGED) { connection.setCatalog(originalCatalog); - currentCatalog = null; - resetCatalog = false; + cacheKeyCatalog = originalCatalog; + catalogState = SCHEMA_CATALOG_KNOWN; } // the connection is assumed GOOD so put it back in the pool @@ -706,13 +751,13 @@ public void setSchema(String schema) throws SQLException { if (status == STATUS_IDLE) { throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setSchema()"); } - if (!initialisedSchema) { + if (schemaState == SCHEMA_CATALOG_UNKNOWN) { // lazily initialise the originalSchema originalSchema = getSchema(); - initialisedSchema = true; + // state would be KNOWN here } - currentSchema = schema; - resetSchema = true; + schemaState = SCHEMA_CATALOG_CHANGED; + cacheKeySchema = schema; connection.setSchema(schema); } @@ -721,12 +766,11 @@ public void setCatalog(String catalog) throws SQLException { if (status == STATUS_IDLE) { throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setCatalog()"); } - if (!initialisedCatalog) { + if (schemaState == SCHEMA_CATALOG_UNKNOWN) { originalCatalog = getCatalog(); - initialisedCatalog = true; } - currentCatalog = catalog; - resetCatalog = true; + catalogState = SCHEMA_CATALOG_CHANGED; + cacheKeyCatalog = catalog; connection.setCatalog(catalog); } From 6e5d4427aa2336e3517d9a83d2440cc85ab5b3da Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 6 Nov 2024 07:48:26 +1300 Subject: [PATCH 7/8] For PooledConnection.resetForUse() remove the asserts The resetForUse() is called "on borrow" just before the connection is returned to the application. As such we can't do anything that could throw any Exception here. All connection reset actions must be performed when the connection is returned to the pool. --- .../io/ebean/datasource/pool/PooledConnection.java | 11 +---------- .../io/ebean/datasource/test/PostgresInitTest.java | 4 ++-- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java index 6adcd6a..db5f529 100644 --- a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java +++ b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java @@ -385,21 +385,12 @@ public PreparedStatement prepareStatement(String sql, int resultSetType, int res * Reset the connection for returning to the client. Resets the status, * startUseTime and hadErrors. */ - void resetForUse() throws SQLException { + void resetForUse() { this.status = STATUS_ACTIVE; this.startUseTime = System.currentTimeMillis(); this.createdByMethod = null; this.lastStatement = null; this.hadErrors = false; - // CHECKME: Shoud we keep the asserts here or should we even reset schema/catalog here - assert schemaState != SCHEMA_CATALOG_CHANGED : "connection is in the wrong state (not properly closed)"; - if (schemaState == SCHEMA_CATALOG_KNOWN) { - assert originalSchema.equals(getSchema()) : "connection is in the wrong schema: " + getSchema() + ", expected: " + originalSchema; - } - assert catalogState != SCHEMA_CATALOG_CHANGED : "connection is in the wrong state (not properly closed)"; - if (catalogState == SCHEMA_CATALOG_KNOWN) { - assert originalCatalog.equals(getCatalog()) : "connection is in the wrong catalog: " + getCatalog() + ", expected: " + originalCatalog; - } } /** diff --git a/ebean-datasource/src/test/java/io/ebean/datasource/test/PostgresInitTest.java b/ebean-datasource/src/test/java/io/ebean/datasource/test/PostgresInitTest.java index d8a8f16..e330b22 100644 --- a/ebean-datasource/src/test/java/io/ebean/datasource/test/PostgresInitTest.java +++ b/ebean-datasource/src/test/java/io/ebean/datasource/test/PostgresInitTest.java @@ -81,7 +81,7 @@ void test_with_clientInfo() throws SQLException { void test_with_applicationNameAndSchema() throws SQLException { DataSourceConfig ds = new DataSourceConfig(); ds.setUrl("jdbc:postgresql://127.0.0.1:9999/app"); - ds.setSchema("fred"); + ds.setSchema("public"); ds.setUsername("db_owner"); ds.setPassword("test"); ds.setApplicationName("my-application-name"); @@ -112,7 +112,7 @@ void test_with_applicationNameAndSchema() throws SQLException { void test_password2() throws SQLException { DataSourceConfig ds = new DataSourceConfig(); ds.setUrl("jdbc:postgresql://127.0.0.1:9999/app"); - ds.setSchema("fred"); + ds.setSchema("public"); ds.setUsername("db_owner"); ds.setPassword("test"); ds.setPassword2("newRolledPassword"); From a8d736f363aa312593f3e34fb5f39dfcbeb8c1d0 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Thu, 7 Nov 2024 21:19:17 +1300 Subject: [PATCH 8/8] Reset catalog before schema and remove asserts on setting original schema/catalog --- .../datasource/pool/PooledConnection.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java index db5f529..3bdb37e 100644 --- a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java +++ b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnection.java @@ -151,15 +151,12 @@ final class PooledConnection extends ConnectionDelegator { this.originalSchema = pool.schema(); this.originalCatalog = pool.catalog(); if (originalSchema != null) { - schemaState = SCHEMA_CATALOG_KNOWN; + this.schemaState = SCHEMA_CATALOG_KNOWN; this.cacheKeySchema = originalSchema; - // if schema & catalog is defined, we can be sure, that connection is initialized properly - assert originalSchema.equals(connection.getSchema()) : "Connection is in the wrong schema: " + connection.getSchema() + ", expected: " + originalSchema; } if (originalCatalog != null) { - catalogState = SCHEMA_CATALOG_KNOWN; + this.catalogState = SCHEMA_CATALOG_KNOWN; this.cacheKeyCatalog = originalCatalog; - assert originalCatalog.equals(connection.getCatalog()) : "Connection is in the wrong catalog: " + connection.getCatalog() + ", expected: " + originalCatalog; } this.pstmtCache = new PstmtCache(pool.pstmtCacheSize()); this.maxStackTrace = pool.maxStackTraceSize(); @@ -461,6 +458,12 @@ public void close() throws SQLException { resetIsolationReadOnlyRequired = false; } + if (catalogState == SCHEMA_CATALOG_CHANGED) { + connection.setCatalog(originalCatalog); + cacheKeyCatalog = originalCatalog; + catalogState = SCHEMA_CATALOG_KNOWN; + } + if (schemaState == SCHEMA_CATALOG_CHANGED) { connection.setSchema(originalSchema); // we can use original value for cache computation from now on @@ -468,12 +471,6 @@ public void close() throws SQLException { schemaState = SCHEMA_CATALOG_KNOWN; } - if (catalogState == SCHEMA_CATALOG_CHANGED) { - connection.setCatalog(originalCatalog); - cacheKeyCatalog = originalCatalog; - catalogState = SCHEMA_CATALOG_KNOWN; - } - // the connection is assumed GOOD so put it back in the pool lastUseTime = System.currentTimeMillis(); connection.clearWarnings();