Skip to content

Commit

Permalink
deprecate: Raise deprecation level of currentScheme property (#1874)
Browse files Browse the repository at this point in the history
* deprecate: Raise deprecation level of currentScheme property

Raise deprecation level of ExposedDatabaseMetadata.currentScheme from WARNING to
ERROR.

This property was initially deprecated after PR 815 introduced the possibility to change the 
schema in runtime.

The goal of this fix is to keep the property private in JdbcDatabaseMetadataImpl, but it has 
been used in VendorDialect` and 1 unit test.

A new abstract fun tableNamesByCurrentSchema() is introduced to abstract away
the schema from the core module. Ideally this function would return a List<String>
but tableExists() needs to know about the current scheme name. So, instead, a new data 
class SchemaMetadata is introduced to store both the schema name and its list of table names.
  • Loading branch information
bog-walk authored Oct 5, 2023
1 parent b579d93 commit 0eb0ac1
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 42 deletions.
17 changes: 16 additions & 1 deletion exposed-core/api/exposed-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -2884,6 +2884,7 @@ public abstract class org/jetbrains/exposed/sql/statements/api/ExposedDatabaseMe
public abstract fun getVersion ()Ljava/math/BigDecimal;
public abstract fun resetCurrentScheme ()V
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 class org/jetbrains/exposed/sql/statements/api/ExposedSavepoint {
Expand Down Expand Up @@ -3418,8 +3419,8 @@ public class org/jetbrains/exposed/sql/vendors/MysqlDialect : org/jetbrains/expo
public fun isAllowedAsColumnDefault (Lorg/jetbrains/exposed/sql/Expression;)Z
public final fun isFractionDateTimeSupported ()Z
public final fun isTimeZoneOffsetSupported ()Z
protected fun metadataMatchesTable (Ljava/lang/String;Ljava/lang/String;Lorg/jetbrains/exposed/sql/Table;)Z
public fun setSchema (Lorg/jetbrains/exposed/sql/Schema;)Ljava/lang/String;
public fun tableExists (Lorg/jetbrains/exposed/sql/Table;)Z
}

public final class org/jetbrains/exposed/sql/vendors/MysqlDialect$Companion : org/jetbrains/exposed/sql/vendors/VendorDialect$DialectNameProvider {
Expand Down Expand Up @@ -3543,6 +3544,19 @@ public final class org/jetbrains/exposed/sql/vendors/SQLiteDialect$Companion : o
public final fun getENABLE_UPDATE_DELETE_LIMIT ()Z
}

public final class org/jetbrains/exposed/sql/vendors/SchemaMetadata {
public fun <init> (Ljava/lang/String;Ljava/util/List;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/util/List;
public final fun copy (Ljava/lang/String;Ljava/util/List;)Lorg/jetbrains/exposed/sql/vendors/SchemaMetadata;
public static synthetic fun copy$default (Lorg/jetbrains/exposed/sql/vendors/SchemaMetadata;Ljava/lang/String;Ljava/util/List;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/vendors/SchemaMetadata;
public fun equals (Ljava/lang/Object;)Z
public final fun getSchemaName ()Ljava/lang/String;
public final fun getTableNames ()Ljava/util/List;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public abstract class org/jetbrains/exposed/sql/vendors/VendorDialect : org/jetbrains/exposed/sql/vendors/DatabaseDialect {
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;
Expand Down Expand Up @@ -3590,6 +3604,7 @@ public abstract class org/jetbrains/exposed/sql/vendors/VendorDialect : org/jetb
public fun getSupportsWindowFrameGroupsMode ()Z
public fun isAllowedAsColumnDefault (Lorg/jetbrains/exposed/sql/Expression;)Z
public fun listDatabases ()Ljava/lang/String;
protected fun metadataMatchesTable (Ljava/lang/String;Ljava/lang/String;Lorg/jetbrains/exposed/sql/Table;)Z
public fun modifyColumn (Lorg/jetbrains/exposed/sql/Column;Lorg/jetbrains/exposed/sql/ColumnDiff;)Ljava/util/List;
protected final fun quoteIdentifierWhenWrongCaseOrNecessary (Ljava/lang/String;Lorg/jetbrains/exposed/sql/Transaction;)Ljava/lang/String;
public fun resetCaches ()V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.jetbrains.exposed.sql.Index
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.vendors.ColumnMetadata
import org.jetbrains.exposed.sql.vendors.PrimaryKeyMetadata
import org.jetbrains.exposed.sql.vendors.SchemaMetadata
import java.math.BigDecimal

abstract class ExposedDatabaseMetadata(val database: String) {
Expand All @@ -23,13 +24,15 @@ abstract class ExposedDatabaseMetadata(val database: String) {

@Deprecated(
message = "it's temporary solution which will be replaced in a future releases. Do not use it in your code",
level = DeprecationLevel.WARNING
level = DeprecationLevel.ERROR
)
abstract val currentScheme: String
abstract fun resetCurrentScheme()
abstract val tableNames: Map<String, List<String>>
abstract val schemaNames: List<String>

abstract fun tableNamesByCurrentSchema(tableNamesCache: Map<String, List<String>>?): SchemaMetadata

abstract fun columns(vararg tables: Table): Map<Table, List<ColumnMetadata>>

abstract fun existingIndices(vararg tables: Table): Map<Table, List<Index>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,20 +384,13 @@ open class MysqlDialect : VendorDialect(dialectName, MysqlDataTypeProvider, Mysq

override fun dropSchema(schema: Schema, cascade: Boolean): String = "DROP SCHEMA IF EXISTS ${schema.identifier}"

override fun tableExists(table: Table): Boolean {
val tableScheme = table.schemaName
val scheme = tableScheme?.inProperCase() ?: TransactionManager.current().connection.metadata { currentScheme }
val allTables = getAllTableNamesCache().getValue(scheme)

return allTables.any {
when {
tableScheme != null -> it == table.nameInDatabaseCase()
scheme.isEmpty() -> it == table.nameInDatabaseCaseUnquoted()
else -> {
val sanitizedTableName = table.tableNameWithoutScheme
val nameInDb = "$scheme.$sanitizedTableName".inProperCase()
it == nameInDb
}
override fun String.metadataMatchesTable(schema: String, table: Table): Boolean {
return when {
schema.isEmpty() -> this == table.nameInDatabaseCaseUnquoted()
else -> {
val sanitizedTableName = table.tableNameWithoutScheme
val nameInDb = "$schema.$sanitizedTableName".inProperCase()
this == nameInDb
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.jetbrains.exposed.sql.vendors

/**
* Represents metadata information about the current schema and its associated tables.
*/
data class SchemaMetadata(
/** Name of the current schema. */
val schemaName: String,
/** Names of the existing tables in the current schema. */
val tableNames: List<String>
)
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ abstract class VendorDialect(
val allTablesNames: List<String>
get() {
val connection = TransactionManager.current().connection
return getAllTableNamesCache().getValue(connection.metadata { currentScheme })
return connection.metadata { tableNamesByCurrentSchema(getAllTableNamesCache()).tableNames }
}

protected fun getAllTableNamesCache(): Map<String, List<String>> {
Expand Down Expand Up @@ -57,22 +57,31 @@ abstract class VendorDialect(
* Using `allTablesNames` field is the preferred way.
*/
override fun allTablesNames(): List<String> = TransactionManager.current().connection.metadata {
tableNames.getValue(currentScheme)
tableNamesByCurrentSchema(null).tableNames
}

override fun tableExists(table: Table): Boolean {
val tableScheme = table.schemaName
val scheme = tableScheme?.inProperCase() ?: TransactionManager.current().connection.metadata { currentScheme }
val allTables = getAllTableNamesCache().getValue(scheme)
return allTables.any {
when {
tableScheme != null -> it == table.nameInDatabaseCase()
scheme.isEmpty() -> it == table.nameInDatabaseCaseUnquoted()
else -> {
val sanitizedTableName = table.tableNameWithoutSchemeSanitized
val nameInDb = "$scheme.$sanitizedTableName".inProperCase()
it == nameInDb
}
return table.schemaName?.let { schema ->
getAllTableNamesCache().getValue(schema.inProperCase()).any {
it == table.nameInDatabaseCase()
}
} ?: run {
val (schema, allTables) = TransactionManager.current().connection.metadata {
tableNamesByCurrentSchema(getAllTableNamesCache())
}
allTables.any {
it.metadataMatchesTable(schema, table)
}
}
}

protected open fun String.metadataMatchesTable(schema: String, table: Table): Boolean {
return when {
schema.isEmpty() -> this == table.nameInDatabaseCaseUnquoted()
else -> {
val sanitizedTableName = table.tableNameWithoutSchemeSanitized
val nameInDb = "$schema.$sanitizedTableName".inProperCase()
this == nameInDb
}
}
}
Expand Down
1 change: 1 addition & 0 deletions exposed-jdbc/api/exposed-jdbc.api
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public final class org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadat
public fun getVersion ()Ljava/math/BigDecimal;
public fun resetCurrentScheme ()V
public fun tableConstraints (Ljava/util/List;)Ljava/util/Map;
public fun tableNamesByCurrentSchema (Ljava/util/Map;)Lorg/jetbrains/exposed/sql/vendors/SchemaMetadata;
}

public final class org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl$Companion {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData)

private val databaseName
get() = when (databaseDialectName) {
MysqlDialect.dialectName, MariaDBDialect.dialectName -> currentScheme
MysqlDialect.dialectName, MariaDBDialect.dialectName -> currentSchema!!
else -> database
}

Expand All @@ -56,7 +56,7 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData)
}
}

private var _currentScheme: String? = null
private var currentSchema: String? = null
get() {
if (field == null) {
field = try {
Expand All @@ -72,10 +72,14 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData)
return field!!
}

override val currentScheme: String get() = _currentScheme!!
@Deprecated(
message = "This will be removed when the interface property is fully deprecated",
level = DeprecationLevel.ERROR
)
override val currentScheme: String get() = currentSchema!!

override fun resetCurrentScheme() {
_currentScheme = null
currentSchema = null
}

private inner class CachableMapWithDefault<K, V>(
Expand Down Expand Up @@ -125,6 +129,15 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData)
return schemas.map { identifierManager.inProperCase(it) }
}

/**
* Returns the default schema name and a list of its existing table names, as [SchemaMetadata],
* found either by reading metadata or from a cache of previously read metadata.
*/
override fun tableNamesByCurrentSchema(tableNamesCache: Map<String, List<String>>?): SchemaMetadata {
val tablesInSchema = (tableNamesCache ?: tableNames).getValue(currentSchema!!)
return SchemaMetadata(currentSchema!!, tablesInSchema)
}

private fun ResultSet.extractColumns(): List<ColumnMetadata> {
val result = mutableListOf<ColumnMetadata>()
while (next()) {
Expand All @@ -136,11 +149,11 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData)
override fun columns(vararg tables: Table): Map<Table, List<ColumnMetadata>> {
val result = mutableMapOf<Table, List<ColumnMetadata>>()
val useSchemaInsteadOfDatabase = currentDialect is MysqlDialect
val tablesBySchema = tables.groupBy { identifierManager.inProperCase(it.schemaName ?: currentScheme) }
val tablesBySchema = tables.groupBy { identifierManager.inProperCase(it.schemaName ?: currentSchema!!) }

for ((schema, schemaTables) in tablesBySchema.entries) {
for (table in schemaTables) {
val catalog = if (!useSchemaInsteadOfDatabase || schema == currentScheme) databaseName else schema
val catalog = if (!useSchemaInsteadOfDatabase || schema == currentSchema!!) databaseName else schema
val rs = metadata.getColumns(catalog, schema, table.nameInDatabaseCaseUnquoted(), "%")
val columns = rs.extractColumns()
check(columns.isNotEmpty())
Expand Down Expand Up @@ -208,7 +221,7 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData)
rs.close()
names
}
val storedIndexTable = if (tableSchema == currentScheme) table.nameInDatabaseCase() else table.nameInDatabaseCaseUnquoted()
val storedIndexTable = if (tableSchema == currentSchema!!) table.nameInDatabaseCase() else table.nameInDatabaseCaseUnquoted()
val rs = metadata.getIndexInfo(catalog, tableSchema, storedIndexTable, false, false)

val tmpIndices = hashMapOf<Triple<String, Boolean, Op.TRUE?>, MutableList<String>>()
Expand Down Expand Up @@ -312,8 +325,8 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData)
* metadata if 'my_schema' is used as the database name.
*/
private fun tableCatalogAndSchema(table: Table): Pair<String, String> {
val tableSchema = identifierManager.inProperCase(table.schemaName ?: currentScheme)
return if (currentDialect is MysqlDialect && tableSchema != currentScheme) {
val tableSchema = identifierManager.inProperCase(table.schemaName ?: currentSchema!!)
return if (currentDialect is MysqlDialect && tableSchema != currentSchema!!) {
tableSchema to tableSchema
} else {
databaseName to tableSchema
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class SchemaTests : DatabaseTestsBase() {

transaction {
connection.metadata {
assertEquals("PUBLIC", currentScheme)
assertEquals("PUBLIC", tableNamesByCurrentSchema(null).schemaName)
}
}

Expand All @@ -154,13 +154,17 @@ class SchemaTests : DatabaseTestsBase() {

transaction(db) {
connection.metadata {
val currentScheme = db.identifierManager.cutIfNecessaryAndQuote(currentScheme)
val currentScheme = db.identifierManager.cutIfNecessaryAndQuote(
tableNamesByCurrentSchema(null).schemaName
)
assertEquals(schema.identifier, currentScheme)
}
// Nested transaction
transaction(db) {
connection.metadata {
val currentScheme = db.identifierManager.cutIfNecessaryAndQuote(currentScheme)
val currentScheme = db.identifierManager.cutIfNecessaryAndQuote(
tableNamesByCurrentSchema(null).schemaName
)
assertEquals(schema.identifier, currentScheme)
}
}
Expand Down

0 comments on commit 0eb0ac1

Please sign in to comment.