Bug 710330 - Refactor batch update helper functions into LocalDB. r=lucasr

This commit is contained in:
Gian-Carlo Pascutto 2012-06-26 00:30:47 +02:00
parent 316dca6e38
commit 4df3be97fa
2 changed files with 181 additions and 197 deletions

View File

@ -13,6 +13,7 @@ import org.mozilla.gecko.db.BrowserContract.Images;
import org.mozilla.gecko.db.BrowserContract.Passwords;
import org.mozilla.gecko.db.BrowserContract.URLColumns;
import org.mozilla.gecko.db.BrowserContract.SyncColumns;
import org.mozilla.gecko.db.LocalBrowserDB;
import org.mozilla.gecko.sqlite.SQLiteBridge;
import org.mozilla.gecko.sqlite.SQLiteBridgeException;
import org.mozilla.gecko.sync.setup.SyncAccounts;
@ -72,6 +73,7 @@ public class ProfileMigrator {
private Runnable mLongOperationStartCallback;
private boolean mLongOperationStartRun;
private Runnable mLongOperationStopCallback;
private LocalBrowserDB mDB;
// Default number of history entries to migrate in one run.
private static final int DEFAULT_HISTORY_MIGRATE_COUNT = 2000;
@ -709,32 +711,15 @@ public class ProfileMigrator {
public PlacesRunnable(File profileDir, int limit) {
mProfileDir = profileDir;
mMaxEntries = limit;
}
protected Uri getBookmarksUri() {
Uri.Builder uriBuilder = Bookmarks.CONTENT_URI.buildUpon()
.appendQueryParameter(BrowserContract.PARAM_SHOW_DELETED, "1");
return uriBuilder.build();
}
protected Uri getHistoryUri() {
Uri.Builder uriBuilder = History.CONTENT_URI.buildUpon()
.appendQueryParameter(BrowserContract.PARAM_SHOW_DELETED, "1");
return uriBuilder.build();
}
protected Uri getImagesUri() {
Uri.Builder uriBuilder = Images.CONTENT_URI.buildUpon()
.appendQueryParameter(BrowserContract.PARAM_SHOW_DELETED, "1");
return uriBuilder.build();
mDB = new LocalBrowserDB(GeckoProfile.get(mContext).getName());
}
private long getFolderId(String guid) {
Cursor c = null;
try {
c = mCr.query(getBookmarksUri(),
// Uses default profile
c = mCr.query(Bookmarks.CONTENT_URI,
new String[] { Bookmarks._ID },
Bookmarks.GUID + " = ?",
new String [] { guid },
@ -822,72 +807,7 @@ public class ProfileMigrator {
protected void updateBrowserHistory(String url, String title,
long date, int visits) {
Cursor cursor = null;
try {
final String[] projection = new String[] {
History._ID,
History.VISITS,
History.DATE_LAST_VISITED
};
cursor = mCr.query(getHistoryUri(),
projection,
History.URL + " = ?",
new String[] { url },
null);
ContentValues values = new ContentValues();
ContentProviderOperation.Builder builder = null;
// Restore deleted record if possible
values.put(History.IS_DELETED, 0);
if (cursor.moveToFirst()) {
int visitsCol = cursor.getColumnIndexOrThrow(History.VISITS);
int dateCol = cursor.getColumnIndexOrThrow(History.DATE_LAST_VISITED);
int oldVisits = cursor.getInt(visitsCol);
long oldDate = cursor.getLong(dateCol);
values.put(History.VISITS, oldVisits + visits);
if (title != null) {
values.put(History.TITLE, title);
}
// Only update last visited if newer.
if (date > oldDate) {
values.put(History.DATE_LAST_VISITED, date);
}
int idCol = cursor.getColumnIndexOrThrow(History._ID);
// We use default profile anyway
Uri historyUri = ContentUris.withAppendedId(getHistoryUri(),
cursor.getLong(idCol));
// Update
builder = ContentProviderOperation.newUpdate(historyUri);
// URL should be unique and we should hit it
builder.withExpectedCount(1);
builder.withValues(values);
} else {
values.put(History.URL, url);
values.put(History.VISITS, visits);
if (title != null) {
values.put(History.TITLE, title);
} else {
values.put(History.TITLE, url);
}
values.put(History.DATE_LAST_VISITED, date);
// Insert
builder = ContentProviderOperation.newInsert(getHistoryUri());
builder.withValues(values);
}
// Queue the operation
mOperations.add(builder.build());
} finally {
if (cursor != null)
cursor.close();
}
mDB.updateHistoryInBatch(mCr, mOperations, url, title, date, visits);
}
protected BitmapDrawable decodeImageData(byte[] data) {
@ -915,57 +835,21 @@ public class ProfileMigrator {
}
}
try {
ContentValues values = new ContentValues();
byte[] newData = null;
// Recompress decoded images to PNG.
if (image != null) {
Bitmap bitmap = image.getBitmap();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
values.put(Images.FAVICON, stream.toByteArray());
newData = stream.toByteArray();
} else {
// PNG images can be passed directly. Well, aside
// from having to convert them into a byte[].
values.put(Images.FAVICON, data);
newData = data;
}
values.put(Images.URL, url);
values.put(Images.FAVICON_URL, faviconUrl);
// Restore deleted record if possible
values.put(Images.IS_DELETED, 0);
if (faviconGuid != null) {
values.put(Images.GUID, faviconGuid);
}
Cursor cursor = null;
ContentProviderOperation.Builder builder = null;
try {
cursor = mCr.query(getImagesUri(),
null,
Images.URL + " = ?",
new String[] { url },
null);
if (cursor != null && cursor.moveToFirst()) {
// Update
builder = ContentProviderOperation.newUpdate(getImagesUri());
// URL should be unique and we should hit it
builder.withExpectedCount(1);
builder.withValues(values);
builder.withSelection(Images.URL + " = ?",
new String[] { url });
} else {
// Insert
builder = ContentProviderOperation.newInsert(getImagesUri());
builder.withValues(values);
}
} finally {
if (cursor != null)
cursor.close();
}
// Queue the operation
mOperations.add(builder.build());
mDB.updateFaviconInBatch(mCr, mOperations, url, faviconUrl, faviconGuid, newData);
} catch (SQLException e) {
Log.i(LOGTAG, "Migrating favicon failed: " + mime + " URL: " + url
+ " error:" + e.getMessage());
@ -1081,81 +965,17 @@ public class ProfileMigrator {
long parent, long added,
long modified, long position,
String keyword, int type) {
ContentValues values = new ContentValues();
if (title == null && url != null) {
title = url;
}
if (title != null) {
values.put(Bookmarks.TITLE, title);
}
if (url != null) {
values.put(Bookmarks.URL, url);
}
if (guid != null) {
values.put(SyncColumns.GUID, guid);
}
if (keyword != null) {
values.put(Bookmarks.KEYWORD, keyword);
}
values.put(SyncColumns.DATE_CREATED, added);
values.put(SyncColumns.DATE_MODIFIED, modified);
values.put(Bookmarks.POSITION, position);
// Restore deleted record if possible
values.put(Bookmarks.IS_DELETED, 0);
// Translate the parent pointer if needed
if (mRerootMap.containsKey(parent)) {
parent = mRerootMap.get(parent);
}
values.put(Bookmarks.PARENT, parent);
// The bookmark can only be one of three valid types
values.put(Bookmarks.TYPE, type == PLACES_TYPE_BOOKMARK ? Bookmarks.TYPE_BOOKMARK :
type == PLACES_TYPE_FOLDER ? Bookmarks.TYPE_FOLDER :
Bookmarks.TYPE_SEPARATOR);
Cursor cursor = null;
ContentProviderOperation.Builder builder = null;
if (url != null) {
try {
final String[] projection = new String[] {
Bookmarks._ID,
Bookmarks.URL
};
// Check if the boomark exists
cursor = mCr.query(getBookmarksUri(),
projection,
Bookmarks.URL + " = ?",
new String[] { url },
null);
if (cursor.moveToFirst()) {
int idCol = cursor.getColumnIndexOrThrow(Bookmarks._ID);
// We use default profile anyway
Uri bookmarkUri = ContentUris.withAppendedId(getBookmarksUri(),
cursor.getLong(idCol));
// Update
builder = ContentProviderOperation.newUpdate(bookmarkUri);
// URL should be unique and we should hit it
builder.withExpectedCount(1);
builder.withValues(values);
} else {
// Insert
builder = ContentProviderOperation.newInsert(getBookmarksUri());
builder.withValues(values);
}
} finally {
if (cursor != null)
cursor.close();
}
} else {
// Insert
builder = ContentProviderOperation.newInsert(getBookmarksUri());
builder.withValues(values);
}
// Queue the operation
mOperations.add(builder.build());
int newtype = (type == PLACES_TYPE_BOOKMARK ? Bookmarks.TYPE_BOOKMARK :
type == PLACES_TYPE_FOLDER ? Bookmarks.TYPE_FOLDER :
Bookmarks.TYPE_SEPARATOR);
mDB.updateBookmarkInBatch(mCr, mOperations,
url, title, guid, parent, added,
modified, position, keyword, newtype);
}
protected void migrateBookmarks(SQLiteBridge db) {

View File

@ -7,6 +7,7 @@ package org.mozilla.gecko.db;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Collection;
import org.mozilla.gecko.db.BrowserContract.Bookmarks;
import org.mozilla.gecko.db.BrowserContract.History;
@ -14,11 +15,14 @@ import org.mozilla.gecko.db.BrowserContract.ImageColumns;
import org.mozilla.gecko.db.BrowserContract.Images;
import org.mozilla.gecko.db.BrowserContract.Combined;
import org.mozilla.gecko.db.BrowserContract.URLColumns;
import org.mozilla.gecko.db.BrowserContract.SyncColumns;
import org.mozilla.gecko.db.DBUtils;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.ContentProviderResult;
import android.content.ContentProviderOperation;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.CursorWrapper;
@ -112,6 +116,24 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
return uri.buildUpon().appendQueryParameter(BrowserContract.PARAM_PROFILE, mProfile).build();
}
private Uri getAllBookmarksUri() {
Uri.Builder uriBuilder = mBookmarksUriWithProfile.buildUpon()
.appendQueryParameter(BrowserContract.PARAM_SHOW_DELETED, "1");
return uriBuilder.build();
}
private Uri getAllHistoryUri() {
Uri.Builder uriBuilder = mHistoryUriWithProfile.buildUpon()
.appendQueryParameter(BrowserContract.PARAM_SHOW_DELETED, "1");
return uriBuilder.build();
}
private Uri getAllImagesUri() {
Uri.Builder uriBuilder = mImagesUriWithProfile.buildUpon()
.appendQueryParameter(BrowserContract.PARAM_SHOW_DELETED, "1");
return uriBuilder.build();
}
private Cursor filterAllSites(ContentResolver cr, String[] projection, CharSequence constraint,
int limit, CharSequence urlFilter) {
String selection = "";
@ -658,6 +680,148 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
return b;
}
// Utility function for updating existing history using batch operations
public void updateHistoryInBatch(ContentResolver cr,
Collection<ContentProviderOperation> operations,
String url, String title,
long date, int visits) {
Cursor cursor = null;
try {
final String[] projection = new String[] {
History._ID,
History.VISITS,
History.DATE_LAST_VISITED
};
// We need to get the old visit count.
cursor = cr.query(getAllHistoryUri(),
projection,
History.URL + " = ?",
new String[] { url },
null);
ContentValues values = new ContentValues();
// Restore deleted record if possible
values.put(History.IS_DELETED, 0);
if (cursor.moveToFirst()) {
int visitsCol = cursor.getColumnIndexOrThrow(History.VISITS);
int dateCol = cursor.getColumnIndexOrThrow(History.DATE_LAST_VISITED);
int oldVisits = cursor.getInt(visitsCol);
long oldDate = cursor.getLong(dateCol);
values.put(History.VISITS, oldVisits + visits);
// Only update last visited if newer.
if (date > oldDate) {
values.put(History.DATE_LAST_VISITED, date);
}
} else {
values.put(History.VISITS, 1);
values.put(History.DATE_LAST_VISITED, date);
}
if (title != null) {
values.put(History.TITLE, title);
}
values.put(History.URL, url);
Uri historyUri = getAllHistoryUri().buildUpon().
appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true").build();
// Update or insert
ContentProviderOperation.Builder builder =
ContentProviderOperation.newUpdate(historyUri);
builder.withSelection(History.URL + " = ?", new String[] { url });
builder.withValues(values);
// Queue the operation
operations.add(builder.build());
} finally {
if (cursor != null)
cursor.close();
}
}
public void updateBookmarkInBatch(ContentResolver cr,
Collection<ContentProviderOperation> operations,
String url, String title, String guid,
long parent, long added,
long modified, long position,
String keyword, int type) {
ContentValues values = new ContentValues();
if (title == null && url != null) {
title = url;
}
if (title != null) {
values.put(Bookmarks.TITLE, title);
}
if (url != null) {
values.put(Bookmarks.URL, url);
}
if (guid != null) {
values.put(SyncColumns.GUID, guid);
}
if (keyword != null) {
values.put(Bookmarks.KEYWORD, keyword);
}
if (added > 0) {
values.put(SyncColumns.DATE_CREATED, added);
}
if (modified > 0) {
values.put(SyncColumns.DATE_MODIFIED, modified);
}
values.put(Bookmarks.POSITION, position);
// Restore deleted record if possible
values.put(Bookmarks.IS_DELETED, 0);
// This assumes no "real" folder has a negative ID. Only
// things like the reading list folder do.
if (parent < 0) {
parent = getFolderIdFromGuid(cr, Bookmarks.MOBILE_FOLDER_GUID);
}
values.put(Bookmarks.PARENT, parent);
values.put(Bookmarks.TYPE, type);
Uri bookmarkUri = getAllBookmarksUri().buildUpon().
appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true").build();
// Update or insert
ContentProviderOperation.Builder builder =
ContentProviderOperation.newUpdate(bookmarkUri);
builder.withSelection(Bookmarks.URL + " = ?", new String[] { url });
builder.withValues(values);
// Queue the operation
operations.add(builder.build());
}
public void updateFaviconInBatch(ContentResolver cr,
Collection<ContentProviderOperation> operations,
String url, String faviconUrl,
String faviconGuid, byte[] data) {
ContentValues values = new ContentValues();
values.put(Images.FAVICON, data);
values.put(Images.URL, url);
if (faviconUrl != null) {
values.put(Images.FAVICON_URL, faviconUrl);
}
// Restore deleted record if possible
values.put(Images.IS_DELETED, 0);
if (faviconGuid != null) {
values.put(Images.GUID, faviconGuid);
}
// Update or insert
Uri imagesUri = getAllImagesUri().buildUpon().
appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true").build();
// Update or insert
ContentProviderOperation.Builder builder =
ContentProviderOperation.newUpdate(imagesUri);
builder.withValues(values);
builder.withSelection(Images.URL + " = ?", new String[] { url });
// Queue the operation
operations.add(builder.build());
}
// This wrapper adds a fake "Desktop Bookmarks" folder entry to the
// beginning of the cursor's data set.
private class SpecialFoldersCursorWrapper extends CursorWrapper {