13

I have an application with a database, created and opened using the standard SQLiteOpenHelper.

Whenever I upgrade the database version, I also upgrade the application's version code, so there is no way for the database to go down (the database version number is always increased, never decreased).

I disabled database backup in my app by setting the android:allowBackup property to false.

But when I upgrade the app on the Play Store, I get a lot of crash

Can't downgrade database from version n to n-1

96% of those crash occur on Samsung device running . Anyone know why this problem occurs, and more importantly how to prevent this crash ?

I know that I can override the onDowngrade to prevent the crash but I actually don't understand why the onDowngrade is called at all as the crash is called on an app that always use the last version of the database.

Edit : Added code sample, FWIW

My OpenHelper :

public class MyDBHelper extends SQLiteOpenHelper {

    private static final String LOG_TAG = MyDBHelper.class.getName();

    public static final String DB_NAME = "my_db";
    public static final int DB_V1 = 1;
    public static final int DB_V2_UNIQUE_IDS = 2;
    public static final int DB_V3_METADATAS = 3;
    public static final int DB_V4_CORRUPTED_IDS = 4;
    public static final int DB_V5_USAGE_TABLE = 5;

    public static final int DB_VERSION = DB_V5_USAGE_TABLE;

    public MyDBHelper(final Context context, IExceptionLogger logger) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(final SQLiteDatabase db) {
        Debug.log_d(DebugConfig.DEFAULT, LOG_TAG, "onCreate()");
        db.execSQL(createMyTable());
    }

    @Override
    public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
        Debug.log_d(DebugConfig.DEFAULT, LOG_TAG, "onUpgrade(): oldVersion = " + oldVersion + " : newVersion = " + newVersion);

        if (oldVersion < 2) {
            Debug.log_d(DebugConfig.DEFAULT, LOG_TAG, "onUpgrade(): upgrading version 1 table to version 2");
            db.execSQL(upgradeTable_v1_to_v2());
        }

        if (oldVersion < 3) {
            Debug.log_d(DebugConfig.DEFAULT, LOG_TAG, "onUpgrade(): upgrading version 2 Entry table to version 3");
            db.execSQL(upgradeTable_v2_to_v3());
        }
    }

    @Override
    @TargetApi(Build.VERSION_CODES.FROYO)
    public void onDowngrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
        Debug.log_d(DebugConfig.DEFAULT, LOG_TAG, "onDowngrade(): oldVersion = " + oldVersion + " : newVersion = " + newVersion);
        super.onDowngrade(db, oldVersion, newVersion);
    }
}

And how I initialize it :

public class DatabaseController {

    private MyDBHelper mDBHelper;

    public void initialize(final Context context) {

       mDBHelper = new MyDBHelper(context);

    }
}
XGouchet
  • 10,002
  • 10
  • 48
  • 83
  • are you able to see if those crashes come from rooted devices? on rooted devices users can downgrade apps if they choose to – marmor Apr 04 '17 at 07:08
  • 99.9% of the device are not rooted. – XGouchet Apr 04 '17 at 07:12
  • 1
    are you maybe using the `SQLiteOpenHelper` with a version parameter somewhere in your project? If you're overriding `SQLiteOpenHelper`, make sure you're calling super in the constructor with the latest version – marmor Apr 04 '17 at 07:21
  • Nope, the SQLiteOpen helper uses a static final constant int as it's version argument. Besides, if it was the case it would crash on all kind of devices and not just on Samsungs – XGouchet Apr 04 '17 at 07:22
  • post your SQLiteOpenHelper class constructors, and onUpgrade methods, also, post some code on how you're using this class in some other code. – marmor Apr 04 '17 at 07:24
  • here `mDBHelper = new DatabaseHelper(context)` you meant `mDBHelper = new MyDBHelper(context)` ? – marmor Apr 04 '17 at 07:41
  • Yes sorry... meant to anonimize / cleanup my production code but missed one – XGouchet Apr 04 '17 at 07:42
  • What are the reported `n`/`n-1` values? Are they plausible? – CL. Apr 04 '17 at 09:27
  • They are reported as downgrading from version 5 to 4, so basically downgrading from the current expected version to the previous version – XGouchet Apr 04 '17 at 10:23
  • 1
    You forgot to add the stacktrace for those crashes. This would have been a bit more obvious then. – AxelH Apr 18 '17 at 08:37

3 Answers3

9

This is the default implementation of SQLiteOpenHelper.onDowngrade(...):

public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    throw new SQLiteException("Can't downgrade database from version " +
            oldVersion + " to " + newVersion);
}

As you see if you call super.onDowngrade(...), as you do, you'll get that exception. You need to implement onDowngrade yourself, without calling super.onDowngrade. It should always be implemented for the sake of completeness, as there's no guarantee when it might be called - it sounds strange that the user has changed to use an older version of the app but there might be a situation like that. Do you know what version of the app the exceptions come from?

etan
  • 573
  • 5
  • 14
  • This was so obvious that I didn't even notice the call to the super method... Note : The question at the end is not necessary, to downgrade a DB (for any reason), this need to be implemented, same for the upgrade. – AxelH Apr 18 '17 at 08:23
  • If you had read my question, I said that I know I can override the onDowngrade method. My question was about why the onDowngrade was called for absolutely no reason ! – XGouchet Apr 19 '17 at 06:35
  • Can you see what app version those crashes come from? That might give you some hint - if it's the latest version, the system is doing something strange. – etan Apr 19 '17 at 07:39
  • Seems that [you're not the only one](http://stackoverflow.com/questions/19786988/sqlite-on-downgrade) who's getting a strange downgrade to an earlier database version on Samsung devices only. – etan Apr 19 '17 at 07:42
0

Your comment in the @etan answer:

why the onDowngrade was called for absolutely no reason?

There is absolute reason,

public static final int DB_V5_USAGE_TABLE = 5;

public static final int DB_VERSION = DB_V5_USAGE_TABLE;

your DB_VERSION holds 5 and in your constructor, you are passing that value. Obviously argument for version should be greater than the previous version otherwise you will get this message.

As @etan expressed, if you need to downgrade the version you need to properly override the onDowngrade method instead throwing the error again.

You may be knew this, so please try to remember your previous version or try to pass 6 or greater for database version parameter.

Blasanka
  • 21,001
  • 12
  • 102
  • 104
0

You can solve this problem by remove "super.onDowngrade(db, oldVersion, newVersion);" line or comment it

@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  //super.onDowngrade(db, oldVersion, newVersion);
  Log.w(TAG, "Downgrade database from version " + oldVersion + " to " + newVersion);
}
Jong
  • 1
  • 1