Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4077d7a7 authored by Vasu Nori's avatar Vasu Nori Committed by Android (Google) Code Review
Browse files

Merge "SQLiteOpenHelper should discard closed singleton database objects"

parents 53e75ea8 cc6f5491
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -2066,11 +2066,9 @@ public class SQLiteDatabase extends SQLiteClosable {
     * mapping is NOT replaced with the new mapping).
     */
    /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
        SQLiteCompiledSql compiledSql = null;
        synchronized(mCompiledQueries) {
            // don't insert the new mapping if a mapping already exists
            compiledSql = mCompiledQueries.get(sql);
            if (compiledSql != null) {
            if (mCompiledQueries.containsKey(sql)) {
                return;
            }

+14 −4
Original line number Diff line number Diff line
@@ -121,9 +121,14 @@ public abstract class SQLiteOpenHelper {
     * @return a read/write database object valid until {@link #close} is called
     */
    public synchronized SQLiteDatabase getWritableDatabase() {
        if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
                // darn! the user closed the database by calling mDatabase.close()
                mDatabase = null;
            } else if (!mDatabase.isReadOnly()) {
                return mDatabase;  // The database is already open for business
            }
        }

        if (mIsInitializing) {
            throw new IllegalStateException("getWritableDatabase called recursively");
@@ -207,9 +212,14 @@ public abstract class SQLiteOpenHelper {
     *     or {@link #close} is called.
     */
    public synchronized SQLiteDatabase getReadableDatabase() {
        if (mDatabase != null && mDatabase.isOpen()) {
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
                // darn! the user closed the database by calling mDatabase.close()
                mDatabase = null;
            } else {
                return mDatabase;  // The database is already open for business
            }
        }

        if (mIsInitializing) {
            throw new IllegalStateException("getReadableDatabase called recursively");
+3 −3
Original line number Diff line number Diff line
@@ -158,18 +158,18 @@ public abstract class SQLiteProgram extends SQLiteClosable {

    @Override
    protected void onAllReferencesReleased() {
        releaseCompiledSqlIfNotInCache();
        release();
        mDatabase.removeSQLiteClosable(this);
        mDatabase.releaseReference();
    }

    @Override
    protected void onAllReferencesReleasedFromContainer() {
        releaseCompiledSqlIfNotInCache();
        release();
        mDatabase.releaseReference();
    }

    /* package */ synchronized void releaseCompiledSqlIfNotInCache() {
    /* package */ synchronized void release() {
        if (mCompiledSql == null) {
            return;
        }
+1 −1
Original line number Diff line number Diff line
@@ -265,7 +265,7 @@ public class SQLiteStatement extends SQLiteProgram
        clearBindings();
        // release the compiled sql statement so that the caller's SQLiteStatement no longer
        // has a hard reference to a database object that may get deallocated at any point.
        releaseCompiledSqlIfNotInCache();
        release();
        // restore the database connection handle to the original value
        mDatabase = mOrigDb;
        nHandle = mDatabase.mNativeHandle;
+40 −4
Original line number Diff line number Diff line
@@ -19,8 +19,11 @@ package android.database.sqlite;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseErrorHandler;
import android.database.DatabaseUtils;
import android.database.DefaultDatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteStatement;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
@@ -41,6 +44,7 @@ public class SQLiteDatabaseTest extends AndroidTestCase {
    private static final int INSERT = 1;
    private static final int UPDATE = 2;
    private static final int DELETE = 3;
    private static final String DB_NAME = "database_test.db";

    @Override
    protected void setUp() throws Exception {
@@ -61,7 +65,7 @@ public class SQLiteDatabaseTest extends AndroidTestCase {

    private void dbSetUp() throws Exception {
        File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
        mDatabaseFile = new File(dbDir, "database_test.db");
        mDatabaseFile = new File(dbDir, DB_NAME);
        if (mDatabaseFile.exists()) {
            mDatabaseFile.delete();
        }
@@ -860,7 +864,7 @@ public class SQLiteDatabaseTest extends AndroidTestCase {
                    "select count(*) from " + TEST_TABLE, null));
            // query in a different thread. but since the transaction is started using
            // execSQ() instead of beginTransaction(), cursor's query is considered part of
            // the same ransaction - and hence it should see the above inserted row
            // the same transaction - and hence it should see the above inserted row
            Thread t = new Thread() {
                @Override public void run() {
                    c1.requery();
@@ -878,11 +882,11 @@ public class SQLiteDatabaseTest extends AndroidTestCase {

    /**
     * This test is same as {@link #testTransactionAndWalInterplay2()} except the following:
     * instead of commiting the data, do rollback and make sure the data seen by the query
     * instead of committing the data, do rollback and make sure the data seen by the query
     * within the transaction is now gone.
     */
    @SmallTest
    public void testTransactionAndWalInterplay3() throws InterruptedException {
    public void testTransactionAndWalInterplay3() {
        createTableAndClearCache();
        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
        String sql = "select * from " + TEST_TABLE;
@@ -909,4 +913,36 @@ public class SQLiteDatabaseTest extends AndroidTestCase {
                "select count(*) from " + TEST_TABLE, null));
        c.close();
    }

    /**
     * http://b/issue?id=2943028
     * SQLiteOpenHelper maintains a Singleton even if it is in bad state.
     */
    @SmallTest
    public void testCloseAndReopen() {
        mDatabase.close();
        TestOpenHelper helper = new TestOpenHelper(getContext(), DB_NAME, null,
                CURRENT_DATABASE_VERSION, new DefaultDatabaseErrorHandler());
        mDatabase = helper.getWritableDatabase();
        createTableAndClearCache();
        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
        Cursor c = mDatabase.query(TEST_TABLE, new String[]{"i", "j"}, null, null, null, null, null);
        assertEquals(1, c.getCount());
        c.close();
        mDatabase.close();
        assertFalse(mDatabase.isOpen());
        mDatabase = helper.getReadableDatabase();
        assertTrue(mDatabase.isOpen());
        c = mDatabase.query(TEST_TABLE, new String[]{"i", "j"}, null, null, null, null, null);
        assertEquals(1, c.getCount());
        c.close();
    }
    private class TestOpenHelper extends SQLiteOpenHelper {
        public TestOpenHelper(Context context, String name, CursorFactory factory, int version,
                DatabaseErrorHandler errorHandler) {
            super(context, name, factory, version, errorHandler);
        }
        @Override public void onCreate(SQLiteDatabase db) {}
        @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
    }
}