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

Commit 7501010b authored by Vasu Nori's avatar Vasu Nori
Browse files

some refactoring and multi-threading fixes

Change-Id: I7a0497dc2ed7b1e21471d71532558ef243eb9f73
parent 69fc43d4
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package android.database.sqlite;

import android.app.ActivityThread;
import android.app.AppGlobals;
import android.content.ContentValues;
import android.database.Cursor;
@@ -1045,6 +1044,9 @@ public class SQLiteDatabase extends SQLiteClosable {
        if (!isOpen()) {
            return; // already closed
        }
        if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
            Log.i(TAG, "closing db: " + mPath + " (connection # " + mConnectionNum);
        }
        lock();
        try {
            closeClosable();
@@ -2123,7 +2125,8 @@ public class SQLiteDatabase extends SQLiteClosable {

    /* package */ void verifyDbIsOpen() {
        if (!isOpen()) {
            throw new IllegalStateException("database " + getPath() + " already closed");
            throw new IllegalStateException("database " + getPath() + " (conn# " +
                    mConnectionNum + ") already closed");
        }
    }

+113 −60
Original line number Diff line number Diff line
@@ -28,6 +28,11 @@ public abstract class SQLiteProgram extends SQLiteClosable {

    private static final String TAG = "SQLiteProgram";

    /** the type of sql statement being processed by this object */
    private static final int SELECT_STMT = 1;
    private static final int UPDATE_STMT = 2;
    private static final int OTHER_STMT = 3;

    /** The database this program is compiled against.
     * @deprecated do not use this
     */
@@ -61,35 +66,37 @@ public abstract class SQLiteProgram extends SQLiteClosable {
    /* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
        mDatabase = db;
        mSql = sql.trim();
        db.acquireReference();
        db.addSQLiteClosable(this);
        this.nHandle = db.mNativeHandle;
        attachObjectToDatabase(db);
        compileSql();
    }

    private void compileSql() {
        if (nStatement > 0) {
            // already compiled.
            return;
        }

        // only cache CRUD statements
        String prefixSql = mSql.substring(0, 6);
        if (!prefixSql.equalsIgnoreCase("INSERT") && !prefixSql.equalsIgnoreCase("UPDATE") &&
                !prefixSql.equalsIgnoreCase("REPLAC") &&
                !prefixSql.equalsIgnoreCase("DELETE") && !prefixSql.equalsIgnoreCase("SELECT")) {
            mCompiledSql = new SQLiteCompiledSql(db, sql);
        if (getSqlStatementType(mSql) == OTHER_STMT) {
            mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
            nStatement = mCompiledSql.nStatement;
            // since it is not in the cache, no need to acquire() it.
            return;
        }

        // it is not pragma
        mCompiledSql = db.getCompiledStatementForSql(sql);
        mCompiledSql = mDatabase.getCompiledStatementForSql(mSql);
        if (mCompiledSql == null) {
            // create a new compiled-sql obj
            mCompiledSql = new SQLiteCompiledSql(db, sql);
            mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);

            // add it to the cache of compiled-sqls
            // but before adding it and thus making it available for anyone else to use it,
            // make sure it is acquired by me.
            mCompiledSql.acquire();
            db.addToCompiledQueries(sql, mCompiledSql);
            mDatabase.addToCompiledQueries(mSql, mCompiledSql);
            if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
                Log.v(TAG, "Created DbObj (id#" + mCompiledSql.nStatement +
                        ") for sql: " + sql);
                        ") for sql: " + mSql);
            }
        } else {
            // it is already in compiled-sql cache.
@@ -100,12 +107,12 @@ public abstract class SQLiteProgram extends SQLiteClosable {
                // we can't have two different SQLiteProgam objects can't share the same
                // CompiledSql object. create a new one.
                // finalize it when I am done with it in "this" object.
                mCompiledSql = new SQLiteCompiledSql(db, sql);
                mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
                if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
                    Log.v(TAG, "** possible bug ** Created NEW DbObj (id#" +
                            mCompiledSql.nStatement +
                            ") because the previously created DbObj (id#" + last +
                            ") was not released for sql:" + sql);
                            ") was not released for sql:" + mSql);
                }
                // since it is not in the cache, no need to acquire() it.
            }
@@ -113,11 +120,37 @@ public abstract class SQLiteProgram extends SQLiteClosable {
        nStatement = mCompiledSql.nStatement;
    }

    private int getSqlStatementType(String sql) {
        if (mSql.length() < 6) {
            return OTHER_STMT;
        }
        String prefixSql = mSql.substring(0, 6);
        if (prefixSql.equalsIgnoreCase("SELECT")) {
            return SELECT_STMT;
        } else if (prefixSql.equalsIgnoreCase("INSERT") ||
                prefixSql.equalsIgnoreCase("UPDATE") ||
                prefixSql.equalsIgnoreCase("REPLAC") ||
                prefixSql.equalsIgnoreCase("DELETE")) {
            return UPDATE_STMT;
        }
        return OTHER_STMT;
    }

    private synchronized void attachObjectToDatabase(SQLiteDatabase db) {
        db.acquireReference();
        db.addSQLiteClosable(this);
        nHandle = db.mNativeHandle;
    }

    private synchronized void detachObjectFromDatabase() {
        mDatabase.removeSQLiteClosable(this);
        mDatabase.releaseReference();
    }

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

    @Override
@@ -135,13 +168,13 @@ public abstract class SQLiteProgram extends SQLiteClosable {
                // it is NOT in compiled-sql cache. i.e., responsibility of
                // releasing this statement is on me.
                mCompiledSql.releaseSqlStatement();
                mCompiledSql = null;
                nStatement = 0;
            } else {
                // it is in compiled-sql cache. reset its CompiledSql#mInUse flag
                mCompiledSql.release();
            }
        }
        mCompiledSql = null;
        nStatement = 0;
    }

    /**
@@ -150,8 +183,10 @@ public abstract class SQLiteProgram extends SQLiteClosable {
     * @return a unique identifier for this program
     */
    public final int getUniqueId() {
        synchronized (this) {
            return (mCompiledSql != null) ? mCompiledSql.nStatement : 0;
        }
    }

    /* package */ String getSqlString() {
        return mSql;
@@ -176,6 +211,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
     * @param index The 1-based index to the parameter to bind null to
     */
    public void bindNull(int index) {
        synchronized (this) {
            mDatabase.verifyDbIsOpen();
            acquireReference();
            try {
@@ -184,6 +220,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
                releaseReference();
            }
        }
    }

    /**
     * Bind a long value to this statement. The value remains bound until
@@ -193,6 +230,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
     * @param value The value to bind
     */
    public void bindLong(int index, long value) {
        synchronized (this) {
            mDatabase.verifyDbIsOpen();
            acquireReference();
            try {
@@ -201,6 +239,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
                releaseReference();
            }
        }
    }

    /**
     * Bind a double value to this statement. The value remains bound until
@@ -210,6 +249,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
     * @param value The value to bind
     */
    public void bindDouble(int index, double value) {
        synchronized (this) {
            mDatabase.verifyDbIsOpen();
            acquireReference();
            try {
@@ -218,6 +258,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
                releaseReference();
            }
        }
    }

    /**
     * Bind a String value to this statement. The value remains bound until
@@ -230,6 +271,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
        if (value == null) {
            throw new IllegalArgumentException("the bind value at index " + index + " is null");
        }
        synchronized (this) {
            mDatabase.verifyDbIsOpen();
            acquireReference();
            try {
@@ -238,6 +280,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
                releaseReference();
            }
        }
    }

    /**
     * Bind a byte array value to this statement. The value remains bound until
@@ -250,6 +293,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
        if (value == null) {
            throw new IllegalArgumentException("the bind value at index " + index + " is null");
        }
        synchronized (this) {
            mDatabase.verifyDbIsOpen();
            acquireReference();
            try {
@@ -258,11 +302,13 @@ public abstract class SQLiteProgram extends SQLiteClosable {
                releaseReference();
            }
        }
    }

    /**
     * Clears all existing bindings. Unset bindings are treated as NULL.
     */
    public void clearBindings() {
        synchronized (this) {
            mDatabase.verifyDbIsOpen();
            acquireReference();
            try {
@@ -271,15 +317,22 @@ public abstract class SQLiteProgram extends SQLiteClosable {
                releaseReference();
            }
        }
    }

    /**
     * Release this program's resources, making it invalid.
     */
    public void close() {
        if (!mDatabase.isOpen()) {
        synchronized (this) {
            if (nStatement == 0 || nHandle == 0 || !mDatabase.isOpen()) {
                return;
            }
            releaseReference();
            // set all database objects to null/0, so that the user can't use a closed Object.
            mCompiledSql = null;
            nStatement = 0;
            nHandle = 0;
        }
    }

    /**
+47 −36
Original line number Diff line number Diff line
@@ -31,6 +31,9 @@ import dalvik.system.BlockGuard;
 */
public class SQLiteStatement extends SQLiteProgram
{
    private static final boolean READ = true;
    private static final boolean WRITE = false;

    /**
     * Don't use SQLiteStatement constructor directly, please use
     * {@link SQLiteDatabase#compileStatement(String)}
@@ -49,19 +52,12 @@ public class SQLiteStatement extends SQLiteProgram
     *         some reason
     */
    public void execute() {
        mDatabase.verifyDbIsOpen();
        BlockGuard.getThreadPolicy().onWriteToDisk();
        long timeStart = SystemClock.uptimeMillis();
        mDatabase.lock();

        acquireReference();
        long timeStart = acquireAndLock(WRITE);
        try {
            mDatabase.closePendingStatements();
            native_execute();
            mDatabase.logTimeStat(mSql, timeStart);
        } finally {
            releaseReference();
            mDatabase.unlock();
            releaseAndUnlock();
        }
    }

@@ -75,20 +71,13 @@ public class SQLiteStatement extends SQLiteProgram
     *         some reason
     */
    public long executeInsert() {
        mDatabase.verifyDbIsOpen();
        BlockGuard.getThreadPolicy().onWriteToDisk();
        long timeStart = SystemClock.uptimeMillis();
        mDatabase.lock();

        acquireReference();
        long timeStart = acquireAndLock(WRITE);
        try {
            mDatabase.closePendingStatements();
            native_execute();
            mDatabase.logTimeStat(mSql, timeStart);
            return (mDatabase.lastChangeCount() > 0) ? mDatabase.lastInsertRow() : -1;
        } finally {
            releaseReference();
            mDatabase.unlock();
            releaseAndUnlock();
        }
    }

@@ -101,20 +90,13 @@ public class SQLiteStatement extends SQLiteProgram
     * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
     */
    public long simpleQueryForLong() {
        mDatabase.verifyDbIsOpen();
        BlockGuard.getThreadPolicy().onReadFromDisk();
        long timeStart = SystemClock.uptimeMillis();
        mDatabase.lock();

        acquireReference();
        long timeStart = acquireAndLock(READ);
        try {
            mDatabase.closePendingStatements();
            long retValue = native_1x1_long();
            mDatabase.logTimeStat(mSql, timeStart);
            return retValue;
        } finally {
            releaseReference();
            mDatabase.unlock();
            releaseAndUnlock();
        }
    }

@@ -127,22 +109,51 @@ public class SQLiteStatement extends SQLiteProgram
     * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
     */
    public String simpleQueryForString() {
        mDatabase.verifyDbIsOpen();
        BlockGuard.getThreadPolicy().onReadFromDisk();
        long timeStart = SystemClock.uptimeMillis();
        mDatabase.lock();

        acquireReference();
        long timeStart = acquireAndLock(READ);
        try {
            mDatabase.closePendingStatements();
            String retValue = native_1x1_string();
            mDatabase.logTimeStat(mSql, timeStart);
            return retValue;
        } finally {
            releaseAndUnlock();
        }
    }

    /**
     * Called before every method in this class before executing a SQL statement,
     * this method does the following:
     * <ul>
     *   <li>make sure the database is open</li>
     *   <li>notifies {@link BlockGuard} of read/write</li>
     *   <li>get lock on the database</li>
     *   <li>acquire reference on this object</li>
     *   <li>and then return the current time _before_ the database lock was acquired</li>
     * </ul>
     * <p>
     * This method removes the duplcate code from the other public
     * methods in this class.
     */
    private long acquireAndLock(boolean rwFlag) {
        mDatabase.verifyDbIsOpen();
        if (rwFlag == WRITE) {
            BlockGuard.getThreadPolicy().onWriteToDisk();
        } else {
            BlockGuard.getThreadPolicy().onReadFromDisk();
        }
        long startTime = SystemClock.uptimeMillis();
        mDatabase.lock();
        acquireReference();
        mDatabase.closePendingStatements();
        return startTime;
    }

    /**
     * this method releases locks and references acquired in {@link #acquireAndLock(boolean)}.
     */
    private void releaseAndUnlock() {
        releaseReference();
        mDatabase.unlock();
    }
    }

    private final native void native_execute();
    private final native long native_1x1_long();
+0 −23
Original line number Diff line number Diff line
@@ -1102,29 +1102,6 @@ public class DatabaseGeneralTest extends AndroidTestCase implements PerformanceT
        }
    }

    @SmallTest
    public void testLruCachingOfSqliteCompiledSqlObjs() {
        mDatabase.execSQL("CREATE TABLE test (i int, j int);");
        mDatabase.execSQL("insert into test values(1,1);");
        // set cache size
        int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
        mDatabase.setMaxSqlCacheSize(N);

        // do N+1 queries - and when the 0th entry is removed from LRU cache due to the
        // insertion of (N+1)th entry, make sure 0th entry is closed
        ArrayList<SQLiteStatement> stmtObjs = new ArrayList<SQLiteStatement>();
        for (int i = 0; i < N+1; i++) {
            SQLiteStatement c = mDatabase.compileStatement("select * from test where i = " + i);
            c.close();
            stmtObjs.add(i, c);
        }

        assertEquals(0, stmtObjs.get(0).getUniqueId());
        for (int i = 1; i < N+1; i++) {
            assertTrue(stmtObjs.get(i).getUniqueId() > 0);
        }
    }

    @SmallTest
    public void testSetMaxCahesize() {
        mDatabase.execSQL("CREATE TABLE test (i int, j int);");
+38 −0
Original line number Diff line number Diff line
@@ -18,12 +18,16 @@ package android.database.sqlite;

import android.content.Context;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;

import java.io.File;
import java.util.ArrayList;

public class SQLiteDatabaseTest extends AndroidTestCase {
    private static final String TAG = "DatabaseGeneralTest";
@@ -125,6 +129,7 @@ public class SQLiteDatabaseTest extends AndroidTestCase {
     * </ul>
     */
    @LargeTest
    @Suppress // run this test only if you need to collect the numbers from this test
    public void testConcurrencyEffectsOfConnPool() throws Exception {
        // run the test with sqlite WAL enable
        runConnectionPoolTest(true);
@@ -271,4 +276,37 @@ public class SQLiteDatabaseTest extends AndroidTestCase {
            setCounts(readerNum, numReads);
        }
    }

    @SmallTest
    public void testLruCachingOfSqliteCompiledSqlObjs() {
        mDatabase.execSQL("CREATE TABLE test (i int, j int);");
        mDatabase.execSQL("insert into test values(1,1);");
        // set cache size
        int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
        mDatabase.setMaxSqlCacheSize(N);

        // do N+1 queries - and when the 0th entry is removed from LRU cache due to the
        // insertion of (N+1)th entry, make sure 0th entry is closed
        ArrayList<Integer> stmtObjs = new ArrayList<Integer>();
        ArrayList<String> sqlStrings = new ArrayList<String>();
        SQLiteStatement stmt0 = null;
        for (int i = 0; i < N+1; i++) {
            String s = "select * from test where i = " + i;
            sqlStrings.add(s);
            SQLiteStatement c = mDatabase.compileStatement(s);
            stmtObjs.add(i, c.getUniqueId());
            if (i == 0) {
                // save thie SQLiteStatement obj. we want to make sure it is thrown out of
                // the cache and its handle is 0'ed.
                stmt0 = c;
            }
            c.close();
        }
        // is 0'th entry out of the cache?
        assertEquals(0, stmt0.getUniqueId());
        for (int i = 1; i < N+1; i++) {
            SQLiteCompiledSql compSql = mDatabase.getCompiledStatementForSql(sqlStrings.get(i));
            assertTrue(stmtObjs.contains(compSql.nStatement));
        }
    }
}