Duplicate column name on SQLite upgrade for a new column

蓝咒 提交于 2020-07-20 10:13:31

问题


I've been getting an inconsistent, non-reproducible crash upon users upgrading to a new version of the local SQLite DB when updating the app.

Fatal Exception: android.database.sqlite.SQLiteException: duplicate column name: upload_pending (code 1): , while compiling: ALTER TABLE purchases ADD COLUMN upload_pending TINYINT DEFAULT 0
#################################################################
Error Code : 1 (SQLITE_ERROR)
Caused By : SQL(query) error or missing database.
    (duplicate column name: upload_pending (code 1): , while compiling: ALTER TABLE purchases ADD COLUMN upload_pending TINYINT DEFAULT 0)
#################################################################

The column is new to this version of the app, which tells me the most likely bug is that SQLiteOpenHelper's onUpgrade method is being called twice. Here is the logic for how upgrade is handled:

@Override   
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    for(int currentUpgrade = oldVersion + 1; currentUpgrade <= newVersion; currentUpgrade++) {
        switch(currentUpgrade) {
            case 2: 
                //upgrade to db v2
                break;
            case 3:
                //upgrade to db v3
                break;
            //etc
            case 7:
                methodWhichUpdatesAnotherTable(db);
                db.execSQL("ALTER TABLE " + Purchases.TABLE_NAME
                            + " ADD COLUMN " + Purchases.UPLOAD_PENDING + " TINYINT DEFAULT 0");
                break;
        }
    }
}

EDIT: I've updated the code to include something important. The failing line is NOT the first ALTER statement in the upgrade. First, a method is called which makes two different alter statements (on a different table), and this portion works fine. This seems to eliminate the possibility that it's a concurrency issue, because if so these would be the first to fail.

Looking at this, the only way I can see this happening is if Android is calling onUpgrade twice, and not adjusting the oldVersion and newVersion params, resulting in case 7 being called twice. Conversely, it could be the case where onCreate is called, and onUpgrade is called afterwards, again with the database versions provided to the method not being correct.

As I mentioned at the start, I'm unable to reproduce this error, and it's only happening to <1% of users, but it is noticeable enough for me to want to solve it. If anyone has a guess I'd appreciate it, and if you need more info feel free to ask. Thanks!


回答1:


Had the same issue. Turned out, that I was creating the table for oldVersions before 4 and adding column for versions before 10. Because of that, both statements, CREATE TABLE and ALTER TABLE ADD COLUMN were executed for oldVersions pre 4:

if ( oldVersion < 4 && newVersion >= 4 ) { 
    TestTableDao.createTable( db, true );
}

if ( oldVersion < 10 && newVersion >= 10 ) { 
   db.execSQL( "ALTER TABLE TEXT_TABLE ADD COLUMN NEW_COLUMN TEXT" );
}

The fix was to check oldVersion for being after creation of table:

 if ( oldVersion < 10 && newVersion >= 10 ) { 
   if( oldVersion >=4 )
       db.execSQL( "ALTER TABLE TEXT_TABLE ADD COLUMN NEW_COLUMN TEXT" );
}

Hope it helps to someone.



来源:https://stackoverflow.com/questions/43356823/duplicate-column-name-on-sqlite-upgrade-for-a-new-column

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!