or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

column-adapters.mddatabase-driver.mdindex.mdlogging-debugging.mdquery-execution.mdschema-management.mdtransaction-management.md

query-execution.mddocs/

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