Commit 2990565a authored by Wes Johnston's avatar Wes Johnston
Browse files

Bug 740218 - Support transactions in sqlitebridge and use them. r=gcp,rnewman,lucasr

parent 2240e441
Loading
Loading
Loading
Loading
+6 −9
Original line number Diff line number Diff line
@@ -152,17 +152,14 @@ public class FormHistoryProvider extends GeckoProvider {
        if (!values.containsKey(FormHistory.GUID)) {
            return;
        }

        String guid = values.getAsString(FormHistory.GUID);
        try {
        if (guid == null) {
            db.delete(TABLE_DELETED_FORM_HISTORY, WHERE_GUID_IS_NULL, null);
            return;
        }
        String[] args = new String[] { guid };
        db.delete(TABLE_DELETED_FORM_HISTORY, WHERE_GUID_IS_VALUE, args);
        } catch(SQLiteBridgeException ex) {
            Log.w(getLogTag(), "Error removing entry with GUID " + guid, ex);
        }
     }

    @Override
+89 −8
Original line number Diff line number Diff line
@@ -10,6 +10,8 @@ import java.io.IOException;
import java.lang.IllegalArgumentException;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Random;

import org.mozilla.gecko.GeckoApp;
@@ -59,6 +61,30 @@ public abstract class GeckoProvider extends ContentProvider {
    private HashMap<String, SQLiteBridge> mDatabasePerProfile;
    protected Context mContext = null;

    @Override
    public void shutdown() {
        if (mDatabasePerProfile == null)
          return;

        Collection<SQLiteBridge> bridges = mDatabasePerProfile.values();
        Iterator<SQLiteBridge> it = bridges.iterator();

        while (it.hasNext()) {
            SQLiteBridge bridge = it.next();
            if (bridge != null) {
                try {
                    bridge.close();
                } catch (Exception ex) { }
            }
        }

        mDatabasePerProfile = null;
    }

    public void finalize() {
        shutdown();
    }

    protected void setLogTag(String aLogTag) {
        mLogTag = aLogTag;
    }
@@ -91,11 +117,14 @@ public abstract class GeckoProvider extends ContentProvider {
            String resourcePath = context.getPackageResourcePath();
            GeckoAppShell.loadSQLiteLibs(context, resourcePath);
            GeckoAppShell.loadNSSLibs(context, resourcePath);
            bridge = new SQLiteBridge(databasePath);
            bridge = SQLiteBridge.openDatabase(databasePath, null, 0);
            int version = bridge.getVersion();
            Log.i(mLogTag, version + " == " + mDBVersion);
            dbNeedsSetup = version != mDBVersion;
        } catch (SQLiteBridgeException ex) {
            // close the database
            if (bridge != null)
                bridge.close();

            // this will throw if the database can't be found
            // we should attempt to set it up if Gecko is running
            dbNeedsSetup = true;
@@ -117,6 +146,7 @@ public abstract class GeckoProvider extends ContentProvider {
            bridge = null;
            initGecko();
        }
        if (bridge != null)
            mDatabasePerProfile.put(databasePath, bridge);

        return bridge;
@@ -204,6 +234,7 @@ public abstract class GeckoProvider extends ContentProvider {
            deleted = db.delete(getTable(uri), selection, selectionArgs);
        } catch (SQLiteBridgeException ex) {
            Log.e(mLogTag, "Error deleting record", ex);
            throw ex;
        }

        return deleted;
@@ -222,17 +253,65 @@ public abstract class GeckoProvider extends ContentProvider {

        setupDefaults(uri, values);

        onPreInsert(values, uri, db);

        boolean useTransaction = !db.inTransaction();
        try {
            if (useTransaction) {
                db.beginTransaction();
            }
 
            // onPreInsert does a check for the item in the deleted table in some cases
            // so we put it inside this transaction
            onPreInsert(values, uri, db);
            id = db.insert(getTable(uri), null, values);

            if (useTransaction) {
                db.setTransactionSuccessful();
            }
        } catch (SQLiteBridgeException ex) {
            Log.e(mLogTag, "Error inserting in db", ex);
            throw ex;
        } finally {
            if (useTransaction) {
                db.endTransaction();
            }
        }

        return ContentUris.withAppendedId(uri, id);
    }

    @Override
    public int bulkInsert(Uri uri, ContentValues[] allValues) {
        final SQLiteBridge db = getDatabase(uri);
        // If we can not get a SQLiteBridge instance, its likely that the database
        // has not been set up and Gecko is not running. We return 0 and expect
        // callers to try again later
        if (db == null)
            return 0;

        long id = -1;
        int rowsAdded = 0;

        String table = getTable(uri);

        try {
            db.beginTransaction();
            for (ContentValues initialValues : allValues) {
                ContentValues values = new ContentValues(initialValues);
                setupDefaults(uri, values);
                onPreInsert(values, uri, db);
                id = db.insert(table, null, values);
                rowsAdded++;
            }
            db.setTransactionSuccessful();
        } catch (SQLiteBridgeException ex) {
            Log.e(mLogTag, "Error inserting in db", ex);
            throw ex;
        } finally {
            db.endTransaction();
        }
        return rowsAdded;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
@@ -251,6 +330,7 @@ public abstract class GeckoProvider extends ContentProvider {
            updated = db.update(getTable(uri), values, selection, selectionArgs);
        } catch (SQLiteBridgeException ex) {
            Log.e(mLogTag, "Error updating table", ex);
            throw ex;
        }

        return updated;
@@ -275,6 +355,7 @@ public abstract class GeckoProvider extends ContentProvider {
            onPostQuery(cursor, uri, db);
        } catch (SQLiteBridgeException ex) {
            Log.e(mLogTag, "Error querying database", ex);
            throw ex;
        }

        return cursor;
+5 −9
Original line number Diff line number Diff line
@@ -217,16 +217,12 @@ public class PasswordsProvider extends GeckoProvider {
    public void onPreInsert(ContentValues values, Uri uri, SQLiteBridge db) {
        if (values.containsKey(Passwords.GUID)) {
            String guid = values.getAsString(Passwords.GUID);
            try {
            if (guid == null) {
                db.delete(TABLE_DELETED_PASSWORDS, WHERE_GUID_IS_NULL, null);
                return;
            }
            String[] args = new String[] { guid };
            db.delete(TABLE_DELETED_PASSWORDS, WHERE_GUID_IS_VALUE, args);
            } catch(SQLiteBridgeException ex) {
                Log.w(getLogTag(), "Error removing entry with GUID " + guid, ex);
            }
        }

        if (values.containsKey(Passwords.ENCRYPTED_PASSWORD)) {
+101 −4
Original line number Diff line number Diff line
@@ -8,14 +8,17 @@ import org.mozilla.gecko.sqlite.SQLiteBridgeException;
import org.mozilla.gecko.sqlite.MatrixBlobCursor;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.DatabaseErrorHandler;
import android.text.TextUtils;
import android.util.Log;

import java.lang.String;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import android.util.Log;
import java.util.Map.Entry;
import java.util.Set;

@@ -27,12 +30,17 @@ import java.util.Set;
public class SQLiteBridge {
    private static final String LOGTAG = "SQLiteBridge";

    // Path to the database. We reopen it every query.
    // Path to the database. If this database was not opened with openDatabase, we reopen it every query.
    private String mDb;
    // pointer to the database if it was opened with openDatabase
    protected long mDbPointer = 0;

    // Values remembered after a query.
    private long[] mQueryResults;

    private boolean mTransactionSuccess = false;
    private boolean mInTransaction = false;

    private static final int RESULT_INSERT_ROW_ID = 0;
    private static final int RESULT_ROWS_CHANGED = 1;

@@ -41,6 +49,13 @@ public class SQLiteBridge {
                                                      String[] aParams,
                                                      long[] aUpdateResult)
        throws SQLiteBridgeException;
    private static native MatrixBlobCursor sqliteCallWithDb(long aDb, String aQuery,
                                                            String[] aParams,
                                                            long[] aUpdateResult)
        throws SQLiteBridgeException;
    private static native long openDatabase(String aDb)
        throws SQLiteBridgeException;
    private static native void closeDatabase(long aDb);

    // Takes the path to the database we want to access.
    public SQLiteBridge(String aDb) throws SQLiteBridgeException {
@@ -204,10 +219,92 @@ public class SQLiteBridge {
    // are not supported.
    private Cursor internalQuery(String aQuery, String[] aParams)
        throws SQLiteBridgeException {

        mQueryResults = new long[2];
        if (isOpen()) {
            return sqliteCallWithDb(mDbPointer, aQuery, aParams, mQueryResults);
        }
        return sqliteCall(mDb, aQuery, aParams, mQueryResults);
    }

    // nop, provided for API compatibility with SQLiteDatabase.
    public void close() { }
    /*
     * The second two parameters here are just provided for compatbility with SQLiteDatabase
     * Support for them is not currently implemented
    */
    public static SQLiteBridge openDatabase(String path, SQLiteDatabase.CursorFactory factory, int flags)
        throws SQLiteException {
        SQLiteBridge bridge = null;
        try {
            bridge = new SQLiteBridge(path);
            bridge.mDbPointer = bridge.openDatabase(path);
        } catch(SQLiteBridgeException ex) {
            // catch and rethrow as a SQLiteException to match SQLiteDatabase
            throw new SQLiteException(ex.getMessage());
        }
        return bridge;
    }

    public void close() {
        if (isOpen()) {
          closeDatabase(mDbPointer);
        }
        mDbPointer = 0;
    }

    public boolean isOpen() {
        return mDbPointer > 0;
    }

    public void beginTransaction() throws SQLiteBridgeException {
        if (inTransaction()) {
            throw new SQLiteBridgeException("Nested transactions are not supported");
        }
        execSQL("BEGIN EXCLUSIVE");
        mTransactionSuccess = false;
        mInTransaction = true;
    }

    public void beginTransactionNonExclusive() throws SQLiteBridgeException {
        if (inTransaction()) {
            throw new SQLiteBridgeException("Nested transactions are not supported");
        }
        execSQL("BEGIN IMMEDIATE");
        mTransactionSuccess = false;
        mInTransaction = true;
    }

    public void endTransaction() {
        if (!inTransaction())
            return;

        try {
          if (mTransactionSuccess) {
              execSQL("COMMIT TRANSACTION");
          } else {
              execSQL("ROLLBACK TRANSACTION");
          }
        } catch(SQLiteBridgeException ex) {
            Log.e(LOGTAG, "Error ending transaction", ex);
        }
        mInTransaction = false;
        mTransactionSuccess = false;
    }

    public void setTransactionSuccessful() throws SQLiteBridgeException {
        if (!inTransaction()) {
            throw new SQLiteBridgeException("setTransactionSuccessful called outside a transaction");
        }
        mTransactionSuccess = true;
    }

    public boolean inTransaction() {
        return mInTransaction;
    }

    public void finalize() {
        if (isOpen()) {
            Log.e(LOGTAG, "Bridge finalized without closing the database");
            close();
        }
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@

package org.mozilla.gecko.sqlite;

public class SQLiteBridgeException extends Exception {
public class SQLiteBridgeException extends RuntimeException {
    static final long serialVersionUID = 1L;

    public SQLiteBridgeException() {}
Loading