Migrating from SQLCipher 3.5.1 to 4.1.3 in Android

I have a database created with SQLCipher 3.5.1. I tried migrating it to SQLCipher 4.1.3 using PRAGMA cipher_migrate according to the guidelines, but even though the database opens immediately after the openDatabase() call, it does not actually migrate.

You can find the code I used below:

import android.content.Context;

import net.sqlcipher.Cursor;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook;

public class DatabaseMigrator {

    static public void checkAndMigrateDatabase(Context context, String databaseName, String password) {

        String path = context.getDatabasePath(databaseName).getPath();

        SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
            @Override
            public void postKey(SQLiteDatabase database) {

                Cursor c = database.rawQuery("PRAGMA cipher_migrate", null);

                Boolean migrationOccurred = false;

                if (c.getCount() == 1) {
                    c.moveToFirst();
                    String selection = c.getString(0);

                    migrationOccurred = selection.equals("0");

                    print("selection: " + selection);
                }

                c.close();

                print("migrationOccurred: " + migrationOccurred);
            }

            @Override
            public void preKey(SQLiteDatabase database) { }
        };

        SQLiteDatabase database = null;

        try {
            database = SQLiteDatabase.openDatabase(path, password, null, 0, hook);
        }
        catch (Exception e) {
            print("Exception while trying to open db: " + e);
        }

        if (database != null) {

            database.close();
        }
    }
}

Also, although the guidelines state that the value of the PRAGMA cipher_migrate SQL result should be 0 after the migration, in my case it’s always 1.

Next time I try to open the DB (on the next app launch) without the PRAGMA cipher_migrate command, I get the “not a database” exception and upon opening the db using the DB Browser for SQLite app, I still have to use the SQLiteCipher 3 defaults, which indicates that the migration never happened.

On top of that, I managed to successfully open the db using PRAGMA cipher_compatibility=3, so it looks like there’s an issue during the migration process.

Does anyone have any hints on how PRAGMA cipher_migrate can be performed successfully so it’s not needed every time the app launches?

1 Like

Hi @petrakeas

We have a large set of tests that cover various migration scenarios in SQLCipher here. Would you try running the SQLCipher for Android test suite, possibly modifying the Android PRAGMA cipher_migrate test to utilize your database file and report back?

Hi Nick,

After checking out the tests, it seems that for the migration to succeed we either have to use the openOrCreateDatabase() method, or the openDatabase() one passing the CREATE_IF_NECESSARY flag instead of the OPEN_READWRITE (0) one.

If we use openDatabase with the OPEN_READWRITE (0) flag, the problem that I described can be reproduced even in the SQLCipher Android Tests repo.

Is this the expected behavior?

Hello @petrakeas,

When performing a PRAGMA cipher_migrate command, SQLCipher will internally attach a migration database temporarily to move/rewrite the content to the latest format. The ATTACH logic will utilize the flags provided to open the initial database instance for the attached database. Thus, in your case you would need to open your database calling openDatabase, but passing SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY as the flags value for migration. Would you give that a try and let us know your results? Thanks!

Hi Nick,

If I understand correctly you are proposing what I already described in my previous comment.

Using openDatabase() with CREATE_IF_NECESSARY before passing "PRAGMA cipher_migrate", migrates the DB successfully.

From your answer it seems that CREATE_IF_NECESSARY is needed so that the temporary DB is created.

I think it would be a good idea for the migration guide to be updated, to point that this flag (CREATE_IF_NECESSARY), or that the openOrCreateDatabase() method are required for the migration to succeed.

Hello @petrakeas

Thank you for your follow-up, that is correct. We will review the documentation and update accordingly. Thanks!

2 Likes