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?
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?
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.
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!
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.
Exception occured while getting valid activeSessionandroid.database.sqlite.SQLiteException: file is not a database (code 26): , while compiling: SELECT COUNT(*) FROM sqlite_schema;
It sounds like you were also trying to migrate from the deprecated library android-database-sqlcipher to sqlcipher-android. We have some online documentation about migrating the API’s here. Please keep in mind that you will still need to take into account the adjustments I previously mentioned when moving to SQLCipher 4 regardless of the client library you use.
Yes, for us we were trying to migrate from the deprecated library & also referred the link guided by you as per SQLCipher for Android Migration - How to Convert Applications from android-database-sqlcipher to sqlcipher-android | Zetetic. We thought of using the latest version i.e. 4.5.6@aar for bin up to date, but it only provides the underlying SQLiteConnection object for creation of the SQLiteDatabaseHook. That’s where we got stuck searching for the answers. Can you please guide if there’s any possible way we can achieve the migration to 4.x using 4.5.6@aar? or its limited to 4.5.4@aar only?
Thanks
The android-database-sqlcipher was deprecated last year and thus the latest Community edition of that library is 4.5.4. Moving forward, the sqlcipher-android is the long-term replacement library and is available with the current SQLCipher release of 4.5.6. Portions of the API and package names have changed when migrating from android-database-sqlcipher to sqlcipher-android, we recommend reviewing the migration guide. The SQLiteConnection can perform the same type of operations within a SQLiteDatabaseHook using the sqlcipher-android library that you would perform with android-database-sqlcipher.