diff --git a/app/src/main/java/org/fdroid/fdroid/data/RepoPersister.java b/app/src/main/java/org/fdroid/fdroid/data/RepoPersister.java index 2138f8575..8f5d2e037 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/RepoPersister.java +++ b/app/src/main/java/org/fdroid/fdroid/data/RepoPersister.java @@ -91,9 +91,8 @@ public class RepoPersister { // end of the process. This is due to the fact that we can't verify the cert // the index was signed with until we've finished reading it - and we don't // want to put stuff in the real database until we are sure it is from a - // trusted source. + // trusted source. It also helps performance as it is done via an in-memory database. TempAppProvider.Helper.init(context); - TempApkProvider.Helper.init(context); hasBeenInitialized = true; } diff --git a/app/src/main/java/org/fdroid/fdroid/data/TempApkProvider.java b/app/src/main/java/org/fdroid/fdroid/data/TempApkProvider.java index 831b231ba..19b4d277d 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/TempApkProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/TempApkProvider.java @@ -68,12 +68,16 @@ public class TempApkProvider extends ApkProvider { /** * Deletes the old temporary table (if it exists). Then creates a new temporary apk provider * table and populates it with all the data from the real apk provider table. + * + * This is package local because it must be invoked after + * {@link org.fdroid.fdroid.data.TempAppProvider.Helper#init(Context)}. Due to this + * dependence, that method invokes this one itself, rather than leaving it to the + * {@link RepoPersister}. */ - public static void init(Context context) { + static void init(Context context) { Uri uri = Uri.withAppendedPath(getContentUri(), PATH_INIT); context.getContentResolver().insert(uri, new ContentValues()); } - } @Override @@ -123,11 +127,11 @@ public class TempApkProvider extends ApkProvider { private void initTable() { final SQLiteDatabase db = db(); - db.execSQL("DROP TABLE IF EXISTS " + getTableName()); - db.execSQL("CREATE TABLE " + getTableName() + " AS SELECT * FROM " + DBHelper.TABLE_APK); - db.execSQL("CREATE INDEX IF NOT EXISTS apk_vercode on " + getTableName() + " (vercode);"); - db.execSQL("CREATE INDEX IF NOT EXISTS apk_id on " + getTableName() + " (id);"); - db.execSQL("CREATE INDEX IF NOT EXISTS apk_compatible ON " + getTableName() + " (compatible);"); + final String memoryDbName = TempAppProvider.DB; + db.execSQL("CREATE TABLE " + memoryDbName + "." + getTableName() + " AS SELECT * FROM main." + DBHelper.TABLE_APK); + db.execSQL("CREATE INDEX IF NOT EXISTS " + memoryDbName + ".apk_vercode on " + getTableName() + " (vercode);"); + db.execSQL("CREATE INDEX IF NOT EXISTS " + memoryDbName + ".apk_id on " + getTableName() + " (id);"); + db.execSQL("CREATE INDEX IF NOT EXISTS " + memoryDbName + ".apk_compatible ON " + getTableName() + " (compatible);"); } } diff --git a/app/src/main/java/org/fdroid/fdroid/data/TempAppProvider.java b/app/src/main/java/org/fdroid/fdroid/data/TempAppProvider.java index 81a0737ff..86ac6bc59 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/TempAppProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/TempAppProvider.java @@ -3,8 +3,10 @@ package org.fdroid.fdroid.data; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; +import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; +import android.text.TextUtils; import android.util.Log; import org.fdroid.fdroid.Utils; @@ -16,6 +18,11 @@ public class TempAppProvider extends AppProvider { private static final String TAG = "TempAppProvider"; + /** + * The name of the in memory database used for updating. + */ + static final String DB = "temp_update_db"; + private static final String PROVIDER_NAME = "TempAppProvider"; private static final String TABLE_TEMP_APP = "temp_" + DBHelper.TABLE_APP; @@ -60,6 +67,7 @@ public class TempAppProvider extends AppProvider { public static void init(Context context) { Uri uri = Uri.withAppendedPath(getContentUri(), PATH_INIT); context.getContentResolver().insert(uri, new ContentValues()); + TempApkProvider.Helper.init(context); } /** @@ -111,13 +119,30 @@ public class TempAppProvider extends AppProvider { return count; } + private void ensureTempTableDetached(SQLiteDatabase db) { + Cursor cursor = db.rawQuery("PRAGMA database_list", null); + try { + cursor.moveToFirst(); + while (!cursor.isAfterLast()) { + String name = cursor.getString(cursor.getColumnIndex("name")); + if (TextUtils.equals(name, DB)) { + db.execSQL("DETACH DATABASE " + DB); + } + cursor.moveToNext(); + } + } finally { + cursor.close(); + } + } + private void initTable() { final SQLiteDatabase db = db(); - db.execSQL("DROP TABLE IF EXISTS " + getTableName()); - db.execSQL("CREATE TABLE " + getTableName() + " AS SELECT * FROM " + DBHelper.TABLE_APP); - db.execSQL("CREATE INDEX IF NOT EXISTS app_id ON " + getTableName() + " (id);"); - db.execSQL("CREATE INDEX IF NOT EXISTS app_upstreamVercode ON " + getTableName() + " (upstreamVercode);"); - db.execSQL("CREATE INDEX IF NOT EXISTS app_compatible ON " + getTableName() + " (compatible);"); + ensureTempTableDetached(db); + db.execSQL("ATTACH DATABASE ':memory:' AS " + DB); + db.execSQL("CREATE TABLE " + DB + "." + getTableName() + " AS SELECT * FROM main." + DBHelper.TABLE_APP); + db.execSQL("CREATE INDEX IF NOT EXISTS " + DB + ".app_id ON " + getTableName() + " (id);"); + db.execSQL("CREATE INDEX IF NOT EXISTS " + DB + ".app_upstreamVercode ON " + getTableName() + " (upstreamVercode);"); + db.execSQL("CREATE INDEX IF NOT EXISTS " + DB + ".app_compatible ON " + getTableName() + " (compatible);"); } private void commitTable() { @@ -125,21 +150,22 @@ public class TempAppProvider extends AppProvider { try { db.beginTransaction(); - Log.i(TAG, "Renaming " + TABLE_TEMP_APP + " to " + DBHelper.TABLE_APP); - db.execSQL("DROP TABLE " + DBHelper.TABLE_APP); - db.execSQL("ALTER TABLE " + TABLE_TEMP_APP + " RENAME TO " + DBHelper.TABLE_APP); + final String tempApp = DB + "." + TempAppProvider.TABLE_TEMP_APP; + final String tempApk = DB + "." + TempApkProvider.TABLE_TEMP_APK; - Log.i(TAG, "Renaming " + TempApkProvider.TABLE_TEMP_APK + " to " + DBHelper.TABLE_APK); - db.execSQL("DROP TABLE " + DBHelper.TABLE_APK); - db.execSQL("ALTER TABLE " + TempApkProvider.TABLE_TEMP_APK + " RENAME TO " + DBHelper.TABLE_APK); + db.execSQL("DELETE FROM " + DBHelper.TABLE_APP + " WHERE 1"); + db.execSQL("INSERT INTO " + DBHelper.TABLE_APP + " SELECT * FROM " + tempApp); + + db.execSQL("DELETE FROM " + DBHelper.TABLE_APK + " WHERE 1"); + db.execSQL("INSERT INTO " + DBHelper.TABLE_APK + " SELECT * FROM " + tempApk); - Utils.debugLog(TAG, "Successfully renamed both tables, will commit transaction"); db.setTransactionSuccessful(); getContext().getContentResolver().notifyChange(AppProvider.getContentUri(), null); getContext().getContentResolver().notifyChange(ApkProvider.getContentUri(), null); } finally { db.endTransaction(); + db.execSQL("DETACH DATABASE " + DB); // Can't be done in a transaction. } } }