{"id":24313542,"url":"https://github.com/eygraber/sqldelight-androidx-driver","last_synced_at":"2026-05-04T10:01:57.800Z","repository":{"id":272322214,"uuid":"916181351","full_name":"eygraber/sqldelight-androidx-driver","owner":"eygraber","description":"A SQLDelight Driver that wraps AndroidX Kotlin Multiplatform SQLite","archived":false,"fork":false,"pushed_at":"2026-05-04T08:17:06.000Z","size":1031,"stargazers_count":84,"open_issues_count":2,"forks_count":6,"subscribers_count":2,"default_branch":"master","last_synced_at":"2026-05-04T09:46:06.133Z","etag":null,"topics":["androidx","androidx-sqlite","kotlin","kotlin-multiplatform","sqldelight","sqlite"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eygraber.png","metadata":{"files":{"readme":"README.md","changelog":"changelog_config.json","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-01-13T15:56:59.000Z","updated_at":"2026-05-04T08:37:05.000Z","dependencies_parsed_at":"2025-03-04T20:27:45.357Z","dependency_job_id":"fd342cfe-e11b-4509-b6e8-85f48db2b26c","html_url":"https://github.com/eygraber/sqldelight-androidx-driver","commit_stats":null,"previous_names":["eygraber/sqldelight-androidx-driver"],"tags_count":23,"template":false,"template_full_name":null,"purl":"pkg:github/eygraber/sqldelight-androidx-driver","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eygraber%2Fsqldelight-androidx-driver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eygraber%2Fsqldelight-androidx-driver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eygraber%2Fsqldelight-androidx-driver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eygraber%2Fsqldelight-androidx-driver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eygraber","download_url":"https://codeload.github.com/eygraber/sqldelight-androidx-driver/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eygraber%2Fsqldelight-androidx-driver/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32602730,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T22:12:39.696Z","status":"online","status_checked_at":"2026-05-04T02:00:06.625Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["androidx","androidx-sqlite","kotlin","kotlin-multiplatform","sqldelight","sqlite"],"created_at":"2025-01-17T09:12:20.776Z","updated_at":"2026-05-04T10:01:57.792Z","avatar_url":"https://github.com/eygraber.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SQLDelight AndroidX Driver\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.eygraber/sqldelight-androidx-driver)](https://central.sonatype.com/artifact/com.eygraber/sqldelight-androidx-driver)\n\nThe SQLDelight AndroidX Driver provides a [SQLDelight] driver that wraps [AndroidX Kotlin Multiplatform SQLite] libraries. It works with any of the available implementations of AndroidX SQLite.\n\n* [SQLDelight docs]\n* [Set up SQLite for KMP]\n\n\u003e [!TIP]\n\u003e Interested in trying out web (`wasmJs` / `js`) support? An alpha is in progress on the [`web`](https://github.com/eygraber/sqldelight-androidx-driver/tree/web) branch — feedback welcome.\n\n## Migrating from the pre-async version of the driver\n\n\u003e [!NOTE]  \n\u003e If you are migrating from version 0.0.17 or earlier, check out the [migration guide](./PRE_ASYNC_MIGRATION_GUIDE.md)\n\n## Getting Started\n\n### 1. Add Sqlite Dependencies\n\nFirst setup SQLite dependencies following [Set up SQLite for KMP].\n\n### 2. Add AndroidX Driver Dependency\n\nNext add the dependency to your project.\n\n```kotlin\nrepositories {\n  mavenCentral()\n}\n\n// Android/JVM\ndependencies {\n  implementation(\"com.eygraber:sqldelight-androidx-driver:0.2.0\")\n}\n\n// Multiplatform\ncommonMain.dependencies {\n  implementation(\"com.eygraber:sqldelight-androidx-driver:0.2.0\")\n}\n```\n\nSee [Consuming Via Gradle] for how to add a -SNAPSHOT release.\n\n### 3. Configure Database\n\nNext configure the database.\n\n\u003e [!IMPORTANT]\n\u003e `generateAsync` must equal `true` since `AndroidxSqliteDriver` is a suspending driver.\n\n\u003e [!IMPORTANT]\n\u003e If you're producing a **dynamic framework** for a Kotlin/Native target (typically iOS) and using\n\u003e `BundledSQLiteDriver()`, set `linkSqlite = false`. SQLDelight defaults to passing `-lsqlite3` to\n\u003e the linker so the framework links against the system SQLite, but `androidx.sqlite:sqlite-bundled`\n\u003e already statically bundles its own SQLite — leaving `linkSqlite = true` makes `lld` fail with\n\u003e `library not found for -lsqlite3`.\n\n```kotlin\nsqldelight {\n  linkSqlite = false\n\n  databases {\n    register(\"Database\") {\n      generateAsync = true\n    }\n  }\n}\n```\n\n## Usage\n\n\u003e [!TIP]\n\u003e AndroidX team recommends `BundledSQLiteDriver` but you can use whichever one suits your needs. See [SQLite Driver Implementations](https://developer.android.com/kotlin/multiplatform/sqlite#sqlite-driver-implementations) for more info.\n\n### Create the Driver and Database\n\nAndroid or JVM - Pass a `File`:\n\n```kotlin\nval driver = AndroidxSqliteDriver(\n  driver = BundledSQLiteDriver(),\n  databaseType = AndroidxSqliteDatabaseType.File(File(\"my.db\")),\n  schema = Database.Schema,\n)\nval database = Database(driver)\n\n// More database stuff...\n```\n\nAndroid - Use `Context` to create the file in the app's database directory:\n\n```kotlin\nval driver = AndroidxSqliteDriver(\n  driver = BundledSQLiteDriver(),\n  databaseType = AndroidxSqliteDatabaseType.FileProvider(context, \"my.db\"),\n  schema = Database.Schema,\n)\nval database = Database(driver)\n\n// More database stuff...\n```\n\nMultiplatform - Create a database type factory:\n\n```kotlin\n// src/commonMain/kotlin\nexpect class DatabaseTypeFactory {\n  fun createDatabaseType(): AndroidxSqliteDatabaseType\n}\n\nfun createDatabase(databaseTypeFactory: DatabaseTypeFactory): Database {\n  val driver = AndroidxSqliteDriver(\n    driver = BundledSQLiteDriver(),\n    databaseType = databaseTypeFactory.createDatabaseType(),\n    schema = Database.Schema\n  )\n  val database = Database(driver)\n  \n  // More database stuff...\n}\n\n// src/androidMain/kotlin\nactual class DatabaseTypeFactory(private val context: Context) {\n  actual fun createDatabaseType(): AndroidxSqliteDatabaseType {\n    return AndroidxSqliteDatabaseType.FileProvider(context, \"my.db\")\n  }\n}\n\n// src/nativeMain/kotlin\nactual class DatabaseTypeFactory {\n  actual fun createDatabaseType(): AndroidxSqliteDatabaseType {\n    return AndroidxSqliteDatabaseType.File(\"\u003cabsolute path to db file\u003e\")\n  }\n}\n\n// src/jvmMain/kotlin\nactual class DatabaseTypeFactory {\n  actual fun createDatabaseType(): AndroidxSqliteDatabaseType {\n    AndroidxSqliteDatabaseType.File(File(\"my.db\"))\n  }\n}\n```\n\n### Provide OpenFlags\n\nIf you want to provide `OpenFlags` to the bundled or native driver, you can use:\n\n```kotlin\nDatabase(\n  AndroidxSqliteDriver(\n    connectionFactory = object : AndroidxSqliteConnectionFactory {\n      override val driver = BundledSQLiteDriver()\n      \n      override fun createConnection(name: String) =\n        driver.open(name, SQLITE_OPEN_READWRITE or SQLITE_OPEN_CREATE)\n    },\n    databaseType = AndroidxSqliteDatabaseType.File(\"\u003cabsolute path to db file\u003e\"),\n    schema = Database.Schema,\n  )\n)\n```\n\nIt will handle calling the `create` and `migrate` functions on your schema for you, and keep track of the database's version.\n\n## Suspending Driver\n\n`AndroidxSqliteDriver` is a suspending driver, so SQLDelight must be configured with\n`generateAsync = true`:\n\n```kotlin\nsqldelight {\n  databases {\n    register(\"Database\") {\n      generateAsync = true\n    }\n  }\n}\n```\n\nWith `generateAsync = true`, generated queries return `QueryResult.AsyncValue` rather than\nmaterialized values — you must call `.await()` (or the `awaitAsOne`/`awaitAsList`/etc. helpers\nfrom `app.cash.sqldelight:async-extensions`) to get the result:\n\n```kotlin\nval user: User = database.userQueries.selectById(id).awaitAsOne()\nval users: List\u003cUser\u003e = database.userQueries.selectAll().awaitAsList()\n```\n\nAll database calls are suspending, and the driver runs them on its own coroutine dispatchers —\nyou don't need to wrap calls in `withContext(Dispatchers.IO)`. Learn more [below](#dispatchers).\n\n### Lifecycle Callbacks\n\n`AndroidxSqliteDriver` accepts four optional callbacks, all invoked at most once on the first\ninteraction with the database:\n\n```kotlin\nAndroidxSqliteDriver(\n  driver = BundledSQLiteDriver(),\n  databaseType = AndroidxSqliteDatabaseType.File(\"my.db\"),\n  schema = Database.Schema,\n  onConfigure = {\n    // Suspending. Runs before create/migrate. Use it to override pragmas that aren't\n    // covered by AndroidxSqliteConfiguration.\n    setForeignKeyConstraintsEnabled(true)\n  },\n  onCreate = { /* side effects after first create */ },\n  onUpdate = { old, new -\u003e /* side effects after migration */ },\n  onOpen = { /* side effects after create or migrate */ },\n)\n```\n\nAll four callbacks run at most once per driver instance, on the first database interaction,\nguarded by an internal mutex — concurrent first-time callers all wait for them to complete before\nany queries execute. If you close the driver and construct a new one against the same database,\n`onConfigure` and `onOpen` run again for the new instance; `onCreate` and `onUpdate` only run if\nthe schema actually needs to be created or migrated.\n\n- `onConfigure` runs first, before any schema work. It's the right place to override pragmas\n  that aren't covered by `AndroidxSqliteConfiguration`.\n- `onCreate` runs only when the database is created for the first time, after `SqlSchema.create`\n  has committed.\n- `onUpdate` runs only when the schema version has increased, after `SqlSchema.migrate` has\n  committed.\n- `onOpen` runs on every first interaction, after any create/migrate work.\n\nAll four callbacks are `suspend` lambdas, so you can `await()` driver operations directly inside\nthem. Note that `onCreate`, `onUpdate`, and `onOpen` run *after* the create/migrate transaction\nhas committed — they aren't part of it. To seed data or run additional SQL inside the\ncreate/migrate transaction, put it in your `SqlSchema.create` / `SqlSchema.migrate`, or use\n`migrationCallbacks` (see below).\n\n### Migration Callbacks\n\nTo run code at specific schema versions during migration (the equivalent of SQLDelight's\n`AfterVersion`), pass `AndroidxSqliteAfterVersion` instances to `migrationCallbacks`:\n\n```kotlin\nAndroidxSqliteDriver(\n  driver = BundledSQLiteDriver(),\n  databaseType = AndroidxSqliteDatabaseType.File(\"my.db\"),\n  schema = Database.Schema,\n  migrationCallbacks = arrayOf(\n    AndroidxSqliteAfterVersion(afterVersion = 3) { driver -\u003e\n      // Suspending. Runs inside the migration's transaction, on its writer connection.\n      driver.execute(null, \"INSERT INTO settings(key, value) VALUES ('feature_x', 'on')\", 0).await()\n    },\n  ),\n)\n```\n\nThe callback's `block` is `suspend (SqlDriver) -\u003e Unit`. The driver bridges the migration's\ncoroutine context into it so DB ops reuse the migration's writer instead of trying to acquire a\nfresh one (which would deadlock). If the callback throws, the entire migration transaction rolls\nback.\n\n### Flow Extensions\n\nA companion artifact provides `Flow` extensions that mirror\n[`app.cash.sqldelight:coroutines-extensions`](https://github.com/sqldelight/sqldelight/blob/master/extensions/coroutines-extensions/src/commonMain/kotlin/app/cash/sqldelight/coroutines/FlowExtensions.kt),\nbut default the `CoroutineContext` parameter to `EmptyCoroutineContext` — the driver already\ndispatches each query onto its own connection pool, so wrapping every mapper in a second\n`withContext(Dispatchers.IO)` is redundant.\n\n```kotlin\ndependencies {\n  implementation(\"com.eygraber:sqldelight-coroutines-extensions:0.2.0\")\n}\n```\n\n```kotlin\nimport com.eygraber.sqldelight.androidx.driver.coroutines.asFlow\nimport com.eygraber.sqldelight.androidx.driver.coroutines.mapToList\nimport com.eygraber.sqldelight.androidx.driver.coroutines.mapToOne\nimport com.eygraber.sqldelight.androidx.driver.coroutines.mapToOneOrNull\n\nval usersFlow: Flow\u003cList\u003cUser\u003e\u003e = database.userQueries.selectAll().asFlow().mapToList()\nval userFlow: Flow\u003cUser?\u003e = database.userQueries.selectById(id).asFlow().mapToOneOrNull()\n```\n\nYou can still pass an explicit `CoroutineContext` if you want the mapper to run somewhere\nspecific (e.g. `Dispatchers.Main` for UI state). If you prefer the sqldelight extensions,\nthey still work — just import from `app.cash.sqldelight.coroutines` instead.\n\n## Testing\n\nFor tests, use `AndroidxSqliteDatabaseType.Memory` — each driver instance gets its own isolated\ndatabase, nothing is written to disk, and there's nothing to clean up between tests. In-memory\nand temporary databases automatically use `SingleReaderWriter`, so you don't need to configure\nthe concurrency model.\n\nPick the underlying `SQLiteDriver` based on where the test runs:\n\n- **JVM, Native, and Android host (unit) tests** — `BundledSQLiteDriver()` from\n  `androidx.sqlite:sqlite-bundled`. Works across all platforms with no Android runtime.\n- **Android instrumented tests** — `AndroidSQLiteDriver()` from `androidx.sqlite:sqlite-framework`\n  if you want to exercise the platform's SQLite; `BundledSQLiteDriver()` also works on device.\n\n\u003e [!IMPORTANT]\n\u003e For Android **host** (unit) tests — the ones that run on a JVM under\n\u003e `src/androidHostTest` / `src/test`, not on a device — the Android variant of\n\u003e `androidx.sqlite:sqlite-bundled` is what gets resolved, and it doesn't ship the JVM native\n\u003e binaries `BundledSQLiteDriver()` needs. The test will load the driver, fail to find a native\n\u003e library for your host OS, and crash. You'll probably need to substitute the Android artifact\n\u003e with its `-jvm` counterpart, but only for the unit test runtime classpath:\n\u003e\n\u003e ```kotlin\n\u003e import com.android.build.api.variant.HasUnitTest\n\u003e\n\u003e androidComponents {\n\u003e   onVariants { variant -\u003e\n\u003e     (variant as HasUnitTest).unitTest?.let { unitTest -\u003e\n\u003e       with(unitTest.runtimeConfiguration.resolutionStrategy.dependencySubstitution) {\n\u003e         substitute(module(\"androidx.sqlite:sqlite-bundled:\u003cversion\u003e\"))\n\u003e           .using(module(\"androidx.sqlite:sqlite-bundled-jvm:\u003cversion\u003e\"))\n\u003e       }\n\u003e     }\n\u003e   }\n\u003e }\n\u003e ```\n\u003e\n\u003e The production Android variant is left untouched — this only swaps in the JVM artifact for the\n\u003e host test JVM that Gradle spins up.\n\n```kotlin\nclass UserRepositoryTest {\n  private lateinit var driver: SqlDriver\n  private lateinit var database: Database\n\n  @BeforeTest\n  fun setup() {\n    driver = AndroidxSqliteDriver(\n      driver = BundledSQLiteDriver(),\n      databaseType = AndroidxSqliteDatabaseType.Memory,\n      schema = Database.Schema,\n    )\n    database = Database(driver)\n  }\n\n  @AfterTest\n  fun tearDown() {\n    driver.close()\n  }\n\n  @Test\n  fun insertedUserCanBeQueried() = runTest {\n    database.userQueries.insert(id = 1, name = \"Alec\").await()\n\n    val user = database.userQueries.selectById(1).awaitAsOne()\n    assertEquals(\"Alec\", user.name)\n  }\n}\n```\n\nA few things to keep in mind:\n\n- Wrap test bodies in `kotlinx.coroutines.test.runTest` so suspending driver calls have a scope to\n  run in. The driver dispatches its own work onto `Dispatchers.IO` by default — `runTest` does not\n  control that scheduling, so tests run against real concurrency (which is what you want when\n  exercising the connection pool).\n- Generated query and statement calls return `QueryResult.AsyncValue` because of\n  `generateAsync = true`. Use `.await()` on `execute` calls and `awaitAsOne` / `awaitAsOneOrNull` /\n  `awaitAsList` from `app.cash.sqldelight:async-extensions` for queries.\n- Always `driver.close()` in `@AfterTest` (or `use { }`) so the driver's dispatcher and connection\n  pool are released between tests.\n- For multiplatform projects, the `commonTest` setup above works as-is — `BundledSQLiteDriver` and\n  `AndroidxSqliteDatabaseType.Memory` are both available in common code.\n\nIf you need to test against a file-backed database instead, the setup from\n[Create the Driver and Database](#create-the-driver-and-database) applies as-is — just swap\n`AndroidxSqliteDatabaseType.Memory` for `AndroidxSqliteDatabaseType.File(...)` (or `FileProvider`\non Android). When tests run in parallel — across Gradle modules, or within a single module via\nparallel test execution — make sure each test uses a unique database filename. Two tests opening\nthe same file concurrently will share state and contend on the same SQLite file locks, which leads\nto flaky failures. A common pattern is to derive the name from the test class/method or a UUID,\nand place the file in a per-test temp directory.\n\n## Foreign Key Constraints\n\nWhen using `AndroidxSqliteDriver`, the handling of foreign key constraints during database creation and migration is\nmanaged to ensure data integrity.\n\nIf you have foreign key constraints enabled in your\n`AndroidxSqliteConfiguration` (i.e. `isForeignKeyConstraintsEnabled = true`),\nthe driver will automatically disable them before executing the schema `create` or `migrate` operations.\nThis is done to prevent issues with table creation order and data manipulation during the migration process.\n\nAfter the creation or migration is complete, foreign key constraints are re-enabled.\n\nFurthermore, to verify the integrity of the foreign key relationships after these operations,\nthe driver performs an additional check. If `isForeignKeyConstraintsCheckedAfterCreateOrUpdate`\nis `true` (which it is by default), a `PRAGMA foreign_key_check` is executed. If this check finds\nany violations, an `AndroidxSqliteDriver.ForeignKeyConstraintCheckException` is thrown, detailing the\nspecific constraints that have been violated. This helps catch any inconsistencies in your data that might\nhave been introduced during the migration.\n\n\u003e [!IMPORTANT]  \n\u003e By default, the first 100 violations will be parsed out of the result set of\n\u003e `PRAGMA foreign_key_check` and stored in the `AndroidxSqliteDriver.ForeignKeyConstraintCheckException`.\n\u003e If your use can result in a large number of violations you can adjust the max amount that will be processed via\n\u003e `AndroidxSqliteConfiguration.maxMigrationForeignKeyConstraintViolationsToReport`.\n\n## Connection Pooling\n\nSQLite supports several concurrency models that can significantly impact your application's performance. This driver\nprovides flexible connection pooling through the `AndroidxSqliteConcurrencyModel` interface.\n\n### Available Concurrency Models\n\n#### 1. SingleReaderWriter\n\nThe simplest model with one connection handling all operations:\n\n```kotlin\nAndroidxSqliteConfiguration(\n  concurrencyModel = AndroidxSqliteConcurrencyModel.SingleReaderWriter()\n)\n```\n\n**Best for:**\n\n- Simple applications with minimal database usage\n- Testing and development\n- When memory usage is a primary concern\n- Single-threaded applications\n\n#### 2. MultipleReaders\n\nDedicated reader connections for read-only access:\n\n```kotlin\nAndroidxSqliteConfiguration(\n  concurrencyModel = AndroidxSqliteConcurrencyModel.MultipleReaders(\n    readerCount = 3  // Number of concurrent reader connections\n  )\n)\n```\n\n**Best for:**\n\n- Read-only applications (analytics dashboards, reporting tools)\n- Data visualization and content browsing applications\n- Scenarios where all writes happen externally (data imports, ETL processes)\n- Applications that only query pre-populated databases\n\n**Important:** This model is designed for **read-only access**. No write operations (INSERT, UPDATE, DELETE) should be\nperformed. If you need write capabilities, use `MultipleReadersSingleWriter` in WAL mode instead.\n\n#### 3. MultipleReadersSingleWriter (Recommended)\n\nThe most flexible model that adapts based on journal mode:\n\n```kotlin\nAndroidxSqliteConfiguration(\n  concurrencyModel = AndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(\n    isWal = true,        // Enable WAL mode for true concurrency\n    walCount = 3,        // Reader connections when WAL is enabled (default: 3)\n    nonWalCount = 0,     // Reader connections when WAL is disabled (default: 0)\n  )\n)\n```\n\n**Best for:**\n\n- Most production applications\n- Mixed read/write workloads\n- When you want to leverage WAL mode benefits\n- Applications requiring optimal performance\n\n### WAL Mode Benefits\n\n- **True Concurrency**: Readers and writers don't block each other\n- **Better Performance**: Concurrent operations improve throughput\n- **Consistency**: ACID properties are maintained (when `PRAGMA synchronous = FULL` is used)\n- **Scalability**: Handles higher concurrent load\n\n### Choosing Reader Connection Count\n\nThe optimal number of reader connections depends on your use case:\n\n```kotlin\n// Conservative (default)\nAndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(\n  isWal = true,\n  walCount = 3,\n  nonWalCount = 0,\n)\n\n// High-concurrency applications\nAndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(\n  isWal = true, \n  walCount = 8\n)\n\n// Memory-conscious applications\nAndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(\n  isWal = true,\n  walCount = 2\n)\n```\n\n### Platform-Specific Configuration\n\nOn Android, you can use system-determined connection pool sizes:\n\n```kotlin\n// Based on SQLiteGlobal.getWALConnectionPoolSize()\nfun getWALConnectionPoolSize(): Int {\n  val resources = Resources.getSystem()\n  val resId = resources.getIdentifier(\"db_connection_pool_size\", \"integer\", \"android\")\n  return if (resId != 0) {\n    resources.getInteger(resId)\n  } else {\n    2  // Fallback default\n  }\n}\n\nAndroidxSqliteConfiguration(\n  concurrencyModel = AndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(\n    isWal = true,\n    walCount = getWALConnectionPoolSize(),\n    nonWalCount = 0,\n  )\n)\n```\n\n### Performance Considerations\n\n| Model                                 | Memory Usage | Read Concurrency | Write Capability | Best Use Case      |\n|---------------------------------------|--------------|------------------|------------------|--------------------|\n| SingleReaderWriter                    | Lowest       | None             | Full             | Simple apps        |\n| MultipleReaders                       | Medium       | Excellent        | None (read-only) | Read-only apps     |\n| MultipleReadersSingleWriter (WAL)     | Higher       | Excellent        | Full             | Production         |\n| MultipleReadersSingleWriter (non-WAL) | Medium       | Limited          | Full             | Legacy/constrained |\n\n### Special Database Types\n\n\u003e [!NOTE]  \n\u003e In-Memory and temporary databases automatically use `SingleReaderWriter` model regardless of configuration, as\n\u003e connection pooling provides no benefit for these database types.\n\n## Dispatchers\n\nThe driver runs SQLite work on its own `CoroutineDispatcher`, sized to match the concurrency model\nso a writer and each reader get their own slot. You don't need to use `withContext(Dispatchers.IO)`\nwhen calling the driver — queries and transactions will suspend onto the driver's dispatcher\nautomatically, and switch back to your calling context when they return. Inside a transaction,\nevery operation stays on the same slot for the lifetime of the transaction, so you don't have to\nworry about switching connections mid-transaction.\n\nYou pick how those threads are sourced by passing a `dispatcherProvider` when constructing the\nconcurrency model. Two are bundled:\n\n```kotlin\n// Default. Shares threads with Dispatchers.IO via limitedParallelism.\n// Lower memory — no dedicated threads are created for the driver.\nAndroidxSqliteConcurrencyModel.memoryOptimizedProvider()\n\n// Allocates a dedicated thread pool (via newFixedThreadPoolContext).\n// Each connection tends to stay on the same thread, which helps CPU cache locality\n// at the cost of extra OS threads.\nAndroidxSqliteConcurrencyModel.CpuCacheHitOptimizedProvider\n```\n\n`memoryOptimizedProvider()` also accepts a base dispatcher if you'd rather derive parallelism from\nsomewhere other than `Dispatchers.IO`:\n\n```kotlin\nAndroidxSqliteConcurrencyModel.memoryOptimizedProvider(\n  dispatcher = myBackgroundDispatcher,\n)\n```\n\nThe provider plugs into any concurrency model:\n\n```kotlin\n// Single connection, single dispatcher slot\nAndroidxSqliteConcurrencyModel.SingleReaderWriter(\n  dispatcherProvider = AndroidxSqliteConcurrencyModel.CpuCacheHitOptimizedProvider,\n)\n\n// Multiple readers (read-only), one slot per reader\nAndroidxSqliteConcurrencyModel.MultipleReaders(\n  readerCount = 3,\n  dispatcherProvider = AndroidxSqliteConcurrencyModel.CpuCacheHitOptimizedProvider,\n)\n\n// Multiple readers + single writer\nAndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(\n  isWal = true,\n  walCount = 3,\n  dispatcherProvider = AndroidxSqliteConcurrencyModel.CpuCacheHitOptimizedProvider,\n)\n```\n\n`driver.close()` disposes the dispatcher, so you generally don't need to manage its lifetime.\n\n### Journal Mode\n\nIf `PRAGMA journal_mode = ...` is executed through the driver, the connection pool will:\n\n1. Block new reader acquisitions and wait for in-flight readers to be returned\n2. Close the returned reader connections\n3. Acquire the writer connection\n4. Run the `PRAGMA` statement\n5. If the `MultipleReadersSingleWriter` model is in use, flip its `isWal` flag based on the\n   journal mode the statement returned\n6. Recreate the reader connections (sized from the updated concurrency model)\n7. Release the writer and wake any parked reader acquisitions\n\nThis ensures all connections use the same journal mode and prevents inconsistencies.\n\n### Best Practices\n\n1. **Start with defaults**: Uses `MultipleReadersSingleWriter` in WAL mode\n2. **Monitor performance**: Profile your specific workload to determine optimal reader count\n3. **Consider memory**: Each connection has overhead - balance performance vs memory usage\n4. **Test thoroughly**: Verify your concurrency model works under expected load\n5. **Platform differences**: Android may have different optimal settings than JVM/Native\n\nFor additional background on WAL mode and dispatcher tuning, see [WAL \u0026 Dispatchers].\n\n[AndroidX Kotlin Multiplatform SQLite]: https://developer.android.com/kotlin/multiplatform/sqlite\n[SQLDelight]: https://github.com/sqldelight/sqldelight\n[WAL \u0026 Dispatchers]: https://blog.p-y.wtf/parallelism-with-android-sqlite#heading-wal-amp-dispatchers\n[Write-Ahead Logging]: https://sqlite.org/wal.html\n[SQLDelight docs]: https://sqldelight.github.io/sqldelight/latest/\n[Set up SQLite for KMP]: https://developer.android.com/kotlin/multiplatform/sqlite\n[Consuming Via Gradle]: https://central.sonatype.org/publish/publish-portal-snapshots/#consuming-via-gradle\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feygraber%2Fsqldelight-androidx-driver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feygraber%2Fsqldelight-androidx-driver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feygraber%2Fsqldelight-androidx-driver/lists"}