Reputation: 111
I am working on my first migration of a Room database version. For this i have choosen the "manual" migration. In my table i added 1 field/column at the end and an index.
Then in my migration function i added this
private val migration2To3: Migration = object: Migration(2,3) {
override fun migrate(database: SupportSQLiteDatabase) {
Log.i("database", "migration from $startVersion to $endVersion started")
database.execSQL("ALTER TABLE 'gps' ADD COLUMN 'isTrack' BOOLEAN NOT NULL DEFAULT false")
Log.i("database", "TABLE gps altered. 1 new column added")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_gps_tripId` ON `gps` (`tripId`)")
Log.i("database", "TABLE gps index on 'tripId' added")
Log.i("database", "migration from $startVersion to $endVersion ended")
}
}
E/AndroidRuntime: FATAL EXCEPTION: arch_disk_io_0 Process: com.example.tripmemory, PID: 15190 java.lang.RuntimeException: Exception while computing database live data. at androidx.room.RoomTrackingLiveData.refreshRunnable$lambda$0(RoomTrackingLiveData.kt:74) at androidx.room.RoomTrackingLiveData.$r8$lambda$PhMGW5zFk4QWazERd2lfEeLZqW0(Unknown Source:0) at androidx.room.RoomTrackingLiveData$$ExternalSyntheticLambda0.run(Unknown Source:2) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637) at java.lang.Thread.run(Thread.java:1012) Caused by: java.lang.IllegalStateException: Migration didn't properly handle: gps(com.example.tripmemory.objects.GPSDataObject). Expected: TableInfo{name='gps', columns={altitude=Column{name='altitude', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, isTrack=Column{name='isTrack', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, datetime=Column{name='datetime', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, latitude=Column{name='latitude', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, icon=Column{name='icon', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, description=Column{name='description', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}, tripId=Column{name='tripId', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='undefined'}, memoryId=Column{name='memoryId', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, longitude=Column{name='longitude', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}}, foreignKeys=[ForeignKey{referenceTable='memory', onDelete='CASCADE +', onUpdate='NO ACTION', columnNames=[memoryId], referenceColumnNames=[id]}, ForeignKey{referenceTable='trip', onDelete='CASCADE +', onUpdate='NO ACTION', columnNames=[tripId], referenceColumnNames=[id]}], indices=[Index{name='index_gps_memoryId', unique=false, columns=[memoryId], orders=[ASC]'}, Index{name='index_gps_tripId', unique=false, columns=[tripId], orders=[ASC]'}]} Found: E/AndroidRuntime: TableInfo{name='gps', columns={id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='undefined'}, tripId=Column{name='tripId', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, memoryId=Column{name='memoryId', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, longitude=Column{name='longitude', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, latitude=Column{name='latitude', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, altitude=Column{name='altitude', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, datetime=Column{name='datetime', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, description=Column{name='description', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}, icon=Column{name='icon', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='undefined'}, isTrack=Column{name='isTrack', type='BOOLEAN', affinity='1', notNull=true, primaryKeyPosition=0, defaultValue='false'}}, foreignKeys=[ForeignKey{referenceTable='memory', onDelete='CASCADE +', onUpdate='NO ACTION', columnNames=[memoryId], referenceColumnNames=[id]}], indices=[Index{name='index_gps_tripId', unique=false, columns=[tripId], orders=[ASC]'}, Index{name='index_gps_memoryId', unique=false, columns=[memoryId], orders=[ASC]'}]} at androidx.room.RoomOpenHelper.onUpgrade(RoomOpenHelper.kt:94) at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onUpgrade(FrameworkSQLiteOpenHelper.kt:252) at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:416) at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316) at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableOrReadableDatabase(FrameworkSQLiteOpenHelper.kt:231) at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.innerGetDatabase(FrameworkSQLiteOpenHelper.kt:189) at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getSupportDatabase(FrameworkSQLiteOpenHelper.kt:151) at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.kt:104) at androidx.room.RoomDatabase.inTransaction(RoomDatabase.kt:629) at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.kt:448) at androidx.room.RoomDatabase.query(RoomDatabase.kt:477) at androidx.room.util.DBUtil.query(DBUtil.kt:75) at com.example.tripmemory.dataabase.TripMemoryDao_Impl$23.call(TripMemoryDao_Impl.java:493) at com.example.tripmemory.dataabase.TripMemoryDao_Impl$23.call(TripMemoryDao_Impl.java:490) at androidx.room.RoomTrackingLiveData.refreshRunnable$lambda$0(RoomTrackingLiveData.kt:72) ... 5 more
What is strange is column order of the expected table is completly scrambled. I did not change anything in the order of the fields. when i look at the schema export of the two version (before and after) it also there has the same field order in both version just with the new extra field at the end in the newest version.
So what am i doing wrong. Only thing i can think of to fix it is to use "fallbackToDestructiveMigration" which is an option for now as the app is not live but when it is that is not an option.
Upvotes: 1
Views: 972
Reputation: 57043
What is strange is column order of the expected table is completly scrambled.
The order of the columns is irrelevant. Your issue is the use of the column type BOOLEAN and also the use of a DEFAULT value.
The type should instead be INTEGER.
So you want to use
database.execSQL("ALTER TABLE 'gps' ADD COLUMN 'isTrack' INTEGER NOT NULL DEFAULT false")
However, you then have the issue that if the NOT NULL constraint is used, you MUST have a default value when using the ADD COLUMN. To indicate to Room that you are using a DEFAULT value then you need to use defaultValue
parameter of the @ColumnInfo
annotation for the member variable/field specifying false
as the value.
The issues can be seen by looking at the isTrack column in the expected and found. The result showing a difference in the affinity, type and default value. As per:-
isTrack=Column{name='isTrack', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='undefined'},
isTrack=Column{name='isTrack', type='BOOLEAN', affinity='1', notNull=true, primaryKeyPosition=0, defaultValue='false'
Upvotes: 0