or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.md

index.mddocs/

0

# SQLDelight Android Driver

1

2

SQLDelight Android Driver provides a SQLite database driver implementation specifically designed for Android applications using SQLDelight. It serves as a bridge between SQLDelight's generated typesafe Kotlin APIs and Android's SQLite database system through the AndroidX SQLite library, with features like statement caching, transaction support, and reactive query listeners.

3

4

## Package Information

5

6

- **Package Name**: app.cash.sqldelight:android-driver

7

- **Package Type**: Maven (Android Library)

8

- **Language**: Kotlin

9

- **Installation**: Add to `build.gradle.kts`:

10

```kotlin

11

dependencies {

12

implementation("app.cash.sqldelight:android-driver:2.1.0")

13

api("androidx.sqlite:sqlite:2.3.1")

14

}

15

```

16

17

## Core Imports

18

19

```kotlin

20

import app.cash.sqldelight.driver.android.AndroidSqliteDriver

21

import app.cash.sqldelight.db.SqlSchema

22

import app.cash.sqldelight.db.QueryResult

23

import android.content.Context

24

```

25

26

## Basic Usage

27

28

```kotlin

29

import app.cash.sqldelight.driver.android.AndroidSqliteDriver

30

import app.cash.sqldelight.db.SqlSchema

31

import android.content.Context

32

33

// Create driver with schema and context

34

val driver = AndroidSqliteDriver(

35

schema = MyDatabase.Schema,

36

context = applicationContext,

37

name = "my_database.db"

38

)

39

40

// Use with SQLDelight generated database

41

val database = MyDatabase(driver)

42

43

// Perform database operations

44

val users = database.userQueries.selectAll().executeAsList()

45

46

// Close when done

47

driver.close()

48

```

49

50

## Architecture

51

52

The Android Driver is built around several key components:

53

54

- **SqlDriver Interface**: Implementation of SQLDelight's core driver interface

55

- **Statement Caching**: LRU cache for prepared statements with configurable size

56

- **Transaction Management**: Nested transaction support with thread-local storage

57

- **Query Listeners**: Reactive change notifications for query result updates

58

- **AndroidX Integration**: Native integration with AndroidX SQLite library

59

- **Cursor Management**: Configurable cursor window size for memory optimization

60

61

## Capabilities

62

63

### Driver Creation

64

65

Create AndroidSqliteDriver instances for different use cases.

66

67

```kotlin { .api }

68

/**

69

* Create driver with schema, context, and optional configuration

70

*/

71

class AndroidSqliteDriver(

72

schema: SqlSchema<QueryResult.Value<Unit>>,

73

context: Context,

74

name: String? = null,

75

factory: SupportSQLiteOpenHelper.Factory = FrameworkSQLiteOpenHelperFactory(),

76

callback: SupportSQLiteOpenHelper.Callback = AndroidSqliteDriver.Callback(schema),

77

cacheSize: Int = 20,

78

useNoBackupDirectory: Boolean = false,

79

windowSizeBytes: Long? = null

80

)

81

82

/**

83

* Create driver from existing SupportSQLiteOpenHelper

84

*/

85

constructor(openHelper: SupportSQLiteOpenHelper)

86

87

/**

88

* Create driver from existing SupportSQLiteDatabase

89

*/

90

constructor(

91

database: SupportSQLiteDatabase,

92

cacheSize: Int = 20,

93

windowSizeBytes: Long? = null

94

)

95

```

96

97

**Parameters:**

98

- `schema`: SQLDelight schema containing DDL and migrations

99

- `context`: Android Context for database file access

100

- `name`: Database file name (null for in-memory database)

101

- `factory`: Factory for creating SupportSQLiteOpenHelper (defaults to FrameworkSQLiteOpenHelperFactory)

102

- `callback`: Database lifecycle callback handler (defaults to AndroidSqliteDriver.Callback)

103

- `cacheSize`: Number of prepared statements to cache (defaults to 20)

104

- `useNoBackupDirectory`: Whether to prevent database backup (defaults to false)

105

- `windowSizeBytes`: Cursor window size in bytes for Android 28+ (defaults to null)

106

107

### SQL Execution

108

109

Execute SQL statements and queries with parameter binding.

110

111

```kotlin { .api }

112

/**

113

* Execute SQL statement (INSERT, UPDATE, DELETE)

114

* @param identifier Optional cache key for statement caching

115

* @param sql SQL statement string

116

* @param parameters Number of bindable parameters

117

* @param binders Function to bind parameters to statement

118

* @return Number of affected rows

119

*/

120

fun execute(

121

identifier: Int?,

122

sql: String,

123

parameters: Int,

124

binders: (SqlPreparedStatement.() -> Unit)? = null

125

): QueryResult<Long>

126

127

/**

128

* Execute SQL query (SELECT) and map results

129

* @param identifier Optional cache key for statement caching

130

* @param sql SQL query string

131

* @param mapper Function to map cursor results to return type

132

* @param parameters Number of bindable parameters

133

* @param binders Function to bind parameters to statement

134

* @return Mapped query results

135

*/

136

fun <R> executeQuery(

137

identifier: Int?,

138

sql: String,

139

mapper: (SqlCursor) -> QueryResult<R>,

140

parameters: Int,

141

binders: (SqlPreparedStatement.() -> Unit)? = null

142

): QueryResult<R>

143

```

144

145

### Transaction Management

146

147

Manage database transactions with proper nesting support.

148

149

```kotlin { .api }

150

/**

151

* Start a new database transaction

152

* @return Transaction instance for controlling transaction lifecycle

153

*/

154

fun newTransaction(): QueryResult<Transacter.Transaction>

155

156

/**

157

* Get the currently active transaction

158

* @return Current transaction or null if none active

159

*/

160

fun currentTransaction(): Transacter.Transaction?

161

```

162

163

**Transaction Class:**

164

```kotlin { .api }

165

inner class Transaction(

166

override val enclosingTransaction: Transacter.Transaction?

167

) : Transacter.Transaction() {

168

/**

169

* End the transaction

170

* @param successful Whether to commit (true) or rollback (false)

171

* @return QueryResult<Unit> indicating completion

172

*/

173

override fun endTransaction(successful: Boolean): QueryResult<Unit>

174

}

175

```

176

177

### Query Listeners

178

179

Register listeners for reactive query result change notifications.

180

181

```kotlin { .api }

182

/**

183

* Add listener for query result changes

184

* @param queryKeys Variable number of query keys to listen for

185

* @param listener Listener to be notified of changes

186

*/

187

fun addListener(vararg queryKeys: String, listener: Query.Listener)

188

189

/**

190

* Remove previously registered listener

191

* @param queryKeys Variable number of query keys to stop listening for

192

* @param listener Listener to remove

193

*/

194

fun removeListener(vararg queryKeys: String, listener: Query.Listener)

195

196

/**

197

* Notify all registered listeners of query changes

198

* @param queryKeys Variable number of query keys that changed

199

*/

200

fun notifyListeners(vararg queryKeys: String)

201

```

202

203

### Resource Management

204

205

Properly close and cleanup database resources.

206

207

```kotlin { .api }

208

/**

209

* Close database connection and cleanup resources

210

* Evicts all cached statements and closes underlying database

211

*/

212

fun close()

213

```

214

215

### Database Callbacks

216

217

Handle database lifecycle events for schema creation and migration.

218

219

```kotlin { .api }

220

/**

221

* Database callback handler for schema creation and migration

222

*/

223

open class Callback(

224

private val schema: SqlSchema<QueryResult.Value<Unit>>,

225

private vararg val callbacks: AfterVersion

226

) : SupportSQLiteOpenHelper.Callback(schema.version.toInt()) {

227

228

/**

229

* Called when database is created for the first time

230

* @param db Database instance being created

231

*/

232

override fun onCreate(db: SupportSQLiteDatabase)

233

234

/**

235

* Called when database needs to be upgraded

236

* @param db Database instance being upgraded

237

* @param oldVersion Previous database version

238

* @param newVersion Target database version

239

*/

240

override fun onUpgrade(

241

db: SupportSQLiteDatabase,

242

oldVersion: Int,

243

newVersion: Int

244

)

245

}

246

```

247

248

## Types

249

250

```kotlin { .api }

251

/**

252

* Interface for SQL cursor operations

253

*/

254

interface SqlCursor {

255

fun next(): QueryResult<Boolean>

256

fun getString(index: Int): String?

257

fun getLong(index: Int): Long?

258

fun getBytes(index: Int): ByteArray?

259

fun getDouble(index: Int): Double?

260

fun getBoolean(index: Int): Boolean?

261

}

262

263

/**

264

* Interface for prepared statement parameter binding

265

*/

266

interface SqlPreparedStatement {

267

fun bindBytes(index: Int, bytes: ByteArray?)

268

fun bindLong(index: Int, long: Long?)

269

fun bindDouble(index: Int, double: Double?)

270

fun bindString(index: Int, string: String?)

271

fun bindBoolean(index: Int, boolean: Boolean?)

272

}

273

274

/**

275

* Schema definition interface containing DDL and migrations

276

*/

277

interface SqlSchema<T> {

278

val version: Long

279

fun create(driver: SqlDriver): T

280

fun migrate(

281

driver: SqlDriver,

282

oldVersion: Long,

283

newVersion: Long,

284

vararg callbacks: AfterVersion

285

): T

286

}

287

288

/**

289

* Result wrapper type for database operations

290

*/

291

sealed interface QueryResult<out T> {

292

val value: T

293

294

object Unit : QueryResult<kotlin.Unit> {

295

override val value: kotlin.Unit = kotlin.Unit

296

}

297

298

data class Value<T>(override val value: T) : QueryResult<T>

299

}

300

301

/**

302

* Migration callback executed after version upgrade

303

*/

304

class AfterVersion(

305

val afterVersion: Long,

306

val block: (SqlDriver) -> kotlin.Unit

307

)

308

309

/**

310

* Query change listener interface

311

*/

312

interface Query.Listener {

313

fun queryResultsChanged()

314

}

315

316

/**

317

* Transaction interface for database transaction control

318

*/

319

abstract class Transacter.Transaction {

320

abstract val enclosingTransaction: Transacter.Transaction?

321

abstract fun endTransaction(successful: Boolean): QueryResult<kotlin.Unit>

322

}

323

```

324

325

## Error Handling

326

327

The Android Driver handles common database errors through the QueryResult type system and standard SQLite exceptions:

328

329

- **SQLiteException**: Thrown for SQL syntax errors, constraint violations, and database corruption

330

- **IllegalStateException**: Thrown when attempting operations on closed driver or invalid transaction state

331

- **IllegalArgumentException**: Thrown for invalid constructor parameters or method arguments

332

333

Common error scenarios:

334

- Database file corruption or inaccessible storage

335

- SQL constraint violations (UNIQUE, FOREIGN KEY, CHECK)

336

- Transaction deadlocks or timeout issues

337

- Invalid SQL syntax in statements

338

339

## Thread Safety

340

341

The AndroidSqliteDriver is designed for multi-threaded access with the following guarantees:

342

343

- **Statement caching**: Thread-safe LRU cache with proper synchronization

344

- **Transaction management**: Thread-local transaction storage prevents cross-thread interference

345

- **Query listeners**: Synchronized listener registration and notification

346

- **Database operations**: All operations are properly synchronized through the underlying AndroidX SQLite library

347

348

Best practices:

349

- Use a single driver instance per database across your application

350

- Transactions are bound to the calling thread and cannot be shared

351

- Query listeners are called on the thread that triggered the database change