Loading core/java/android/database/sqlite/SQLiteDatabase.java +5 −2 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package android.database.sqlite; import android.app.ActivityThread; import android.app.AppGlobals; import android.content.ContentValues; import android.database.Cursor; Loading Loading @@ -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(); Loading Loading @@ -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"); } } Loading core/java/android/database/sqlite/SQLiteProgram.java +113 −60 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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. Loading @@ -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. } Loading @@ -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 Loading @@ -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; } /** Loading @@ -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; Loading @@ -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 { Loading @@ -184,6 +220,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { releaseReference(); } } } /** * Bind a long value to this statement. The value remains bound until Loading @@ -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 { Loading @@ -201,6 +239,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { releaseReference(); } } } /** * Bind a double value to this statement. The value remains bound until Loading @@ -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 { Loading @@ -218,6 +258,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { releaseReference(); } } } /** * Bind a String value to this statement. The value remains bound until Loading @@ -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 { Loading @@ -238,6 +280,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { releaseReference(); } } } /** * Bind a byte array value to this statement. The value remains bound until Loading @@ -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 { Loading @@ -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 { Loading @@ -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; } } /** Loading core/java/android/database/sqlite/SQLiteStatement.java +47 −36 Original line number Diff line number Diff line Loading @@ -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)} Loading @@ -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(); } } Loading @@ -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(); } } Loading @@ -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(); } } Loading @@ -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(); Loading core/tests/coretests/src/android/database/DatabaseGeneralTest.java +0 −23 Original line number Diff line number Diff line Loading @@ -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);"); Loading core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java +38 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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); Loading Loading @@ -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)); } } } Loading
core/java/android/database/sqlite/SQLiteDatabase.java +5 −2 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package android.database.sqlite; import android.app.ActivityThread; import android.app.AppGlobals; import android.content.ContentValues; import android.database.Cursor; Loading Loading @@ -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(); Loading Loading @@ -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"); } } Loading
core/java/android/database/sqlite/SQLiteProgram.java +113 −60 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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. Loading @@ -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. } Loading @@ -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 Loading @@ -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; } /** Loading @@ -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; Loading @@ -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 { Loading @@ -184,6 +220,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { releaseReference(); } } } /** * Bind a long value to this statement. The value remains bound until Loading @@ -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 { Loading @@ -201,6 +239,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { releaseReference(); } } } /** * Bind a double value to this statement. The value remains bound until Loading @@ -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 { Loading @@ -218,6 +258,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { releaseReference(); } } } /** * Bind a String value to this statement. The value remains bound until Loading @@ -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 { Loading @@ -238,6 +280,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { releaseReference(); } } } /** * Bind a byte array value to this statement. The value remains bound until Loading @@ -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 { Loading @@ -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 { Loading @@ -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; } } /** Loading
core/java/android/database/sqlite/SQLiteStatement.java +47 −36 Original line number Diff line number Diff line Loading @@ -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)} Loading @@ -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(); } } Loading @@ -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(); } } Loading @@ -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(); } } Loading @@ -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(); Loading
core/tests/coretests/src/android/database/DatabaseGeneralTest.java +0 −23 Original line number Diff line number Diff line Loading @@ -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);"); Loading
core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java +38 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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); Loading Loading @@ -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)); } } }