CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-app-cash-sqldelight--runtime-iossimulatorarm64

SQLDelight multiplatform runtime library providing Kotlin APIs for type-safe database operations with compile-time SQL verification

Pending
Overview
Eval results
Files

query-execution.mddocs/

Query Execution

Core query execution functionality providing type-safe database operations with result mapping and change notification capabilities.

Capabilities

Query Factory Functions

Factory functions for creating Query and ExecutableQuery instances with various parameter combinations.

/**
 * Creates a listenable query that can notify observers of result set changes
 * @param identifier Unique identifier for prepared statement caching
 * @param queryKeys Array of table/view names this query depends on
 * @param driver SqlDriver instance for executing the query
 * @param query SQL query string
 * @param mapper Function to convert SqlCursor rows to RowType instances
 */
fun <RowType : Any> Query(
    identifier: Int,
    queryKeys: Array<out String>,
    driver: SqlDriver,
    query: String,
    mapper: (SqlCursor) -> RowType
): Query<RowType>

/**
 * Creates a listenable query with file and label information for debugging
 */
fun <RowType : Any> Query(
    identifier: Int,
    queryKeys: Array<out String>,
    driver: SqlDriver,
    fileName: String,
    label: String,
    query: String,
    mapper: (SqlCursor) -> RowType
): Query<RowType>

/**
 * Creates an executable query without change listening capability
 * @param identifier Unique identifier for prepared statement caching
 * @param driver SqlDriver instance for executing the query
 * @param query SQL query string
 * @param mapper Function to convert SqlCursor rows to RowType instances
 */
fun <RowType : Any> Query(
    identifier: Int,
    driver: SqlDriver,
    query: String,
    mapper: (SqlCursor) -> RowType
): ExecutableQuery<RowType>

/**
 * Creates an executable query with file and label information for debugging
 */
fun <RowType : Any> Query(
    identifier: Int,
    driver: SqlDriver,
    fileName: String,
    label: String,
    query: String,
    mapper: (SqlCursor) -> RowType
): ExecutableQuery<RowType>

ExecutableQuery Class

Base class for executing SQL queries and mapping results to typed objects.

/**
 * Base class for executable queries that can map SQL result cursors to typed objects
 * @param RowType The type that query results will be mapped to
 * @param mapper Function to convert SqlCursor rows to RowType instances
 */
abstract class ExecutableQuery<out RowType : Any>(
    val mapper: (SqlCursor) -> RowType
) {
    /**
     * Execute the underlying statement with custom result mapping
     * @param mapper Function to process the SqlCursor and return a QueryResult
     * @return QueryResult containing the processed data
     */
    abstract fun <R> execute(mapper: (SqlCursor) -> QueryResult<R>): QueryResult<R>
    
    /**
     * Execute the query and return all results as a list
     * @return List of all rows mapped to RowType instances
     */
    fun executeAsList(): List<RowType>
    
    /**
     * Execute the query and return exactly one result
     * @return Single RowType instance
     * @throws NullPointerException if the result set is empty
     * @throws IllegalStateException if the result set has multiple rows
     */
    fun executeAsOne(): RowType
    
    /**
     * Execute the query and return at most one result
     * @return Single RowType instance or null if result set is empty
     * @throws IllegalStateException if the result set has multiple rows
     */
    fun executeAsOneOrNull(): RowType?
}

Query Class

Extended query class that supports change listening for reactive database operations.

/**
 * A listenable, typed query that can notify observers when the underlying data changes
 * @param RowType The type that query results will be mapped to
 * @param mapper Function to convert SqlCursor rows to RowType instances
 */
abstract class Query<out RowType : Any>(
    mapper: (SqlCursor) -> RowType
) : ExecutableQuery<RowType>(mapper) {
    /**
     * Register a listener to be notified of future changes in the result set
     * @param listener Listener instance to register
     */
    abstract fun addListener(listener: Listener)
    
    /**
     * Remove a listener to no longer be notified of future changes in the result set
     * @param listener Listener instance to remove
     */
    abstract fun removeListener(listener: Listener)
    
    /**
     * Functional interface for listening to query result changes
     */
    fun interface Listener {
        /**
         * Called whenever the query this listener was attached to is dirtied.
         * Calls are made synchronously on the thread where the update occurred,
         * after the update applied successfully.
         */
        fun queryResultsChanged()
    }
}

Usage Examples:

import app.cash.sqldelight.Query
import app.cash.sqldelight.db.SqlDriver

// Create a basic executable query
val userQuery = Query(
    identifier = 1,
    driver = sqlDriver,
    query = "SELECT * FROM users WHERE active = 1",
    mapper = { cursor -> 
        User(
            id = cursor.getLong(0)!!,
            name = cursor.getString(1)!!,
            email = cursor.getString(2)!!
        )
    }
)

// Execute and get results
val allUsers: List<User> = userQuery.executeAsList()
val firstUser: User? = userQuery.executeAsOneOrNull()

// Create a listenable query
val activeUsersQuery = Query(
    identifier = 2,
    queryKeys = arrayOf("users"),
    driver = sqlDriver,
    query = "SELECT * FROM users WHERE active = 1",
    mapper = { cursor -> 
        User(
            id = cursor.getLong(0)!!,
            name = cursor.getString(1)!!,
            email = cursor.getString(2)!!
        )
    }
)

// Listen for changes
val listener = Query.Listener { 
    println("User data has changed!")
    refreshUI()
}
activeUsersQuery.addListener(listener)

// Later, remove the listener
activeUsersQuery.removeListener(listener)

// Custom result processing
val userCount: Int = userQuery.execute { cursor ->
    var count = 0
    while (cursor.next().value) {
        count++
    }
    QueryResult.Value(count)
}.value

Thread Safety and Lifecycle

  • Query instances are thread-safe for execution but listener management should be done from the same thread
  • Listeners are called synchronously on the thread where the database update occurred
  • Cursor access is confined to the execution block and must not escape the lambda scope
  • Query caching is handled automatically by the SqlDriver implementation using the provided identifier

Install with Tessl CLI

npx tessl i tessl/maven-app-cash-sqldelight--runtime-iossimulatorarm64

docs

column-adapters.md

database-driver.md

index.md

logging-debugging.md

query-execution.md

schema-management.md

transaction-management.md

tile.json