From 8be8e411103e5a0078a0c40277eddce203b15fb4 Mon Sep 17 00:00:00 2001 From: Jocelyne <jocelyne.abihaidar@jetbrains.com> Date: Tue, 10 Dec 2024 14:36:13 +0100 Subject: [PATCH 1/5] fix: EXPOSED-662 SchemaUtils.listTables() closes connection to db For some unclear reason, the changes made [here](https://github.com/JetBrains/Exposed/pull/2301/commits/008faf34f498b0481d6e2c71e96dbcf2cf199d5f) were causing the database connection to close. This fixes that by adding a new function `tableNamesForAllSchemas()` and invoking that in `allTablesNames()`. --- exposed-core/api/exposed-core.api | 1 + .../statements/api/ExposedDatabaseMetadata.kt | 2 ++ .../exposed/sql/vendors/VendorDialect.kt | 8 +++----- exposed-jdbc/api/exposed-jdbc.api | 1 + .../jdbc/JdbcDatabaseMetadataImpl.kt | 3 +++ .../sql/tests/shared/ddl/CreateTableTests.kt | 20 +++++++++++++++++++ 6 files changed, 30 insertions(+), 5 deletions(-) diff --git a/exposed-core/api/exposed-core.api b/exposed-core/api/exposed-core.api index 30280279f2..1e56a5b9fd 100644 --- a/exposed-core/api/exposed-core.api +++ b/exposed-core/api/exposed-core.api @@ -3580,6 +3580,7 @@ public abstract class org/jetbrains/exposed/sql/statements/api/ExposedDatabaseMe public abstract fun sequences ()Ljava/util/List; public abstract fun tableConstraints (Ljava/util/List;)Ljava/util/Map; public abstract fun tableNamesByCurrentSchema (Ljava/util/Map;)Lorg/jetbrains/exposed/sql/vendors/SchemaMetadata; + public abstract fun tableNamesForAllSchemas ()Ljava/util/List; } public abstract class org/jetbrains/exposed/sql/statements/api/ExposedSavepoint { diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/api/ExposedDatabaseMetadata.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/api/ExposedDatabaseMetadata.kt index 78271a0e70..817eacb25a 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/api/ExposedDatabaseMetadata.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/api/ExposedDatabaseMetadata.kt @@ -55,6 +55,8 @@ abstract class ExposedDatabaseMetadata(val database: String) { */ abstract fun tableNamesByCurrentSchema(tableNamesCache: Map<String, List<String>>?): SchemaMetadata + abstract fun tableNamesForAllSchemas(): List<String> + /** Returns a map with the [ColumnMetadata] of all the defined columns in each of the specified [tables]. */ abstract fun columns(vararg tables: Table): Map<Table, List<ColumnMetadata>> diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/VendorDialect.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/VendorDialect.kt index 4d3c979a82..0fe4c3cbad 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/VendorDialect.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/VendorDialect.kt @@ -32,17 +32,15 @@ abstract class VendorDialect( } protected fun getAllTableNamesCache(): Map<String, List<String>> { - val connection = TransactionManager.current().connection if (_allTableNames == null) { - _allTableNames = connection.metadata { tableNames } + _allTableNames = TransactionManager.current().connection.metadata { tableNames } } return _allTableNames!! } private fun getAllSchemaNamesCache(): List<String> { - val connection = TransactionManager.current().connection if (_allSchemaNames == null) { - _allSchemaNames = connection.metadata { schemaNames } + _allSchemaNames = TransactionManager.current().connection.metadata { schemaNames } } return _allSchemaNames!! } @@ -53,7 +51,7 @@ abstract class VendorDialect( /** Returns a list with the names of all the defined tables with schema prefixes if the database supports it. */ override fun allTablesNames(): List<String> = TransactionManager.current().connection.metadata { - getAllTableNamesCache().flatMap { it.value } + tableNamesForAllSchemas() } override fun tableExists(table: Table): Boolean { diff --git a/exposed-jdbc/api/exposed-jdbc.api b/exposed-jdbc/api/exposed-jdbc.api index 5ee212d776..6724deac1c 100644 --- a/exposed-jdbc/api/exposed-jdbc.api +++ b/exposed-jdbc/api/exposed-jdbc.api @@ -55,6 +55,7 @@ public final class org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadat public fun sequences ()Ljava/util/List; public fun tableConstraints (Ljava/util/List;)Ljava/util/Map; public fun tableNamesByCurrentSchema (Ljava/util/Map;)Lorg/jetbrains/exposed/sql/vendors/SchemaMetadata; + public fun tableNamesForAllSchemas ()Ljava/util/List; } public final class org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl$Companion { diff --git a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt index e8d8a7d34b..6390dc6db9 100644 --- a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt +++ b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt @@ -131,6 +131,9 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData) return SchemaMetadata(currentSchema!!, tablesInSchema) } + override fun tableNamesForAllSchemas(): List<String> = + schemaNames.flatMap { tableNamesFor(it) } + private fun ResultSet.extractColumns(): List<ColumnMetadata> { val result = mutableListOf<ColumnMetadata>() while (next()) { diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateTableTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateTableTests.kt index 80f1d5c9b0..12d27a13e0 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateTableTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateTableTests.kt @@ -13,6 +13,7 @@ import org.jetbrains.exposed.sql.tests.shared.Category import org.jetbrains.exposed.sql.tests.shared.Item import org.jetbrains.exposed.sql.tests.shared.assertEqualCollections import org.jetbrains.exposed.sql.tests.shared.assertEquals +import org.jetbrains.exposed.sql.tests.shared.assertFalse import org.jetbrains.exposed.sql.tests.shared.assertTrue import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.vendors.MysqlDialect @@ -666,4 +667,23 @@ class CreateTableTests : DatabaseTestsBase() { } } } + + @Test + fun testListTablesDoesNotCloseDatabaseConnection() { + val tester = object : IntIdTable("tester") { + val int = integer("intColumn") + } + withDb { testDb -> + val defaultSchemaName = when (currentDialectTest) { + is SQLServerDialect -> "dbo" + is OracleDialect -> testDb.user + is MysqlDialect -> testDb.db!!.name + else -> "public" + } + assertTrue(SchemaUtils.listTables().none { it.equals("$defaultSchemaName.${OneTable.tableName}", ignoreCase = true) }) + } + withDb { + assertFalse(tester.exists()) + } + } } From bf6a070b49326c780a24334aacc17a8aaf4f6e40 Mon Sep 17 00:00:00 2001 From: Chantal Loncle <82039410+bog-walk@users.noreply.github.com> Date: Thu, 9 Jan 2025 06:47:44 -0500 Subject: [PATCH 2/5] fix: EXPOSED-662 SchemaUtils.listTables() returns empty list & closes db connection - Revert change to VendorDialect.allTablesNames() so that both it and SchemaUtils.listTables() has original behavior (only returns lists in current schema) - Add new SchemaUtils method to alternatively return list of all table names in all schemas. - Adjust tests to invoke listTables() without prior call to exists(). --- exposed-core/api/exposed-core.api | 4 +- .../org/jetbrains/exposed/sql/SchemaUtils.kt | 11 ++- .../statements/api/ExposedDatabaseMetadata.kt | 2 - .../exposed/sql/vendors/DatabaseDialect.kt | 12 ++- .../exposed/sql/vendors/VendorDialect.kt | 21 ++++- exposed-jdbc/api/exposed-jdbc.api | 1 - .../jdbc/JdbcDatabaseMetadataImpl.kt | 3 - .../sql/tests/shared/ddl/CreateTableTests.kt | 82 +++++++++++++------ 8 files changed, 97 insertions(+), 39 deletions(-) diff --git a/exposed-core/api/exposed-core.api b/exposed-core/api/exposed-core.api index 1e56a5b9fd..8aeff76133 100644 --- a/exposed-core/api/exposed-core.api +++ b/exposed-core/api/exposed-core.api @@ -2202,6 +2202,7 @@ public final class org/jetbrains/exposed/sql/SchemaUtils { public static synthetic fun dropSequence$default (Lorg/jetbrains/exposed/sql/SchemaUtils;[Lorg/jetbrains/exposed/sql/Sequence;ZILjava/lang/Object;)V public final fun listDatabases ()Ljava/util/List; public final fun listTables ()Ljava/util/List; + public final fun listTablesInAllSchemas ()Ljava/util/List; public final fun setSchema (Lorg/jetbrains/exposed/sql/Schema;Z)V public static synthetic fun setSchema$default (Lorg/jetbrains/exposed/sql/SchemaUtils;Lorg/jetbrains/exposed/sql/Schema;ZILjava/lang/Object;)V public final fun sortTablesByReferences (Ljava/lang/Iterable;)Ljava/util/List; @@ -3580,7 +3581,6 @@ public abstract class org/jetbrains/exposed/sql/statements/api/ExposedDatabaseMe public abstract fun sequences ()Ljava/util/List; public abstract fun tableConstraints (Ljava/util/List;)Ljava/util/Map; public abstract fun tableNamesByCurrentSchema (Ljava/util/Map;)Lorg/jetbrains/exposed/sql/vendors/SchemaMetadata; - public abstract fun tableNamesForAllSchemas ()Ljava/util/List; } public abstract class org/jetbrains/exposed/sql/statements/api/ExposedSavepoint { @@ -3825,6 +3825,7 @@ public abstract interface class org/jetbrains/exposed/sql/vendors/DatabaseDialec public static final field Companion Lorg/jetbrains/exposed/sql/vendors/DatabaseDialect$Companion; public abstract fun addPrimaryKey (Lorg/jetbrains/exposed/sql/Table;Ljava/lang/String;[Lorg/jetbrains/exposed/sql/Column;)Ljava/lang/String; public abstract fun allTablesNames ()Ljava/util/List; + public abstract fun allTablesNamesInAllSchemas ()Ljava/util/List; public abstract fun catalog (Lorg/jetbrains/exposed/sql/Transaction;)Ljava/lang/String; public abstract fun checkTableMapping (Lorg/jetbrains/exposed/sql/Table;)Z public abstract fun columnConstraints ([Lorg/jetbrains/exposed/sql/Table;)Ljava/util/Map; @@ -4321,6 +4322,7 @@ public abstract class org/jetbrains/exposed/sql/vendors/VendorDialect : org/jetb public fun <init> (Ljava/lang/String;Lorg/jetbrains/exposed/sql/vendors/DataTypeProvider;Lorg/jetbrains/exposed/sql/vendors/FunctionProvider;)V public fun addPrimaryKey (Lorg/jetbrains/exposed/sql/Table;Ljava/lang/String;[Lorg/jetbrains/exposed/sql/Column;)Ljava/lang/String; public fun allTablesNames ()Ljava/util/List; + public fun allTablesNamesInAllSchemas ()Ljava/util/List; public fun catalog (Lorg/jetbrains/exposed/sql/Transaction;)Ljava/lang/String; public fun checkTableMapping (Lorg/jetbrains/exposed/sql/Table;)Z public fun columnConstraints ([Lorg/jetbrains/exposed/sql/Table;)Ljava/util/Map; diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SchemaUtils.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SchemaUtils.kt index 5f9de60092..705c1c3dff 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SchemaUtils.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SchemaUtils.kt @@ -856,12 +856,21 @@ object SchemaUtils { } /** - * Retrieves a list of all table names in the current database. + * Retrieves a list of all table names in the current database schema. + * The names will be returned with schema prefixes if the database supports it. * * @return A list of table names as strings. */ fun listTables(): List<String> = currentDialect.allTablesNames() + /** + * Retrieves a list of all table names in the current database schema. + * The names will be returned with schema prefixes if the database supports it. + * + * @return A list of table names as strings. + */ + fun listTablesInAllSchemas(): List<String> = currentDialect.allTablesNamesInAllSchemas() + /** Drops all [tables], using a batch execution if [inBatch] is set to `true`. */ fun drop(vararg tables: Table, inBatch: Boolean = false) { if (tables.isEmpty()) return diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/api/ExposedDatabaseMetadata.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/api/ExposedDatabaseMetadata.kt index 817eacb25a..78271a0e70 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/api/ExposedDatabaseMetadata.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/api/ExposedDatabaseMetadata.kt @@ -55,8 +55,6 @@ abstract class ExposedDatabaseMetadata(val database: String) { */ abstract fun tableNamesByCurrentSchema(tableNamesCache: Map<String, List<String>>?): SchemaMetadata - abstract fun tableNamesForAllSchemas(): List<String> - /** Returns a map with the [ColumnMetadata] of all the defined columns in each of the specified [tables]. */ abstract fun columns(vararg tables: Table): Map<Table, List<ColumnMetadata>> diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DatabaseDialect.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DatabaseDialect.kt index 407b302eae..f6ae982aa6 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DatabaseDialect.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DatabaseDialect.kt @@ -78,9 +78,19 @@ interface DatabaseDialect { /** Returns the name of the current database. */ fun getDatabase(): String - /** Returns a list with the names of all the defined tables. */ + /** + * Returns a list with the names of all the defined tables in the current database schema. + * The names will be returned with schema prefixes if the database supports it. + */ fun allTablesNames(): List<String> + /** + * Returns a list with the names of all the tables in all database schemas. + * The names will be returned with schema prefixes, if the database supports it, and non-user defined tables, + * like system information table names, will be included. + */ + fun allTablesNamesInAllSchemas(): List<String> + /** Checks if the specified table exists in the database. */ fun tableExists(table: Table): Boolean diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/VendorDialect.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/VendorDialect.kt index 0fe4c3cbad..a66419c5a5 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/VendorDialect.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/VendorDialect.kt @@ -24,7 +24,7 @@ abstract class VendorDialect( private var _allTableNames: Map<String, List<String>>? = null private var _allSchemaNames: List<String>? = null - /** Returns a list with the names of all the defined tables within default scheme. */ + /** Returns a list with the names of all the defined tables in the current schema. */ val allTablesNames: List<String> get() { val connection = TransactionManager.current().connection @@ -49,9 +49,24 @@ abstract class VendorDialect( override fun getDatabase(): String = catalog(TransactionManager.current()) - /** Returns a list with the names of all the defined tables with schema prefixes if the database supports it. */ + /** + * Returns a list with the names of all the defined tables in the current database schema. + * The names will be returned with schema prefixes if the database supports it. + * + * **Note:** This method always re-reads data from the database. Using `allTablesNames` field is + * the preferred way to avoid unnecessary metadata queries. + */ override fun allTablesNames(): List<String> = TransactionManager.current().connection.metadata { - tableNamesForAllSchemas() + tableNamesByCurrentSchema(null).tableNames + } + + /** + * Returns a list with the names of all the tables in all database schemas. + * The names will be returned with schema prefixes, if the database supports it, and non-user defined tables, + * like system information table names, will be included. + */ + override fun allTablesNamesInAllSchemas(): List<String> = getAllSchemaNamesCache().flatMap { schema -> + getAllTableNamesCache().getValue(schema) } override fun tableExists(table: Table): Boolean { diff --git a/exposed-jdbc/api/exposed-jdbc.api b/exposed-jdbc/api/exposed-jdbc.api index 6724deac1c..5ee212d776 100644 --- a/exposed-jdbc/api/exposed-jdbc.api +++ b/exposed-jdbc/api/exposed-jdbc.api @@ -55,7 +55,6 @@ public final class org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadat public fun sequences ()Ljava/util/List; public fun tableConstraints (Ljava/util/List;)Ljava/util/Map; public fun tableNamesByCurrentSchema (Ljava/util/Map;)Lorg/jetbrains/exposed/sql/vendors/SchemaMetadata; - public fun tableNamesForAllSchemas ()Ljava/util/List; } public final class org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl$Companion { diff --git a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt index 6390dc6db9..e8d8a7d34b 100644 --- a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt +++ b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt @@ -131,9 +131,6 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData) return SchemaMetadata(currentSchema!!, tablesInSchema) } - override fun tableNamesForAllSchemas(): List<String> = - schemaNames.flatMap { tableNamesFor(it) } - private fun ResultSet.extractColumns(): List<ColumnMetadata> { val result = mutableListOf<ColumnMetadata>() while (next()) { diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateTableTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateTableTests.kt index 12d27a13e0..b19da9bc77 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateTableTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateTableTests.kt @@ -19,6 +19,7 @@ import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.vendors.MysqlDialect import org.jetbrains.exposed.sql.vendors.OracleDialect import org.jetbrains.exposed.sql.vendors.SQLServerDialect +import org.jetbrains.exposed.sql.vendors.SQLiteDialect import org.junit.Test import java.util.* import kotlin.test.assertFails @@ -596,20 +597,15 @@ class CreateTableTests : DatabaseTestsBase() { assertEquals(true, OneTable.exists()) assertEquals(false, OneOneTable.exists()) - val defaultSchemaName = when (currentDialectTest) { - is SQLServerDialect -> "dbo" - is OracleDialect -> testDb.user - is MysqlDialect -> testDb.db!!.name - else -> "public" - } - assertTrue(SchemaUtils.listTables().any { it.equals("$defaultSchemaName.${OneTable.tableName}", ignoreCase = true) }) + val schemaPrefixedName = testDb.getDefaultSchemaPrefixedTableName(OneTable.tableName) + assertTrue(SchemaUtils.listTables().any { it.equals(schemaPrefixedName, ignoreCase = true) }) SchemaUtils.createSchema(one) SchemaUtils.create(OneOneTable) assertEquals(true, OneTable.exists()) assertEquals(true, OneOneTable.exists()) - assertTrue(SchemaUtils.listTables().any { it.equals(OneOneTable.tableName, ignoreCase = true) }) + assertTrue(SchemaUtils.listTablesInAllSchemas().any { it.equals(OneOneTable.tableName, ignoreCase = true) }) } finally { SchemaUtils.drop(OneTable, OneOneTable) val cascade = testDb != TestDB.SQLSERVER @@ -618,6 +614,57 @@ class CreateTableTests : DatabaseTestsBase() { } } + @Test + fun testListTablesInCurrentSchema() { + withDb { testDb -> + SchemaUtils.create(OneTable) + + val schemaPrefixedName = testDb.getDefaultSchemaPrefixedTableName(OneTable.tableName) + assertTrue(SchemaUtils.listTables().any { it.equals(schemaPrefixedName, ignoreCase = true) }) + } + + withDb { testDb -> + // ensures that db connection has not been lost by calling listTables() + assertEquals(testDb != TestDB.SQLITE, OneTable.exists()) + + SchemaUtils.drop(OneTable) + } + } + + private fun TestDB.getDefaultSchemaPrefixedTableName(tableName: String): String = when (currentDialectTest) { + is SQLServerDialect -> "dbo.$tableName" + is OracleDialect -> "${this.user}.$tableName" + is MysqlDialect -> "${this.db!!.name}.$tableName" + is SQLiteDialect -> tableName + else -> "public.$tableName" + } + + @Test + fun testListTablesInAllSchemas() { + withDb { testDb -> + if (currentDialectTest.supportsCreateSchema) { + val one = prepareSchemaForTest("one") + + try { + SchemaUtils.createSchema(one) + // table "one.one" is created in new schema by db because of name + // even though current schema has not been set to the new one above + SchemaUtils.create(OneOneTable) + + // so new table will not appear in list of tables in current schema + assertFalse(SchemaUtils.listTables().any { it.equals(OneOneTable.tableName, ignoreCase = true) }) + // but new table appears in list of tables from all schema + assertTrue(SchemaUtils.listTablesInAllSchemas().any { it.equals(OneOneTable.tableName, ignoreCase = true) }) + assertTrue(OneOneTable.exists()) + } finally { + SchemaUtils.drop(OneOneTable) + val cascade = testDb != TestDB.SQLSERVER + SchemaUtils.dropSchema(one, cascade = cascade) + } + } + } + } + @Test fun `create table with quoted name with camel case`() { val testTable = object : IntIdTable("quotedTable") { @@ -667,23 +714,4 @@ class CreateTableTests : DatabaseTestsBase() { } } } - - @Test - fun testListTablesDoesNotCloseDatabaseConnection() { - val tester = object : IntIdTable("tester") { - val int = integer("intColumn") - } - withDb { testDb -> - val defaultSchemaName = when (currentDialectTest) { - is SQLServerDialect -> "dbo" - is OracleDialect -> testDb.user - is MysqlDialect -> testDb.db!!.name - else -> "public" - } - assertTrue(SchemaUtils.listTables().none { it.equals("$defaultSchemaName.${OneTable.tableName}", ignoreCase = true) }) - } - withDb { - assertFalse(tester.exists()) - } - } } From 6c1d3fe15b6f47c60461904b21f2c8f358eb939f Mon Sep 17 00:00:00 2001 From: Chantal Loncle <82039410+bog-walk@users.noreply.github.com> Date: Thu, 9 Jan 2025 07:27:54 -0500 Subject: [PATCH 3/5] fix: EXPOSED-662 SchemaUtils.listTables() returns empty list & closes db connection - Fix KDocs for new SchemaUtils method - Override CachableMapWithDefault properties to throw unsupported exception & prevent future similar issues --- .../main/kotlin/org/jetbrains/exposed/sql/SchemaUtils.kt | 5 +++-- .../exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SchemaUtils.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SchemaUtils.kt index 705c1c3dff..198c385770 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SchemaUtils.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SchemaUtils.kt @@ -864,8 +864,9 @@ object SchemaUtils { fun listTables(): List<String> = currentDialect.allTablesNames() /** - * Retrieves a list of all table names in the current database schema. - * The names will be returned with schema prefixes if the database supports it. + * Returns a list with the names of all the tables in all database schemas. + * The names will be returned with schema prefixes, if the database supports it, and non-user defined tables, + * like system information table names, will be included. * * @return A list of table names as strings. */ diff --git a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt index e8d8a7d34b..35dc062daf 100644 --- a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt +++ b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt @@ -87,6 +87,12 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData) override fun get(key: K): V? = map.getOrPut(key) { default(key) } override fun containsKey(key: K): Boolean = true override fun isEmpty(): Boolean = false + + override val entries: Set<Map.Entry<K, V>> = throw UnsupportedOperationException( + "Iteration is impossible because CachableMapWithDefault is filled in lazily" + ) + override val keys: Set<K> = throw UnsupportedOperationException() + override val values: Collection<V> = throw UnsupportedOperationException() } override val tableNames: Map<String, List<String>> From d51c95cc7b60562a5de8cecb2a2170e496faed62 Mon Sep 17 00:00:00 2001 From: Chantal Loncle <82039410+bog-walk@users.noreply.github.com> Date: Thu, 9 Jan 2025 07:52:09 -0500 Subject: [PATCH 4/5] fix: EXPOSED-662 SchemaUtils.listTables() returns empty list & closes db connection - Undo changes to CachableMapWithDefault --- .../exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt index 35dc062daf..e8d8a7d34b 100644 --- a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt +++ b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt @@ -87,12 +87,6 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData) override fun get(key: K): V? = map.getOrPut(key) { default(key) } override fun containsKey(key: K): Boolean = true override fun isEmpty(): Boolean = false - - override val entries: Set<Map.Entry<K, V>> = throw UnsupportedOperationException( - "Iteration is impossible because CachableMapWithDefault is filled in lazily" - ) - override val keys: Set<K> = throw UnsupportedOperationException() - override val values: Collection<V> = throw UnsupportedOperationException() } override val tableNames: Map<String, List<String>> From 0e1c754e0fc5a852103570c9e1cd385c5132558e Mon Sep 17 00:00:00 2001 From: Oleg Babichev <oleg.babichev@jetbrains.com> Date: Thu, 9 Jan 2025 14:38:26 +0100 Subject: [PATCH 5/5] Disallow `entries` and `keys` fields of CachableMapWithDefault --- .../sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt index e8d8a7d34b..c0ff258eae 100644 --- a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt +++ b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt @@ -87,6 +87,18 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData) override fun get(key: K): V? = map.getOrPut(key) { default(key) } override fun containsKey(key: K): Boolean = true override fun isEmpty(): Boolean = false + + override val entries: Set<Map.Entry<K, V>> + get() = throw UnsupportedOperationException( + "The entries field should not be used on CachableMapWithDefault because the lazy population of the collection for missing keys " + + "and entries may lead to inconsistencies between calls." + ) + + override val keys: Set<K> + get() = throw UnsupportedOperationException( + "The keys field should not be used on CachableMapWithDefault because the lazy population of the collection for missing keys " + + "and keys may lead to inconsistencies between calls." + ) } override val tableNames: Map<String, List<String>>