The`androidx.sqlite`library contains abstract interfaces along with basic implementations which can be used to build your own libraries that access SQLite. You might want to consider using the[Room](https://developer.android.com/kotlin/multiplatform/room)library, which provides an abstraction layer over SQLite to allow for more robust database access while harnessing the full power of SQLite.

## Set up dependencies

To setup SQLite in your KMP project, add the dependencies for the artifacts in the`build.gradle.kts`file for your module:
**Note:** `androidx.sqlite`supports KMP in version 2.5.0 or higher.  

    [versions]
    sqlite = "2.6.2"

    [libraries]
    # The SQLite Driver interfaces
    androidx-sqlite = { module = "androidx.sqlite:sqlite", version.ref = "sqlite" }

    # The bundled SQLite driver implementation
    androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "sqlite" }

    [plugins]
    ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }

## SQLite Driver APIs

The`androidx.sqlite`library groups offer low-level APIs for communicating with the SQLite library either included in the library when using`androidx.sqlite:sqlite-bundled`or in the host platform, such as Android or iOS when using`androidx.sqlite:sqlite-framework`. The APIs closely follow the core functionality of SQLite C API.

There are 3 main interfaces:

- [`SQLiteDriver`](https://developer.android.com/reference/kotlin/androidx/sqlite/SQLiteDriver)- It is the entry point to use SQLite and is responsible for opening database connections.
- [`SQLiteConnection`](https://developer.android.com/reference/kotlin/androidx/sqlite/SQLiteConnection)- Is the representation of the`sqlite3`object.
- [`SQLiteStatement`](https://developer.android.com/reference/kotlin/androidx/sqlite/SQLiteStatement)- Is the representation of the`sqlite3_stmt`object.

The following example showcases the core APIs:  

    fun main() {
      val databaseConnection = BundledSQLiteDriver().open("todos.db")
      databaseConnection.execSQL(
        "CREATE TABLE IF NOT EXISTS Todo (id INTEGER PRIMARY KEY, content TEXT)"
      )
      databaseConnection.prepare(
        "INSERT OR IGNORE INTO Todo (id, content) VALUES (? ,?)"
      ).use { stmt ->
        stmt.bindInt(index = 1, value = 1)
        stmt.bindText(index = 2, value = "Try Room in the KMP project.")
        stmt.step()
      }
      databaseConnection.prepare("SELECT content FROM Todo").use { stmt ->
        while (stmt.step()) {
          println("Action item: ${stmt.getText(0)}")
        }
      }
      databaseConnection.close()
    }

Similar to SQLite C APIs the common usage is to:

- Open a database connection using the instantiated`SQLiteDriver`implementation.
- Prepare a SQL statement using`SQLiteConnection.prepare()`
- Execute a`SQLiteStatement`in the following way:
  1. Optionally bind arguments using the`bind*()`functions.
  2. Iterate over the result set using the`step()`function.
  3. Read columns from the result set using the`get*()`functions.

| **Caution:** Database connections and statements are resources that need to be managed. All prepared statements should be closed once they are no longer needed. Similarly if a database connection is no longer needed, then it should be closed.

### Driver Implementations

The following table summarizes the available driver implementations:

|-----------------------|------------------------------------|--------------------------------------------|
| Class Name            | Artifact                           | Supported Platforms                        |
| `AndroidSQLiteDriver` | `androidx.sqlite:sqlite-framework` | Android                                    |
| `NativeSQLiteDriver`  | `androidx.sqlite:sqlite-framework` | iOS, Mac, and Linux                        |
| `BundledSQLiteDriver` | `androidx.sqlite:sqlite-bundled`   | Android, iOS, Mac, Linux and JVM (Desktop) |

The recommended implementation to use is`BundledSQLiteDriver`available in`androidx.sqlite:sqlite-bundled`. It includes the SQLite library compiled from source, offering the most up-to-date version and consistency across all the supported KMP platforms.

### SQLite Driver and Room

The driver APIs are useful for low-level interactions with an SQLite database. For a feature rich library that provides a more robust access of SQLite then Room is recommended.

A`RoomDatabase`relies on a`SQLiteDriver`to perform database operations and an implementation is required to be configured using[`RoomDatabase.Builder.setDriver()`](https://developer.android.com/reference/kotlin/androidx/room/RoomDatabase#setDriver(androidx.sqlite.SQLiteDriver)). Room provides[`RoomDatabase.useReaderConnection`](https://developer.android.com/reference/kotlin/androidx/room/package-summary#(androidx.room.RoomDatabase).useReaderConnection(kotlin.coroutines.SuspendFunction1))and[`RoomDatabase.useWriterConnection`](https://developer.android.com/reference/kotlin/androidx/room/package-summary#(androidx.room.RoomDatabase).useWriterConnection(kotlin.coroutines.SuspendFunction1))for more direct access to the managed database connections.

## Migrate to Kotlin Multiplatform

Any usage of low-level Support SQLite API components (such as the`SupportSQLiteDatabase`interface) needs to be migrated to the equivalent SQLite Driver components.  

### Kotlin Multiplatform

Perform a transaction using low-level`SQLiteConnection`
**Note:** It is always recommended to use`RoomDatabase`transaction APIs as those allow nested transactions and are safer to use than the APIs available in`androidx.sqlite`.  

    val connection: SQLiteConnection = ...
    connection.execSQL("BEGIN IMMEDIATE TRANSACTION")
    try {
      // perform database operations in transaction
      connection.execSQL("END TRANSACTION")
    } catch(t: Throwable) {
      connection.execSQL("ROLLBACK TRANSACTION")
    }

Execute a query with no result  

    val connection: SQLiteConnection = ...
    connection.execSQL("ALTER TABLE ...")

Execute a query with result but no arguments  

    val connection: SQLiteConnection = ...
    connection.prepare("SELECT * FROM Pet").use { statement ->
      while (statement.step()) {
        // read columns
        statement.getInt(0)
        statement.getText(1)
      }
    }

Execute a query with result and arguments  

    connection.prepare("SELECT * FROM Pet WHERE id = ?").use { statement ->
      statement.bindInt(1, id)
      if (statement.step()) {
        // row found, read columns
      } else {
        // row not found
      }
    }

### Android-only

Perform a transaction using`SupportSQLiteDatabase`
**Note:** It is always recommended to use`RoomDatabase`transaction APIs as those allow nested transactions and are safer to use than the APIs available in`androidx.sqlite`.  

    val database: SupportSQLiteDatabase = ...
    database.beginTransaction()
    try {
      // perform database operations in transaction
      database.setTransactionSuccessful()
    } finally {
      database.endTransaction()
    }

Execute a query with no result  

    val database: SupportSQLiteDatabase = ...
    database.execSQL("ALTER TABLE ...")

Execute a query with result but no arguments  

    val database: SupportSQLiteDatabase = ...
    database.query("SELECT * FROM Pet").use { cursor ->
      while (cusor.moveToNext()) {
        // read columns
        cursor.getInt(0)
        cursor.getString(1)
      }
    }

Execute a query with result and arguments  

    database.query("SELECT * FROM Pet WHERE id = ?", id).use { cursor ->
      if (cursor.moveToNext()) {
        // row found, read columns
      } else {
        // row not found
      }
    }