Refactor join between installed apps and packages.

This commit is contained in:
Peter Serwylo 2017-06-09 12:05:03 +10:00
parent b729f4dc84
commit bb96cdeff9
9 changed files with 104 additions and 29 deletions

View File

@ -250,7 +250,7 @@ public class AppProvider extends FDroidProvider {
join(
InstalledAppTable.NAME,
"installed",
"installed." + InstalledAppTable.Cols.PACKAGE_NAME + " = " + PackageTable.NAME + "." + PackageTable.Cols.PACKAGE_NAME);
"installed." + InstalledAppTable.Cols.PACKAGE_ID + " = " + PackageTable.NAME + "." + PackageTable.Cols.ROW_ID);
requiresInstalledTable = true;
}
}
@ -270,7 +270,7 @@ public class AppProvider extends FDroidProvider {
leftJoin(
InstalledAppTable.NAME,
"installed",
"installed." + InstalledAppTable.Cols.PACKAGE_NAME + " = " + PackageTable.NAME + "." + PackageTable.Cols.PACKAGE_NAME);
"installed." + InstalledAppTable.Cols.PACKAGE_ID + " = " + PackageTable.NAME + "." + PackageTable.Cols.ROW_ID);
requiresInstalledTable = true;
}
}
@ -959,8 +959,9 @@ public class AppProvider extends FDroidProvider {
* @see #updateSuggestedFromLatest()
*/
private void updateSuggestedFromUpstream() {
Utils.debugLog(TAG, "Calculating suggested versions for all apps which specify an upstream version code.");
Utils.debugLog(TAG, "Calculating suggested versions for all NON-INSTALLED apps which specify an upstream version code.");
Utils.Profiler profiler = new Utils.Profiler("UpdateSuggestedApks");
final String apk = getApkTableName();
final String app = getTableName();
@ -986,6 +987,7 @@ public class AppProvider extends FDroidProvider {
" WHERE " + Cols.UPSTREAM_VERSION_CODE + " > 0 ";
LoggingQuery.execSQL(db(), updateSql);
profiler.log("Done");
}
/**
@ -1029,7 +1031,7 @@ public class AppProvider extends FDroidProvider {
" FROM " + InstalledAppTable.NAME + " AS installed " +
" JOIN " + PackageTable.NAME + " AS pkg ON " +
" (pkg." + PackageTable.Cols.ROW_ID + " = " + appTable + "." + Cols.PACKAGE_ID + " AND " +
" installed." + InstalledAppTable.Cols.PACKAGE_NAME + " = pkg." + PackageTable.Cols.PACKAGE_NAME + ") " +
" installed." + InstalledAppTable.Cols.PACKAGE_ID + " = pkg." + PackageTable.Cols.ROW_ID + ") " +
")";
// Ideally, the check below would actually be written as:

View File

@ -182,7 +182,7 @@ class DBHelper extends SQLiteOpenHelper {
private static final String CREATE_TABLE_INSTALLED_APP = "CREATE TABLE " + InstalledAppTable.NAME
+ " ( "
+ InstalledAppTable.Cols.PACKAGE_NAME + " TEXT NOT NULL PRIMARY KEY, "
+ InstalledAppTable.Cols.PACKAGE_ID + " INT NOT NULL UNIQUE, "
+ InstalledAppTable.Cols.VERSION_CODE + " INT NOT NULL, "
+ InstalledAppTable.Cols.VERSION_NAME + " TEXT NOT NULL, "
+ InstalledAppTable.Cols.APPLICATION_LABEL + " TEXT NOT NULL, "
@ -192,7 +192,7 @@ class DBHelper extends SQLiteOpenHelper {
+ InstalledAppTable.Cols.HASH + " TEXT NOT NULL"
+ " );";
protected static final int DB_VERSION = 70;
protected static final int DB_VERSION = 71;
private final Context context;
@ -277,6 +277,28 @@ class DBHelper extends SQLiteOpenHelper {
recalculatePreferredMetadata(db, oldVersion);
addWhatsNewAndVideo(db, oldVersion);
dropApkPrimaryKey(db, oldVersion);
addIntegerPrimaryKeyToInstalledApps(db, oldVersion);
}
private void addIntegerPrimaryKeyToInstalledApps(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 71) {
return;
}
Log.i(TAG, "Replacing primary key on installed app table with integer for performance.");
db.beginTransaction();
try {
if (tableExists(db, Schema.InstalledAppTable.NAME)) {
db.execSQL("DROP TABLE " + Schema.InstalledAppTable.NAME);
}
db.execSQL(CREATE_TABLE_INSTALLED_APP);
ensureIndexes(db);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
private void dropApkPrimaryKey(SQLiteDatabase db, int oldVersion) {
@ -1061,9 +1083,11 @@ class DBHelper extends SQLiteOpenHelper {
AppPrefsTable.Cols.IGNORE_THIS_UPDATE + ");");
}
Utils.debugLog(TAG, "Ensuring indexes exist for " + InstalledAppTable.NAME);
db.execSQL("CREATE INDEX IF NOT EXISTS installedApp_appId_vercode on " + InstalledAppTable.NAME + " (" +
InstalledAppTable.Cols.PACKAGE_NAME + ", " + InstalledAppTable.Cols.VERSION_CODE + ");");
if (columnExists(db, InstalledAppTable.NAME, InstalledAppTable.Cols.PACKAGE_ID)) {
Utils.debugLog(TAG, "Ensuring indexes exist for " + InstalledAppTable.NAME);
db.execSQL("CREATE INDEX IF NOT EXISTS installedApp_packageId_vercode on " + InstalledAppTable.NAME + " (" +
InstalledAppTable.Cols.PACKAGE_ID + ", " + InstalledAppTable.Cols.VERSION_CODE + ");");
}
Utils.debugLog(TAG, "Ensuring indexes exist for " + RepoTable.NAME);
db.execSQL("CREATE INDEX IF NOT EXISTS repo_id_isSwap on " + RepoTable.NAME + " (" +

View File

@ -24,7 +24,7 @@ public class InstalledApp extends ValueObject {
case Schema.InstalledAppTable.Cols._ID:
id = cursor.getLong(i);
break;
case Schema.InstalledAppTable.Cols.PACKAGE_NAME:
case Schema.InstalledAppTable.Cols.Package.NAME:
packageName = cursor.getString(i);
break;
case Schema.InstalledAppTable.Cols.VERSION_CODE:

View File

@ -10,6 +10,7 @@ import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils;
@ -41,7 +42,7 @@ public class InstalledAppProvider extends FDroidProvider {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
cachedInfo.put(
cursor.getString(cursor.getColumnIndex(Cols.PACKAGE_NAME)),
cursor.getString(cursor.getColumnIndex(Cols.Package.NAME)),
cursor.getLong(cursor.getColumnIndex(Cols.LAST_UPDATE_TIME))
);
cursor.moveToNext();
@ -136,7 +137,17 @@ public class InstalledAppProvider extends FDroidProvider {
}
private QuerySelection queryApp(String packageName) {
return new QuerySelection(Cols.PACKAGE_NAME + " = ?", new String[]{packageName});
return new QuerySelection(Cols.Package.NAME + " = ?", new String[]{packageName});
}
private QuerySelection queryAppSubQuery(String packageName) {
String pkg = Schema.PackageTable.NAME;
String subQuery = "(" +
" SELECT " + pkg + "." + Schema.PackageTable.Cols.ROW_ID +
" FROM " + pkg +
" WHERE " + pkg + "." + Schema.PackageTable.Cols.PACKAGE_NAME + " = ?)";
String query = Cols.PACKAGE_ID + " = " + subQuery;
return new QuerySelection(query, new String[]{packageName});
}
private QuerySelection querySearch(String query) {
@ -144,6 +155,26 @@ public class InstalledAppProvider extends FDroidProvider {
new String[]{"%" + query + "%"});
}
private static class QueryBuilder extends org.fdroid.fdroid.data.QueryBuilder {
@Override
protected String getRequiredTables() {
String pkg = Schema.PackageTable.NAME;
String installed = InstalledAppTable.NAME;
return installed + " JOIN " + pkg +
" ON (" + pkg + "." + Schema.PackageTable.Cols.ROW_ID + " = " +
installed + "." + Cols.PACKAGE_ID + ")";
}
@Override
public void addField(String field) {
if (TextUtils.equals(field, Cols.Package.NAME)) {
appendField(Schema.PackageTable.Cols.PACKAGE_NAME, Schema.PackageTable.NAME, field);
} else {
appendField(field, InstalledAppTable.NAME);
}
}
}
@Override
public Cursor query(Uri uri, String[] projection,
String customSelection, String[] selectionArgs, String sortOrder) {
@ -170,8 +201,15 @@ public class InstalledAppProvider extends FDroidProvider {
throw new UnsupportedOperationException(message);
}
Cursor cursor = db().query(getTableName(), projection,
selection.getSelection(), selection.getArgs(), null, null, sortOrder);
QueryBuilder query = new QueryBuilder();
query.addFields(projection);
if (projection.length == 0) {
query.addField(Cols._ID);
}
query.addSelection(selection);
query.addOrderBy(sortOrder);
Cursor cursor = db().rawQuery(query.toString(), selection.getArgs());
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@ -184,7 +222,7 @@ public class InstalledAppProvider extends FDroidProvider {
}
QuerySelection query = new QuerySelection(where, whereArgs);
query = query.add(queryApp(uri.getLastPathSegment()));
query = query.add(queryAppSubQuery(uri.getLastPathSegment()));
return db().delete(getTableName(), query.getSelection(), query.getArgs());
}
@ -196,9 +234,16 @@ public class InstalledAppProvider extends FDroidProvider {
throw new UnsupportedOperationException("Insert not supported for " + uri + ".");
}
if (values.containsKey(Cols.Package.NAME)) {
String packageName = values.getAsString(Cols.Package.NAME);
long packageId = PackageProvider.Helper.ensureExists(getContext(), packageName);
values.remove(Cols.Package.NAME);
values.put(Cols.PACKAGE_ID, packageId);
}
verifyVersionNameNotNull(values);
db().replaceOrThrow(getTableName(), null, values);
return getAppUri(values.getAsString(Cols.PACKAGE_NAME));
return getAppUri(values.getAsString(Cols.Package.NAME));
}
/**

View File

@ -288,7 +288,7 @@ public class InstalledAppProviderService extends IntentService {
static void insertAppIntoDb(Context context, PackageInfo packageInfo, String hashType, String hash) {
Uri uri = InstalledAppProvider.getContentUri();
ContentValues contentValues = new ContentValues();
contentValues.put(InstalledAppTable.Cols.PACKAGE_NAME, packageInfo.packageName);
contentValues.put(InstalledAppTable.Cols.Package.NAME, packageInfo.packageName);
contentValues.put(InstalledAppTable.Cols.VERSION_CODE, packageInfo.versionCode);
contentValues.put(InstalledAppTable.Cols.VERSION_NAME, packageInfo.versionName);
contentValues.put(InstalledAppTable.Cols.APPLICATION_LABEL,

View File

@ -329,7 +329,7 @@ public interface Schema {
interface Cols {
String _ID = "rowid as _id"; // Required for CursorLoaders
String PACKAGE_NAME = "appId";
String PACKAGE_ID = "packageId";
String VERSION_CODE = "versionCode";
String VERSION_NAME = "versionName";
String APPLICATION_LABEL = "applicationLabel";
@ -338,8 +338,12 @@ public interface Schema {
String HASH_TYPE = "hashType";
String HASH = "hash";
interface Package {
String NAME = "packageName";
}
String[] ALL = {
_ID, PACKAGE_NAME, VERSION_CODE, VERSION_NAME, APPLICATION_LABEL,
_ID, PACKAGE_ID, Package.NAME, VERSION_CODE, VERSION_NAME, APPLICATION_LABEL,
SIGNATURE, LAST_UPDATE_TIME, HASH_TYPE, HASH,
};
}

View File

@ -142,7 +142,7 @@ public class SelectAppsView extends ListView implements
private void toggleAppSelected(int position) {
Cursor c = (Cursor) adapter.getItem(position);
String packageName = c.getString(c.getColumnIndex(InstalledAppTable.Cols.PACKAGE_NAME));
String packageName = c.getString(c.getColumnIndex(InstalledAppTable.Cols.Package.NAME));
if (getState().hasSelectedPackage(packageName)) {
getState().deselectPackage(packageName);
adapter.updateCheckedIndicatorView(position, false);
@ -176,7 +176,7 @@ public class SelectAppsView extends ListView implements
for (int i = 0; i < getCount(); i++) {
Cursor c = (Cursor) getItemAtPosition(i);
String packageName = c.getString(c.getColumnIndex(InstalledAppTable.Cols.PACKAGE_NAME));
String packageName = c.getString(c.getColumnIndex(InstalledAppTable.Cols.Package.NAME));
getState().ensureFDroidSelected();
for (String selected : getState().getAppsToSwap()) {
if (TextUtils.equals(packageName, selected)) {
@ -257,7 +257,7 @@ public class SelectAppsView extends ListView implements
TextView labelView = (TextView) view.findViewById(R.id.application_label);
ImageView iconView = (ImageView) view.findViewById(android.R.id.icon);
String packageName = cursor.getString(cursor.getColumnIndex(InstalledAppTable.Cols.PACKAGE_NAME));
String packageName = cursor.getString(cursor.getColumnIndex(InstalledAppTable.Cols.Package.NAME));
String appLabel = cursor.getString(cursor.getColumnIndex(InstalledAppTable.Cols.APPLICATION_LABEL));
Drawable icon;

View File

@ -158,7 +158,7 @@ public class Assert {
Uri uri = InstalledAppProvider.getAppUri(appId);
String[] projection = {
InstalledAppTable.Cols.PACKAGE_NAME,
InstalledAppTable.Cols.Package.NAME,
InstalledAppTable.Cols.VERSION_CODE,
InstalledAppTable.Cols.VERSION_NAME,
InstalledAppTable.Cols.APPLICATION_LABEL,
@ -171,7 +171,7 @@ public class Assert {
cursor.moveToFirst();
assertEquals(appId, cursor.getString(cursor.getColumnIndex(InstalledAppTable.Cols.PACKAGE_NAME)));
assertEquals(appId, cursor.getString(cursor.getColumnIndex(InstalledAppTable.Cols.Package.NAME)));
assertEquals(versionCode, cursor.getInt(cursor.getColumnIndex(InstalledAppTable.Cols.VERSION_CODE)));
assertEquals(versionName, cursor.getString(cursor.getColumnIndex(InstalledAppTable.Cols.VERSION_NAME)));
cursor.close();

View File

@ -37,7 +37,7 @@ public class InstalledAppProviderTest extends FDroidProviderTest {
assertEquals(foundBefore.size(), 0);
ContentValues values = new ContentValues();
values.put(Cols.PACKAGE_NAME, "org.example.test-app");
values.put(Cols.Package.NAME, "org.example.test-app");
values.put(Cols.APPLICATION_LABEL, "Test App");
values.put(Cols.VERSION_CODE, 1021);
values.put(Cols.VERSION_NAME, "Longhorn");
@ -56,7 +56,7 @@ public class InstalledAppProviderTest extends FDroidProviderTest {
assertEquals(cursor.getCount(), 1);
cursor.moveToFirst();
assertEquals("org.example.test-app", cursor.getString(cursor.getColumnIndex(Cols.PACKAGE_NAME)));
assertEquals("org.example.test-app", cursor.getString(cursor.getColumnIndex(Cols.Package.NAME)));
assertEquals("Test App", cursor.getString(cursor.getColumnIndex(Cols.APPLICATION_LABEL)));
assertEquals(1021, cursor.getInt(cursor.getColumnIndex(Cols.VERSION_CODE)));
assertEquals("Longhorn", cursor.getString(cursor.getColumnIndex(Cols.VERSION_NAME)));
@ -125,7 +125,7 @@ public class InstalledAppProviderTest extends FDroidProviderTest {
Uri uri = InstalledAppProvider.getAppUri(packageName);
String[] projection = {
Cols.PACKAGE_NAME,
Cols.Package.NAME,
Cols.LAST_UPDATE_TIME,
};
@ -133,7 +133,7 @@ public class InstalledAppProviderTest extends FDroidProviderTest {
assertNotNull(cursor);
assertEquals("App \"" + packageName + "\" not installed", 1, cursor.getCount());
cursor.moveToFirst();
assertEquals(packageName, cursor.getString(cursor.getColumnIndex(Cols.PACKAGE_NAME)));
assertEquals(packageName, cursor.getString(cursor.getColumnIndex(Cols.Package.NAME)));
long lastUpdateTime = cursor.getLong(cursor.getColumnIndex(Cols.LAST_UPDATE_TIME));
assertTrue(lastUpdateTime > 0);
assertTrue(lastUpdateTime < System.currentTimeMillis());
@ -170,7 +170,7 @@ public class InstalledAppProviderTest extends FDroidProviderTest {
private ContentValues createContentValues(String appId, int versionCode, String versionNumber) {
ContentValues values = new ContentValues(3);
if (appId != null) {
values.put(Cols.PACKAGE_NAME, appId);
values.put(Cols.Package.NAME, appId);
}
values.put(Cols.APPLICATION_LABEL, "Mock app: " + appId);
values.put(Cols.VERSION_CODE, versionCode);