Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Add missing KDocs for exposed-core database API #1945

Merged
merged 2 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
package org.jetbrains.exposed.sql

/**
* Represents differences between a column definition and database metadata for the existing column.
*/
data class ColumnDiff(
/** Whether the nullability of the existing column is correct. */
val nullability: Boolean,
/** Whether the existing column has a matching auto-increment sequence. */
val autoInc: Boolean,
/** Whether the default value of the existing column is correct. */
val defaults: Boolean,
/** Whether the existing column identifier matches and has the correct casing. */
val caseSensitiveName: Boolean,
) {

/** Returns `true` if there is a difference between the column definition and the existing column in the database. */
fun hasDifferences() = this != NoneChanged

companion object {
/** A [ColumnDiff] with no differences. */
val NoneChanged = ColumnDiff(
nullability = false,
autoInc = false,
defaults = false,
caseSensitiveName = false,
)

/** A [ColumnDiff] with differences for every matched property. */
val AllChanged = ColumnDiff(
nullability = true,
autoInc = true,
Expand Down
72 changes: 70 additions & 2 deletions exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@ import java.util.concurrent.ConcurrentHashMap
import javax.sql.ConnectionPoolDataSource
import javax.sql.DataSource

/**
* Class representing the underlying database to which connections are made and on which transaction tasks are performed.
*/
class Database private constructor(
private val resolvedVendor: String? = null,
val config: DatabaseConfig,
val connector: () -> ExposedConnection<*>
) {

/** Whether nested transaction blocks are configured to act like top-level transactions. */
var useNestedTransactions: Boolean = config.useNestedTransactions
@Deprecated("Use DatabaseConfig to define the useNestedTransactions")
@TestOnly
Expand All @@ -42,26 +45,37 @@ class Database private constructor(
}
}

/** The connection URL for the database. */
val url: String by lazy { metadata { url } }

/** The name of the database based on the name of the underlying JDBC driver. */
val vendor: String by lazy {
resolvedVendor ?: metadata { databaseDialectName }
}

/** The name of the database as a [DatabaseDialect]. */
val dialect by lazy {
config.explicitDialect ?: dialects[vendor.lowercase()]?.invoke() ?: error("No dialect registered for $name. URL=$url")
}

/** The version number of the database as a [BigDecimal]. */
val version by lazy { metadata { version } }

/** Whether the version number of the database is equal to or greater than the provided [version]. */
fun isVersionCovers(version: BigDecimal) = this.version >= version

/** Whether the database supports ALTER TABLE with an add column clause. */
val supportsAlterTableWithAddColumn by lazy(
LazyThreadSafetyMode.NONE
) { metadata { supportsAlterTableWithAddColumn } }

/** Whether the database supports getting multiple result sets from a single execute. */
val supportsMultipleResultSets by lazy(LazyThreadSafetyMode.NONE) { metadata { supportsMultipleResultSets } }

/** The database-specific class responsible for parsing and processing identifier tokens in SQL syntax. */
val identifierManager by lazy { metadata { identifierManager } }

/** The default number of results that should be fetched when queries are executed. */
var defaultFetchSize: Int? = config.defaultFetchSize
private set

Expand Down Expand Up @@ -111,10 +125,12 @@ class Database private constructor(
registerDialect(MariaDBDialect.dialectName) { MariaDBDialect() }
}

/** Registers a new [DatabaseDialect] with the identifier [prefix]. */
fun registerDialect(prefix: String, dialect: () -> DatabaseDialect) {
dialects[prefix.lowercase()] = dialect
}

/** Registers a new JDBC driver, using the specified [driverClassName], with the identifier [prefix]. */
fun registerJdbcDriver(prefix: String, driverClassName: String, dialect: String) {
driverMapping[prefix] = driverClassName
dialectMapping[prefix] = dialect
Expand All @@ -134,6 +150,17 @@ class Database private constructor(
}
}

/**
* Creates a [Database] instance.
*
* **Note:** This function does not immediately instantiate an actual connection to a database,
* but instead provides the details necessary to do so whenever a connection is required by a transaction.
*
* @param datasource The [DataSource] object to be used as a means of getting a connection.
* @param setupConnection Any setup that should be applied to each new connection.
* @param databaseConfig Configuration parameters for this [Database] instance.
* @param manager The [TransactionManager] responsible for new transactions that use this [Database] instance.
*/
fun connect(
datasource: DataSource,
setupConnection: (Connection) -> Unit = {},
Expand All @@ -149,6 +176,17 @@ class Database private constructor(
)
}

/**
* Creates a [Database] instance.
*
* **Note:** This function does not immediately instantiate an actual connection to a database,
* but instead provides the details necessary to do so whenever a connection is required by a transaction.
*
* @param datasource The [ConnectionPoolDataSource] object to be used as a means of getting a pooled connection.
* @param setupConnection Any setup that should be applied to each new connection.
* @param databaseConfig Configuration parameters for this [Database] instance.
* @param manager The [TransactionManager] responsible for new transactions that use this [Database] instance.
*/
fun connectPool(
datasource: ConnectionPoolDataSource,
setupConnection: (Connection) -> Unit = {},
Expand All @@ -164,6 +202,16 @@ class Database private constructor(
)
}

/**
* Creates a [Database] instance.
*
* **Note:** This function does not immediately instantiate an actual connection to a database,
* but instead provides the details necessary to do so whenever a connection is required by a transaction.
*
* @param getNewConnection A function that returns a new connection.
* @param databaseConfig Configuration parameters for this [Database] instance.
* @param manager The [TransactionManager] responsible for new transactions that use this [Database] instance.
*/
fun connect(
getNewConnection: () -> Connection,
databaseConfig: DatabaseConfig? = null,
Expand All @@ -177,6 +225,21 @@ class Database private constructor(
)
}

/**
* Creates a [Database] instance.
*
* **Note:** This function does not immediately instantiate an actual connection to a database,
* but instead provides the details necessary to do so whenever a connection is required by a transaction.
*
* @param url The URL that represents the database when getting a connection.
* @param driver The JDBC driver class. If not provided, the specified [url] will be used to find
* a match from the existing driver mappings.
* @param user The database user that owns the new connections.
* @param password The password specific for the database [user].
* @param setupConnection Any setup that should be applied to each new connection.
* @param databaseConfig Configuration parameters for this [Database] instance.
* @param manager The [TransactionManager] responsible for new transactions that use this [Database] instance.
*/
fun connect(
url: String,
driver: String = getDriver(url),
Expand All @@ -199,6 +262,7 @@ class Database private constructor(
)
}

/** Returns the stored default transaction isolation level for a specific database. */
fun getDefaultIsolationLevel(db: Database): Int =
when (db.dialect) {
is SQLiteDialect -> Connection.TRANSACTION_SERIALIZABLE
Expand All @@ -210,12 +274,16 @@ class Database private constructor(
url.startsWith(prefix)
}?.value ?: error("Database driver not found for $url")

/** Returns the database name used internally for the provided connection [url]. */
fun getDialectName(url: String) = dialectMapping.entries.firstOrNull { (prefix, _) ->
url.startsWith(prefix)
}?.value
}
}

/** Represents an [ExposedConnection] that is loaded whenever a connection is accessed by a [Database] instance. */
interface DatabaseConnectionAutoRegistration : (Connection) -> ExposedConnection<*>

val Database.name: String get() = url.substringBefore('?').substringAfterLast('/')
/** Returns the name of the database obtained from its connection URL. */
val Database.name: String
get() = url.substringBefore('?').substringAfterLast('/')
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ package org.jetbrains.exposed.sql

import org.jetbrains.exposed.sql.vendors.DatabaseDialect

/**
* A configuration class for a [Database].
*
* Parameters set in this class apply to all transactions that use the [Database] instance,
* unless an applicable override is specified in an individual transaction block.
*/
@Suppress("LongParameterList")
class DatabaseConfig private constructor(
val sqlLogger: SqlLogger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import org.jetbrains.exposed.sql.statements.expandArgs
import org.jetbrains.exposed.sql.vendors.DatabaseDialect
import java.sql.SQLException

/**
* An exception that provides information about a database access error,
* within the [contexts] of the executed statements that caused the exception.
*/
class ExposedSQLException(
cause: Throwable?,
val contexts: List<StatementContext>,
Expand Down Expand Up @@ -40,7 +44,10 @@ class ExposedSQLException(
override fun toString() = "${super.toString()}\nSQL: ${causedByQueries()}"
}

@Suppress("MaximumLineLength")
/**
* An exception that provides information about an operation that is not supported by
* the provided [dialect].
*/
class UnsupportedByDialectException(baseMessage: String, val dialect: DatabaseDialect) : UnsupportedOperationException(
baseMessage + ", dialect: ${dialect.name}."
)
Expand All @@ -53,7 +60,6 @@ class UnsupportedByDialectException(baseMessage: String, val dialect: DatabaseDi
*
* @param columnName the duplicated column name
*/
@Suppress("MaximumLineLength")
class DuplicateColumnException(columnName: String, tableName: String) : ExceptionInInitializerError(
"Duplicate column name \"$columnName\" in table \"$tableName\""
)
Expand Down
18 changes: 17 additions & 1 deletion exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SQLLog.kt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package org.jetbrains.exposed.sql

import org.jetbrains.exposed.sql.statements.StatementContext
import org.jetbrains.exposed.sql.statements.StatementInterceptor
import org.jetbrains.exposed.sql.statements.api.PreparedStatementApi
Expand All @@ -7,33 +8,47 @@ import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.slf4j.LoggerFactory
import java.util.*

/** Base class representing a provider of log messages. */
interface SqlLogger {
/** Determines how a log message is routed. */
fun log(context: StatementContext, transaction: Transaction)
}

/** Returns a [org.slf4j.Logger] named specifically for Exposed log messages. */
val exposedLogger = LoggerFactory.getLogger("Exposed")!!

/** Class representing a provider of log messages sent to standard output stream. */
object StdOutSqlLogger : SqlLogger {
/** Prints a log message containing the string representation of a complete SQL statement. */
override fun log(context: StatementContext, transaction: Transaction) {
System.out.println("SQL: ${context.expandArgs(transaction)}")
println("SQL: ${context.expandArgs(transaction)}")
}
}

/** Class representing a provider of log messages at DEBUG level. */
object Slf4jSqlDebugLogger : SqlLogger {
/**
* Logs a message containing the string representation of a complete SQL statement.
*
* **Note:** This is only logged if DEBUG level is currently enabled.
*/
override fun log(context: StatementContext, transaction: Transaction) {
if (exposedLogger.isDebugEnabled) {
exposedLogger.debug(context.expandArgs(TransactionManager.current()))
}
}
}

/** Class representing one or more [SqlLogger]s. */
class CompositeSqlLogger : SqlLogger, StatementInterceptor {
private val loggers: ArrayList<SqlLogger> = ArrayList(2)

/** Adds an [SqlLogger] instance. */
fun addLogger(logger: SqlLogger) {
loggers.add(logger)
}

/** Removes an [SqlLogger] instance. */
fun removeLogger(logger: SqlLogger) {
loggers.remove(logger)
}
Expand All @@ -51,6 +66,7 @@ class CompositeSqlLogger : SqlLogger, StatementInterceptor {
}
}

/** Adds one or more [SqlLogger]s to [this] transaction. */
fun Transaction.addLogger(vararg logger: SqlLogger): CompositeSqlLogger {
return CompositeSqlLogger().apply {
logger.forEach { this.addLogger(it) }
Expand Down
28 changes: 15 additions & 13 deletions exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Schema.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,15 @@ import org.jetbrains.exposed.sql.vendors.currentDialect
import java.lang.StringBuilder

/**
* Database Schema
*
* @param name the schema name
* @param authorization owner_name Specifies the name of the database-level
* principal that will own the schema.
* @param password used only for oracle schema.
* @param defaultTablespace used only for oracle schema.
* @param temporaryTablespace used only for oracle schema.
* @param quota used only for oracle schema.
* @param on used only for oracle schema.
*
* Represents a database schema.
*
* @param name The schema name.
* @param authorization Specifies the name of the database-level principal that will own the schema.
* @param password Used only for Oracle schema.
* @param defaultTablespace Used only for Oracle schema.
* @param temporaryTablespace Used only for Oracle schema.
* @param quota Used only for Oracle schema.
* @param on Used only for Oracle schema.
*/
data class Schema(
private val name: String,
Expand All @@ -28,9 +25,11 @@ data class Schema(
val quota: String? = null,
val on: String? = null
) {
/** This schema's name in proper database casing. */
val identifier
get() = TransactionManager.current().db.identifierManager.cutIfNecessaryAndQuote(name)

val identifier get() = TransactionManager.current().db.identifierManager.cutIfNecessaryAndQuote(name)

/** The SQL statements that create this schema. */
val ddl: List<String>
get() = createStatement()

Expand All @@ -39,6 +38,7 @@ data class Schema(
*/
fun exists(): Boolean = currentDialect.schemaExists(this)

/** Returns the SQL statements that create this schema. */
fun createStatement(): List<String> {
if (!currentDialect.supportsCreateSchema) {
throw UnsupportedByDialectException("The current dialect doesn't support create schema statement", currentDialect)
Expand All @@ -47,6 +47,7 @@ data class Schema(
return listOf(currentDialect.createSchema(this))
}

/** Returns the SQL statements that drop this schema, as well as all its objects if [cascade] is `true`. */
fun dropStatement(cascade: Boolean): List<String> {
if (!currentDialect.supportsCreateSchema) {
throw UnsupportedByDialectException("The current dialect doesn't support drop schema statement", currentDialect)
Expand All @@ -55,6 +56,7 @@ data class Schema(
return listOf(currentDialect.dropSchema(this, cascade))
}

/** Returns the SQL statements that set this schema as the current schema. */
fun setSchemaStatement(): List<String> {
if (!currentDialect.supportsCreateSchema) {
throw UnsupportedByDialectException("The current dialect doesn't support schemas", currentDialect)
Expand Down
Loading
Loading