Migrations

August 23, 2026 · View on GitHub

Bump @Database(version = …) and register migration classes. They run on open when the on-disk version is lower.

@Database(
    version = 2,
    tables = [User::class],
    migrations = [AddEmailMigration::class],
)
abstract class AppDatabase : DBFlowDatabase<AppDatabase>()

@Migration(version = 2, priority = 1)
class AddEmailMigration : Migration {
    override suspend fun MigrationScope.migrate() {
        if (!(migrationAdapter("User") hasColumn "email")) {
            (alterTable("User") addColumn ColumnAlter.Plain(
                name = "email",
                type = SQLiteType.TEXT,
            )).execute()
        }
    }
}

priority is lowest-first when several migrations share a version.

SQLite can ALTER TABLE … RENAME and ADD COLUMN. For anything else, create a new table, copy, drop, rename.

Alter helpers

alterTable("User") addColumn ColumnAlter.Plain("email", SQLiteType.TEXT)
alterTable("User") dropColumn "legacy"
alterTable("User") renameTo "Person"

MigrationScope is the only safe API during upgrade. Do not open the same database recursively from a migration.

SQL files

Place statements in:

assets/migrations/{DatabaseName}/{version}.sql

Example: assets/migrations/AppDatabase/2.sql

Each statement ends with ;. -- comments must be on their own line.

Version 0

@Migration(version = 0) runs only when the database is created, not on later upgrades.

Prepackaged files

Ship a starter .db in assets and point the open helper at it (see tests under PrepackagedDB). Run migrations after copy if version is higher than the packaged file.