SQLDelight multiplatform runtime library providing typesafe Kotlin APIs from SQL statements with compile-time schema verification
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
SQLDelight Runtime is the multiplatform runtime library for SQLDelight, providing typesafe Kotlin APIs from SQL statements. It serves as the foundational layer that enables compile-time schema verification, reactive query results, and thread-safe database operations across JVM, Android, Native, and JavaScript platforms.
implementation "app.cash.sqldelight:runtime:2.1.0"import app.cash.sqldelight.Query
import app.cash.sqldelight.Transacter
import app.cash.sqldelight.SuspendingTransacter
import app.cash.sqldelight.ColumnAdapter
import app.cash.sqldelight.EnumColumnAdapter
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.db.SqlCursor
import app.cash.sqldelight.db.QueryResult
import app.cash.sqldelight.db.SqlSchema
import app.cash.sqldelight.db.AfterVersion
import app.cash.sqldelight.logs.LogSqliteDriver
import app.cash.sqldelight.logs.StatementParameterInterceptorimport app.cash.sqldelight.Query
import app.cash.sqldelight.Transacter
import app.cash.sqldelight.db.SqlDriver
// Execute a query and get results
val users: List<User> = userQueries.selectAll().executeAsList()
// Execute a single result query
val user: User? = userQueries.selectById(123).executeAsOneOrNull()
// Listen to query result changes
userQueries.selectAll().addListener(object : Query.Listener {
override fun queryResultsChanged() {
// Handle query result updates
updateUI()
}
})
// Execute operations in a transaction
database.transaction {
userQueries.insertUser("Alice", 25)
userQueries.insertUser("Bob", 30)
// Both inserts succeed or both are rolled back
}SQLDelight Runtime is built around several key components:
Core query functionality for executing SQL statements and managing result sets. Provides type-safe query execution with reactive updates and lifecycle management.
abstract class Query<out RowType : Any>(
mapper: (SqlCursor) -> RowType
) : ExecutableQuery<RowType>(mapper) {
abstract fun addListener(listener: Listener)
abstract fun removeListener(listener: Listener)
fun interface Listener {
fun queryResultsChanged()
}
}
abstract class ExecutableQuery<out RowType : Any>(
val mapper: (SqlCursor) -> RowType
) {
abstract fun <R> execute(mapper: (SqlCursor) -> QueryResult<R>): QueryResult<R>
fun executeAsList(): List<RowType>
fun executeAsOne(): RowType
fun executeAsOneOrNull(): RowType?
}
// Factory functions for creating queries
fun <RowType : Any> Query(
identifier: Int,
queryKeys: Array<out String>,
driver: SqlDriver,
query: String,
mapper: (SqlCursor) -> RowType
): Query<RowType>Comprehensive transaction support with ACID guarantees, nested transactions, and automatic resource management. Supports both synchronous and coroutine-based asynchronous operations.
interface Transacter : TransacterBase {
fun <R> transactionWithResult(
noEnclosing: Boolean = false,
bodyWithReturn: TransactionWithReturn<R>.() -> R
): R
fun transaction(
noEnclosing: Boolean = false,
body: TransactionWithoutReturn.() -> Unit
)
}
interface SuspendingTransacter : TransacterBase {
suspend fun <R> transactionWithResult(
noEnclosing: Boolean = false,
bodyWithReturn: suspend SuspendingTransactionWithReturn<R>.() -> R
): R
suspend fun transaction(
noEnclosing: Boolean = false,
body: suspend SuspendingTransactionWithoutReturn.() -> Unit
)
}
abstract class Transacter.Transaction : TransactionCallbacks {
protected abstract val enclosingTransaction: Transaction?
fun afterCommit(function: () -> Unit)
fun afterRollback(function: () -> Unit)
protected abstract fun endTransaction(successful: Boolean): QueryResult<Unit>
}Platform-agnostic database driver abstraction that provides SQL execution, connection management, and query result handling. Supports both synchronous and asynchronous operation modes.
interface SqlDriver : Closeable {
fun <R> executeQuery(
identifier: Int?,
sql: String,
mapper: (SqlCursor) -> QueryResult<R>,
parameters: Int,
binders: (SqlPreparedStatement.() -> Unit)? = null
): QueryResult<R>
fun execute(
identifier: Int?,
sql: String,
parameters: Int,
binders: (SqlPreparedStatement.() -> Unit)? = null
): QueryResult<Long>
fun newTransaction(): QueryResult<Transacter.Transaction>
fun currentTransaction(): Transacter.Transaction?
fun addListener(vararg queryKeys: String, listener: Query.Listener)
fun removeListener(vararg queryKeys: String, listener: Query.Listener)
fun notifyListeners(vararg queryKeys: String)
}
interface SqlCursor {
fun next(): QueryResult<Boolean>
fun getString(index: Int): String?
fun getLong(index: Int): Long?
fun getBytes(index: Int): ByteArray?
fun getDouble(index: Int): Double?
fun getBoolean(index: Int): Boolean?
}
sealed interface QueryResult<T> {
val value: T
suspend fun await(): T
value class Value<T>(override val value: T) : QueryResult<T>
value class AsyncValue<T>(private val getter: suspend () -> T) : QueryResult<T>
}Type conversion system for mapping between Kotlin types and database column types. Enables custom type serialization and provides built-in adapters for common use cases.
interface ColumnAdapter<T : Any, S> {
fun decode(databaseValue: S): T
fun encode(value: T): S
}
class EnumColumnAdapter<T : Enum<T>>(
private val enumValues: Array<out T>
) : ColumnAdapter<T, String> {
override fun decode(databaseValue: String): T
override fun encode(value: T): String
}
// Factory function for enum adapters
inline fun <reified T : Enum<T>> EnumColumnAdapter(): EnumColumnAdapter<T>Comprehensive logging and debugging tools for monitoring database operations, troubleshooting issues, and performance analysis.
class LogSqliteDriver(
private val sqlDriver: SqlDriver,
private val logger: (String) -> Unit
) : SqlDriver {
// Logs all SQL operations including queries, executions, transactions
override fun <R> executeQuery(
identifier: Int?,
sql: String,
mapper: (SqlCursor) -> QueryResult<R>,
parameters: Int,
binders: (SqlPreparedStatement.() -> Unit)?
): QueryResult<R>
override fun execute(
identifier: Int?,
sql: String,
parameters: Int,
binders: (SqlPreparedStatement.() -> Unit)?
): QueryResult<Long>
}
class StatementParameterInterceptor : SqlPreparedStatement {
fun getAndClearParameters(): List<Any?>
// Intercepts and stores parameter values for debugging
}Database schema creation, migration, and version management system. Provides APIs for initializing databases and handling schema changes across application versions.
interface SqlSchema<T : QueryResult<Unit>> {
val version: Long
fun create(driver: SqlDriver): T
fun migrate(
driver: SqlDriver,
oldVersion: Long,
newVersion: Long,
vararg callbacks: AfterVersion
): T
}
class AfterVersion(
val afterVersion: Long,
val block: (SqlDriver) -> Unit
)// Core interfaces
sealed interface TransacterBase {
// Base interface for all transaction-capable components
// Implemented by Transacter and SuspendingTransacter
}
interface TransactionCallbacks {
fun afterCommit(function: () -> Unit)
fun afterRollback(function: () -> Unit)
}
// Transaction context interfaces
interface TransactionWithReturn<R> : TransactionCallbacks {
fun rollback(returnValue: R): Nothing
fun <R> transaction(body: TransactionWithReturn<R>.() -> R): R
}
interface TransactionWithoutReturn : TransactionCallbacks {
fun rollback(): Nothing
fun transaction(body: TransactionWithoutReturn.() -> Unit)
}
interface SuspendingTransactionWithReturn<R> : TransactionCallbacks {
fun rollback(returnValue: R): Nothing
suspend fun <R> transaction(body: suspend SuspendingTransactionWithReturn<R>.() -> R): R
}
interface SuspendingTransactionWithoutReturn : TransactionCallbacks {
fun rollback(): Nothing
suspend fun transactionWithResult(body: suspend SuspendingTransactionWithoutReturn.() -> Unit)
}
// Prepared statement interface
interface SqlPreparedStatement {
fun bindBytes(index: Int, bytes: ByteArray?)
fun bindLong(index: Int, long: Long?)
fun bindDouble(index: Int, double: Double?)
fun bindString(index: Int, string: String?)
fun bindBoolean(index: Int, boolean: Boolean?)
}
// Platform abstraction
expect interface Closeable {
fun close()
}
expect inline fun <T : Closeable?, R> T.use(body: (T) -> R): R
// Exception types
class OptimisticLockException(
message: String?,
cause: Throwable? = null
) : IllegalStateException(message, cause)
// Internal APIs
internal expect fun currentThreadId(): Long