androidsqliteandroid-roomdatabase-migrationillegalstateexception

Room don't work migration when I try to add new column in table


After adding new migration I have the next exception. What didn't I do? When I debuging, migration was done. The new column is text and I try to write all data in one cell.

java.lang.IllegalStateException: Pre-packaged database has an invalid schema: library(com.tnco.runar.model.LibraryItemsModel).
 Expected:
TableInfo{name='library', columns={audio_duration=Column{name='audio_duration', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, rune_tags=Column{name='rune_tags', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, image_url=Column{name='image_url', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, link_url=Column{name='link_url', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, link_title=Column{name='link_title', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, audio_url=Column{name='audio_url', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1, defaultValue='null'}, title=Column{name='title', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, type=Column{name='type', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, childs=Column{name='childs', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, sort_order=Column{name='sort_order', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, content=Column{name='content', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
 Found:
TableInfo{name='library', columns={audio_duration=Column{name='audio_duration', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, image_url=Column{name='image_url', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, link_url=Column{name='link_url', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, link_title=Column{name='link_title', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, audio_url=Column{name='audio_url', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1, defaultValue='null'}, title=Column{name='title', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, type=Column{name='type', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, childs=Column{name='childs', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, sort_order=Column{name='sort_order', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, content=Column{name='content', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}

Expected: rune_tags=Column BUT didn't found

My DataBase class:

@Database(entities = [LibraryItemsModel::class, ...],
version = 4,
exportSchema = false)
abstract class AppDB : RoomDatabase() {
abstract fun appDAO(): AppDAO

companion object {
    @Volatile
    private lateinit var INSTANCE: AppDB
    fun init(context: Context) {

        var dataBaseFilePath = "database/layouts.db"
       
        INSTANCE = Room.databaseBuilder(context, AppDB::class.java, "EN_DATABASE")
            .createFromAsset(dataBaseFilePath)
            .addMigrations(MIGRATION_2_3, MIGRATION_3_4)
            .build()
    }

    fun getLayoutDB(): AppDB {
        return INSTANCE
    }


    private val MIGRATION_2_3 = object : Migration(2, 3) {
        override fun migrate(database: SupportSQLiteDatabase) {
            database.execSQL("CREATE TABLE runes_generator (id INTEGER PRIMARY KEY NOT NULL, imgUrl TEXT, enTitle TEXT, ruTitle TEXT)")
        }
    }


    private val MIGRATION_3_4 = object : Migration(3, 4) {
        override fun migrate(database: SupportSQLiteDatabase) {
            database.execSQL("ALTER TABLE library ADD COLUMN rune_tags TEXT")
        }
    }

Model class:

@Entity(tableName = "library")
@TypeConverters(LibraryConverter::class)
data class LibraryItemsModel(
@PrimaryKey
var id: String,
var childs: List<String>?,
var content: String?,
var title: String?,
@ColumnInfo(name = "image_url")
var imageUrl: String?,
@ColumnInfo(name = "sort_order")
var sortOrder: Int?,
var type: String?,
@ColumnInfo(name = "link_title")
var linkTitle: String?,
@ColumnInfo(name = "link_url")
var linkUrl: String?,
@ColumnInfo(name = "audio_url")
var audioUrl: String?,
@ColumnInfo(name = "audio_duration")
var audioDuration: Int?,
@ColumnInfo(name = "rune_tags")
var runeTags: List<String>?
) {
 constructor() : this(
    "null",
    listOf(),
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    null,
    listOf()
)
}

I added @ColumnInfo(name = "rune_tags") var runeTags: List?

Also I have Converter class

class LibraryConverter {

@TypeConverter
fun fromList(items: List<String>?): String? {
    return items?.stream()?.collect(Collectors.joining("|"))
}

@TypeConverter
fun toList(data: String?): List<String> {
    return data?.split("|") ?: listOf()
}

}


Solution

  • Your issue is that the pre-packaged database (the asset) does not include the schema change (and if I recall correctly is not at version 3) AND that the database doesn't exist on the device (so the database is being copied from the asset/prepackaged database).

    Thus the pre-packaged database is being copied, it's version is then set to, or already at version 4 so no migration is undertaken and thus the resultant message:-

    Pre-packaged database ....

    To cater for new installs you need/should to have the pre-packaged database using the current schema.

    Or you could use the PrePackagedDatabaseCallback to alter the table. You may find How do I use Room's prepackagedDatabaseCallback? useful.