0
# Query Execution
1
2
Core query execution functionality providing type-safe database operations with result mapping and change notification capabilities.
3
4
## Capabilities
5
6
### Query Factory Functions
7
8
Factory functions for creating Query and ExecutableQuery instances with various parameter combinations.
9
10
```kotlin { .api }
11
/**
12
* Creates a listenable query that can notify observers of result set changes
13
* @param identifier Unique identifier for prepared statement caching
14
* @param queryKeys Array of table/view names this query depends on
15
* @param driver SqlDriver instance for executing the query
16
* @param query SQL query string
17
* @param mapper Function to convert SqlCursor rows to RowType instances
18
*/
19
fun <RowType : Any> Query(
20
identifier: Int,
21
queryKeys: Array<out String>,
22
driver: SqlDriver,
23
query: String,
24
mapper: (SqlCursor) -> RowType
25
): Query<RowType>
26
27
/**
28
* Creates a listenable query with file and label information for debugging
29
*/
30
fun <RowType : Any> Query(
31
identifier: Int,
32
queryKeys: Array<out String>,
33
driver: SqlDriver,
34
fileName: String,
35
label: String,
36
query: String,
37
mapper: (SqlCursor) -> RowType
38
): Query<RowType>
39
40
/**
41
* Creates an executable query without change listening capability
42
* @param identifier Unique identifier for prepared statement caching
43
* @param driver SqlDriver instance for executing the query
44
* @param query SQL query string
45
* @param mapper Function to convert SqlCursor rows to RowType instances
46
*/
47
fun <RowType : Any> Query(
48
identifier: Int,
49
driver: SqlDriver,
50
query: String,
51
mapper: (SqlCursor) -> RowType
52
): ExecutableQuery<RowType>
53
54
/**
55
* Creates an executable query with file and label information for debugging
56
*/
57
fun <RowType : Any> Query(
58
identifier: Int,
59
driver: SqlDriver,
60
fileName: String,
61
label: String,
62
query: String,
63
mapper: (SqlCursor) -> RowType
64
): ExecutableQuery<RowType>
65
```
66
67
### ExecutableQuery Class
68
69
Base class for executing SQL queries and mapping results to typed objects.
70
71
```kotlin { .api }
72
/**
73
* Base class for executable queries that can map SQL result cursors to typed objects
74
* @param RowType The type that query results will be mapped to
75
* @param mapper Function to convert SqlCursor rows to RowType instances
76
*/
77
abstract class ExecutableQuery<out RowType : Any>(
78
val mapper: (SqlCursor) -> RowType
79
) {
80
/**
81
* Execute the underlying statement with custom result mapping
82
* @param mapper Function to process the SqlCursor and return a QueryResult
83
* @return QueryResult containing the processed data
84
*/
85
abstract fun <R> execute(mapper: (SqlCursor) -> QueryResult<R>): QueryResult<R>
86
87
/**
88
* Execute the query and return all results as a list
89
* @return List of all rows mapped to RowType instances
90
*/
91
fun executeAsList(): List<RowType>
92
93
/**
94
* Execute the query and return exactly one result
95
* @return Single RowType instance
96
* @throws NullPointerException if the result set is empty
97
* @throws IllegalStateException if the result set has multiple rows
98
*/
99
fun executeAsOne(): RowType
100
101
/**
102
* Execute the query and return at most one result
103
* @return Single RowType instance or null if result set is empty
104
* @throws IllegalStateException if the result set has multiple rows
105
*/
106
fun executeAsOneOrNull(): RowType?
107
}
108
```
109
110
### Query Class
111
112
Extended query class that supports change listening for reactive database operations.
113
114
```kotlin { .api }
115
/**
116
* A listenable, typed query that can notify observers when the underlying data changes
117
* @param RowType The type that query results will be mapped to
118
* @param mapper Function to convert SqlCursor rows to RowType instances
119
*/
120
abstract class Query<out RowType : Any>(
121
mapper: (SqlCursor) -> RowType
122
) : ExecutableQuery<RowType>(mapper) {
123
/**
124
* Register a listener to be notified of future changes in the result set
125
* @param listener Listener instance to register
126
*/
127
abstract fun addListener(listener: Listener)
128
129
/**
130
* Remove a listener to no longer be notified of future changes in the result set
131
* @param listener Listener instance to remove
132
*/
133
abstract fun removeListener(listener: Listener)
134
135
/**
136
* Functional interface for listening to query result changes
137
*/
138
fun interface Listener {
139
/**
140
* Called whenever the query this listener was attached to is dirtied.
141
* Calls are made synchronously on the thread where the update occurred,
142
* after the update applied successfully.
143
*/
144
fun queryResultsChanged()
145
}
146
}
147
```
148
149
**Usage Examples:**
150
151
```kotlin
152
import app.cash.sqldelight.Query
153
import app.cash.sqldelight.db.SqlDriver
154
155
// Create a basic executable query
156
val userQuery = Query(
157
identifier = 1,
158
driver = sqlDriver,
159
query = "SELECT * FROM users WHERE active = 1",
160
mapper = { cursor ->
161
User(
162
id = cursor.getLong(0)!!,
163
name = cursor.getString(1)!!,
164
email = cursor.getString(2)!!
165
)
166
}
167
)
168
169
// Execute and get results
170
val allUsers: List<User> = userQuery.executeAsList()
171
val firstUser: User? = userQuery.executeAsOneOrNull()
172
173
// Create a listenable query
174
val activeUsersQuery = Query(
175
identifier = 2,
176
queryKeys = arrayOf("users"),
177
driver = sqlDriver,
178
query = "SELECT * FROM users WHERE active = 1",
179
mapper = { cursor ->
180
User(
181
id = cursor.getLong(0)!!,
182
name = cursor.getString(1)!!,
183
email = cursor.getString(2)!!
184
)
185
}
186
)
187
188
// Listen for changes
189
val listener = Query.Listener {
190
println("User data has changed!")
191
refreshUI()
192
}
193
activeUsersQuery.addListener(listener)
194
195
// Later, remove the listener
196
activeUsersQuery.removeListener(listener)
197
198
// Custom result processing
199
val userCount: Int = userQuery.execute { cursor ->
200
var count = 0
201
while (cursor.next().value) {
202
count++
203
}
204
QueryResult.Value(count)
205
}.value
206
```
207
208
### Thread Safety and Lifecycle
209
210
- **Query instances** are thread-safe for execution but listener management should be done from the same thread
211
- **Listeners** are called synchronously on the thread where the database update occurred
212
- **Cursor access** is confined to the execution block and must not escape the lambda scope
213
- **Query caching** is handled automatically by the SqlDriver implementation using the provided identifier