SQLite¶
The SQLite driver runs on every supported target, including Android:
| Platform | Backed by |
|---|---|
| Native | sqlx (Rust), through Kotlin/Native FFI. |
| JVM | xerial/sqlite-jdbc, pooled with HikariCP (transitive dependencies). |
| Android | The platform's android.database.sqlite, so no native libraries need to be added (minSdk 26). |
Need encryption? See SQLCipher.
Connecting¶
val db = SQLite(
url = "sqlite://test.db", // If the `test.db` file is not found, a new db will be created.
options = options // see Connection pool
)
In a multiplatform setup, use the sqlite(url, options) function instead. On Android, use the
sqlite(context, url, options) overload (or SQLite(context, url, options)): the Context resolves a relative
database filename into the app's databases directory.
URL format¶
Every target accepts the same sqlx-style URL, so a single string works everywhere:
| URL | Opens |
|---|---|
sqlite::memory: |
An in-memory database. |
sqlite:data.db |
The file data.db in the current directory. |
sqlite://data.db |
Same as above. |
sqlite:///abs/path/data.db |
The file at an absolute path. |
sqlite://data.db?mode=ro |
data.db for read-only access. |
sqlite:///abs/data.db?mode=rwc |
Read-write, creating the file if missing. |
sqlite::memory:?cache=shared |
An in-memory database with a shared cache. |
The query parameters are the ones SQLite defines for URI filenames: mode (ro, rw, rwc, memory), cache
(shared, private), immutable and vfs. Any other parameter is rejected, so a bad URL fails the same way on every
target.
- JVM: a
jdbc:sqlite:URL is accepted as well and skips that check, so JDBC-style URLs with driver-specific parameters (e.g. xerial pragmas such as?journal_mode=WAL) keep working. HikariCP raises timeouts below its minimums:acquireTimeoutto 250 ms,idleTimeoutto 10 seconds andmaxLifetimeto 30 seconds. - Android: a relative filename lives in the app's databases directory (
Context.getDatabasePath).cacheis accepted but ignored, andimmutableandvfscannot be honoured, so they are rejected. A URL without thesqlite:prefix is prefixed automatically.
In-memory databases¶
An in-memory SQLite database is private to the connection that opened it, so an in-memory database must use a pool of a single connection; on the JVM and Android a larger pool is rejected:
val db = SQLite(
url = "sqlite::memory:",
options = ConnectionPool.Options(minConnections = 1, maxConnections = 1)
)
WAL mode¶
File-based databases are opened in Write-Ahead Logging (WAL) mode, which lets readers run concurrently with a writer. In-memory databases do not support WAL.
Tips and limitations¶
- Write concurrency: SQLite locks the whole database for a write, so writes are serialized whatever the pool size. A larger pool helps read-heavy workloads; a write-heavy workload gains little from more connections.
- Batch writes in transactions: many small writes are much faster inside a single transaction.
executeWithResultreports the rowid of an inserted row; for a multi-row insert, the last one.- Foreign keys in migrations: see the note in Migrations.
- Android: savepoints (
savepoint/releaseSavepoint/rollbackToSavepoint) are not supported, becauseandroid.database.sqliteinterceptsROLLBACK TO SAVEPOINTand ends the whole transaction. Use SQLCipher (sqlx4k-sqlite-cipher) if you need savepoints on Android.
For complete working examples, see examples/sqlite.