Loading core/java/android/database/sqlite/SQLiteProgram.java +97 −50 Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ package android.database.sqlite; import android.util.Log; import android.util.Pair; import java.util.ArrayList; /** * A base class for compiled SQLite programs. Loading @@ -29,7 +32,7 @@ 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; /* package */ static final int SELECT_STMT = 1; private static final int UPDATE_STMT = 2; private static final int OTHER_STMT = 3; Loading Loading @@ -63,13 +66,40 @@ public abstract class SQLiteProgram extends SQLiteClosable { @Deprecated protected int nStatement = 0; /** * In the case of {@link SQLiteStatement}, this member stores the bindargs passed * to the following methods, instead of actually doing the binding. * <ul> * <li>{@link #bindBlob(int, byte[])}</li> * <li>{@link #bindDouble(int, double)}</li> * <li>{@link #bindLong(int, long)}</li> * <li>{@link #bindNull(int)}</li> * <li>{@link #bindString(int, String)}</li> * </ul> * <p> * Each entry in the array is a Pair of * <ol> * <li>bind arg position number</li> * <li>the value to be bound to the bindarg</li> * </ol> * <p> * It is lazily initialized in the above bind methods * and it is cleared in {@link #clearBindings()} method. * <p> * It is protected (in multi-threaded environment) by {@link SQLiteProgram}.this */ private ArrayList<Pair<Integer, Object>> bindArgs = null; /* package */ SQLiteProgram(SQLiteDatabase db, String sql) { this(db, sql, true); } /* package */ SQLiteProgram(SQLiteDatabase db, String sql, boolean compileFlag) { mSql = sql.trim(); attachObjectToDatabase(db); db.acquireReference(); db.addSQLiteClosable(this); mDatabase = db; nHandle = db.mNativeHandle; if (compileFlag) { compileSql(); } Loading Loading @@ -120,7 +150,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { nStatement = mCompiledSql.nStatement; } private int getSqlStatementType(String sql) { /* package */ int getSqlStatementType(String sql) { if (mSql.length() < 6) { return OTHER_STMT; } Loading @@ -136,46 +166,11 @@ public abstract class SQLiteProgram extends SQLiteClosable { return OTHER_STMT; } private synchronized void attachObjectToDatabase(SQLiteDatabase db) { db.acquireReference(); db.addSQLiteClosable(this); mDatabase = db; nHandle = db.mNativeHandle; } private synchronized void detachObjectFromDatabase() { mDatabase.removeSQLiteClosable(this); mDatabase.releaseReference(); } /* package */ synchronized void verifyDbAndCompileSql() { mDatabase.verifyDbIsOpen(); // use pooled database connection handles for SELECT SQL statements SQLiteDatabase db = (getSqlStatementType(mSql) != SELECT_STMT) ? mDatabase : mDatabase.getDbConnection(mSql); if (!db.equals(mDatabase)) { // the database connection handle to be used is not the same as the one supplied // in the constructor. do some housekeeping. detachObjectFromDatabase(); attachObjectToDatabase(db); } // compile the sql statement if (nStatement > 0) { // already compiled. return; } mDatabase.lock(); try { compileSql(); } finally { mDatabase.unlock(); } } @Override protected void onAllReferencesReleased() { releaseCompiledSqlIfNotInCache(); detachObjectFromDatabase(); mDatabase.removeSQLiteClosable(this); mDatabase.releaseReference(); } @Override Loading Loading @@ -246,11 +241,17 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @param index The 1-based index to the parameter to bind null to */ public void bindNull(int index) { mDatabase.verifyDbIsOpen(); synchronized (this) { verifyDbAndCompileSql(); acquireReference(); try { if (this.nStatement == 0) { // since the SQL statement is not compiled, don't do the binding yet. // can be done before executing the SQL statement addToBindArgs(index, null); } else { native_bind_null(index); } } finally { releaseReference(); } Loading @@ -265,11 +266,15 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @param value The value to bind */ public void bindLong(int index, long value) { mDatabase.verifyDbIsOpen(); synchronized (this) { verifyDbAndCompileSql(); acquireReference(); try { if (this.nStatement == 0) { addToBindArgs(index, value); } else { native_bind_long(index, value); } } finally { releaseReference(); } Loading @@ -284,11 +289,15 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @param value The value to bind */ public void bindDouble(int index, double value) { mDatabase.verifyDbIsOpen(); synchronized (this) { verifyDbAndCompileSql(); acquireReference(); try { if (this.nStatement == 0) { addToBindArgs(index, value); } else { native_bind_double(index, value); } } finally { releaseReference(); } Loading @@ -306,11 +315,15 @@ public abstract class SQLiteProgram extends SQLiteClosable { if (value == null) { throw new IllegalArgumentException("the bind value at index " + index + " is null"); } mDatabase.verifyDbIsOpen(); synchronized (this) { verifyDbAndCompileSql(); acquireReference(); try { if (this.nStatement == 0) { addToBindArgs(index, value); } else { native_bind_string(index, value); } } finally { releaseReference(); } Loading @@ -328,11 +341,15 @@ public abstract class SQLiteProgram extends SQLiteClosable { if (value == null) { throw new IllegalArgumentException("the bind value at index " + index + " is null"); } mDatabase.verifyDbIsOpen(); synchronized (this) { verifyDbAndCompileSql(); acquireReference(); try { if (this.nStatement == 0) { addToBindArgs(index, value); } else { native_bind_blob(index, value); } } finally { releaseReference(); } Loading @@ -344,6 +361,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { */ public void clearBindings() { synchronized (this) { bindArgs = null; if (this.nStatement == 0) { return; } Loading @@ -362,6 +380,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { */ public void close() { synchronized (this) { bindArgs = null; if (nHandle == 0 || !mDatabase.isOpen()) { return; } Loading @@ -369,6 +388,34 @@ public abstract class SQLiteProgram extends SQLiteClosable { } } private synchronized void addToBindArgs(int index, Object value) { if (bindArgs == null) { bindArgs = new ArrayList<Pair<Integer, Object>>(); } bindArgs.add(new Pair<Integer, Object>(index, value)); } /* package */ synchronized void compileAndbindAllArgs() { assert nStatement == 0; compileSql(); if (bindArgs == null) { return; } for (Pair<Integer, Object> p : bindArgs) { if (p.second == null) { native_bind_null(p.first); } else if (p.second instanceof Long) { native_bind_long(p.first, (Long)p.second); } else if (p.second instanceof Double) { native_bind_double(p.first, (Double)p.second); } else if (p.second instanceof byte[]) { native_bind_blob(p.first, (byte[])p.second); } else { native_bind_string(p.first, (String)p.second); } } } /** * @deprecated This method is deprecated and must not be used. * Compiles SQL into a SQLite program. Loading core/java/android/database/sqlite/SQLiteStatement.java +50 −28 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ public class SQLiteStatement extends SQLiteProgram private static final boolean READ = true; private static final boolean WRITE = false; private SQLiteDatabase mOrigDb; /** * Don't use SQLiteStatement constructor directly, please use * {@link SQLiteDatabase#compileStatement(String)} Loading @@ -53,6 +55,7 @@ public class SQLiteStatement extends SQLiteProgram * some reason */ public void execute() { synchronized(this) { long timeStart = acquireAndLock(WRITE); try { native_execute(); Loading @@ -61,6 +64,7 @@ public class SQLiteStatement extends SQLiteProgram releaseAndUnlock(); } } } /** * Execute this SQL statement and return the ID of the row inserted due to this call. Loading @@ -72,6 +76,7 @@ public class SQLiteStatement extends SQLiteProgram * some reason */ public long executeInsert() { synchronized(this) { long timeStart = acquireAndLock(WRITE); try { native_execute(); Loading @@ -81,6 +86,7 @@ public class SQLiteStatement extends SQLiteProgram releaseAndUnlock(); } } } /** * Execute a statement that returns a 1 by 1 table with a numeric value. Loading @@ -91,6 +97,7 @@ public class SQLiteStatement extends SQLiteProgram * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows */ public long simpleQueryForLong() { synchronized(this) { long timeStart = acquireAndLock(READ); try { long retValue = native_1x1_long(); Loading @@ -100,6 +107,7 @@ public class SQLiteStatement extends SQLiteProgram releaseAndUnlock(); } } } /** * Execute a statement that returns a 1 by 1 table with a text value. Loading @@ -110,6 +118,7 @@ public class SQLiteStatement extends SQLiteProgram * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows */ public String simpleQueryForString() { synchronized(this) { long timeStart = acquireAndLock(READ); try { String retValue = native_1x1_string(); Loading @@ -119,12 +128,14 @@ public class SQLiteStatement extends SQLiteProgram 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>get a database connection from the connection pool,if possible</li> * <li>notifies {@link BlockGuard} of read/write</li> * <li>get lock on the database</li> * <li>acquire reference on this object</li> Loading @@ -135,7 +146,14 @@ public class SQLiteStatement extends SQLiteProgram * methods in this class. */ private long acquireAndLock(boolean rwFlag) { verifyDbAndCompileSql(); // use pooled database connection handles for SELECT SQL statements mDatabase.verifyDbIsOpen(); SQLiteDatabase db = (getSqlStatementType(mSql) != SELECT_STMT) ? mDatabase : mDatabase.getDbConnection(mSql); // use the database connection obtained above mOrigDb = mDatabase; mDatabase = db; nHandle = mDatabase.mNativeHandle; if (rwFlag == WRITE) { BlockGuard.getThreadPolicy().onWriteToDisk(); } else { Loading @@ -145,6 +163,7 @@ public class SQLiteStatement extends SQLiteProgram mDatabase.lock(); acquireReference(); mDatabase.closePendingStatements(); compileAndbindAllArgs(); return startTime; } Loading @@ -158,6 +177,9 @@ public class SQLiteStatement extends SQLiteProgram // 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(); // restore the database connection handle to the original value mDatabase = mOrigDb; nHandle = mDatabase.mNativeHandle; } private final native void native_execute(); Loading core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java +53 −40 Original line number Diff line number Diff line Loading @@ -279,11 +279,24 @@ public class SQLiteDatabaseTest extends AndroidTestCase { } } private static class ClassToTestSqlCompilationAndCaching extends SQLiteProgram { private ClassToTestSqlCompilationAndCaching(SQLiteDatabase db, String sql) { super(db, sql); } private static ClassToTestSqlCompilationAndCaching create(SQLiteDatabase db, String sql) { db.lock(); try { return new ClassToTestSqlCompilationAndCaching(db, sql); } finally { db.unlock(); } } } @SmallTest public void testLruCachingOfSqliteCompiledSqlObjs() { mDatabase.disableWriteAheadLogging(); 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); Loading @@ -292,22 +305,24 @@ public class SQLiteDatabaseTest extends AndroidTestCase { // 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; int stmt0 = 0; for (int i = 0; i < N+1; i++) { String s = "select * from test where i = " + i + " and j = ?"; String s = "insert into test values(" + i + ",?);"; sqlStrings.add(s); SQLiteStatement c = mDatabase.compileStatement(s); c.bindLong(1, 1); stmtObjs.add(i, c.getSqlStatementId()); ClassToTestSqlCompilationAndCaching c = ClassToTestSqlCompilationAndCaching.create(mDatabase, s); int n = c.getSqlStatementId(); stmtObjs.add(i, n); 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; // save the statementId of this obj. we want to make sure it is thrown out of // the cache at the end of this test. stmt0 = n; } c.close(); } // is 0'th entry out of the cache? assertEquals(0, stmt0.getSqlStatementId()); // is 0'th entry out of the cache? it should be in the list of statementIds // corresponding to the pre-compiled sql statements to be finalized. assertTrue(mDatabase.getQueuedUpStmtList().contains(stmt0)); for (int i = 1; i < N+1; i++) { SQLiteCompiledSql compSql = mDatabase.getCompiledStatementForSql(sqlStrings.get(i)); assertNotNull(compSql); Loading @@ -321,11 +336,7 @@ public class SQLiteDatabaseTest extends AndroidTestCase { "num1 INTEGER, num2 INTEGER, image BLOB);"); final String statement = "DELETE FROM test WHERE _id=?;"; SQLiteStatement statementDoNotClose = mDatabase.compileStatement(statement); // SQl statement is compiled only at find bind or execute call assertTrue(statementDoNotClose.getSqlStatementId() == 0); statementDoNotClose.bindLong(1, 1); assertTrue(statementDoNotClose.getSqlStatementId() > 0); int nStatement = statementDoNotClose.getSqlStatementId(); /* do not close statementDoNotClose object. * That should leave it in SQLiteDatabase.mPrograms. * mDatabase.close() in tearDown() should release it. Loading @@ -340,24 +351,25 @@ public class SQLiteDatabaseTest extends AndroidTestCase { public void testStatementClose() { mDatabase.execSQL("CREATE TABLE test (i int, j int);"); // fill up statement cache in mDatabase\ int N = 26; int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE; mDatabase.setMaxSqlCacheSize(N); SQLiteStatement stmt; int stmt0Id = 0; for (int i = 0; i < N; i ++) { stmt = mDatabase.compileStatement("insert into test values(" + i + ", ?);"); stmt.bindLong(1, 1); ClassToTestSqlCompilationAndCaching c = ClassToTestSqlCompilationAndCaching.create(mDatabase, "insert into test values(" + i + ", ?);"); // keep track of 0th entry if (i == 0) { stmt0Id = stmt.getSqlStatementId(); stmt0Id = c.getSqlStatementId(); } stmt.executeInsert(); stmt.close(); c.close(); } // add one more to the cache - and the above 'stmt0Id' should fall out of cache SQLiteStatement stmt1 = mDatabase.compileStatement("insert into test values(100, ?);"); stmt1.bindLong(1, 1); ClassToTestSqlCompilationAndCaching stmt1 = ClassToTestSqlCompilationAndCaching.create(mDatabase, "insert into test values(100, ?);"); stmt1.close(); // the above close() should have queuedUp the statement for finalization Loading @@ -381,18 +393,18 @@ public class SQLiteDatabaseTest extends AndroidTestCase { // fill up statement cache in mDatabase in a thread Thread t1 = new Thread() { @Override public void run() { int N = 26; int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE; mDatabase.setMaxSqlCacheSize(N); SQLiteStatement stmt; for (int i = 0; i < N; i ++) { stmt = mDatabase.compileStatement("insert into test values(" + i + ", ?);"); stmt.bindLong(1,1); ClassToTestSqlCompilationAndCaching c = ClassToTestSqlCompilationAndCaching.create(mDatabase, "insert into test values(" + i + ", ?);"); // keep track of 0th entry if (i == 0) { setStmt0Id(stmt.getSqlStatementId()); stmt0Id = c.getSqlStatementId(); } stmt.executeInsert(); stmt.close(); c.close(); } } }; Loading @@ -404,9 +416,9 @@ public class SQLiteDatabaseTest extends AndroidTestCase { // just for the heck of it, do it in a separate thread Thread t2 = new Thread() { @Override public void run() { SQLiteStatement stmt1 = mDatabase.compileStatement( ClassToTestSqlCompilationAndCaching stmt1 = ClassToTestSqlCompilationAndCaching.create(mDatabase, "insert into test values(100, ?);"); stmt1.bindLong(1, 1); stmt1.close(); } }; Loading Loading @@ -452,18 +464,18 @@ public class SQLiteDatabaseTest extends AndroidTestCase { // fill up statement cache in mDatabase in a thread Thread t1 = new Thread() { @Override public void run() { int N = 26; int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE; mDatabase.setMaxSqlCacheSize(N); SQLiteStatement stmt; for (int i = 0; i < N; i ++) { stmt = mDatabase.compileStatement("insert into test values(" + i + ", ?);"); stmt.bindLong(1, 1); ClassToTestSqlCompilationAndCaching c = ClassToTestSqlCompilationAndCaching.create(mDatabase, "insert into test values(" + i + ", ?);"); // keep track of 0th entry if (i == 0) { setStmt0Id(stmt.getSqlStatementId()); stmt0Id = c.getSqlStatementId(); } stmt.executeInsert(); stmt.close(); c.close(); } } }; Loading @@ -475,7 +487,8 @@ public class SQLiteDatabaseTest extends AndroidTestCase { // just for the heck of it, do it in a separate thread Thread t2 = new Thread() { @Override public void run() { SQLiteStatement stmt1 = mDatabase.compileStatement( ClassToTestSqlCompilationAndCaching stmt1 = ClassToTestSqlCompilationAndCaching.create(mDatabase, "insert into test values(100, ?);"); stmt1.bindLong(1, 1); stmt1.close(); Loading core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java +16 −86 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/database/sqlite/SQLiteProgram.java +97 −50 Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ package android.database.sqlite; import android.util.Log; import android.util.Pair; import java.util.ArrayList; /** * A base class for compiled SQLite programs. Loading @@ -29,7 +32,7 @@ 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; /* package */ static final int SELECT_STMT = 1; private static final int UPDATE_STMT = 2; private static final int OTHER_STMT = 3; Loading Loading @@ -63,13 +66,40 @@ public abstract class SQLiteProgram extends SQLiteClosable { @Deprecated protected int nStatement = 0; /** * In the case of {@link SQLiteStatement}, this member stores the bindargs passed * to the following methods, instead of actually doing the binding. * <ul> * <li>{@link #bindBlob(int, byte[])}</li> * <li>{@link #bindDouble(int, double)}</li> * <li>{@link #bindLong(int, long)}</li> * <li>{@link #bindNull(int)}</li> * <li>{@link #bindString(int, String)}</li> * </ul> * <p> * Each entry in the array is a Pair of * <ol> * <li>bind arg position number</li> * <li>the value to be bound to the bindarg</li> * </ol> * <p> * It is lazily initialized in the above bind methods * and it is cleared in {@link #clearBindings()} method. * <p> * It is protected (in multi-threaded environment) by {@link SQLiteProgram}.this */ private ArrayList<Pair<Integer, Object>> bindArgs = null; /* package */ SQLiteProgram(SQLiteDatabase db, String sql) { this(db, sql, true); } /* package */ SQLiteProgram(SQLiteDatabase db, String sql, boolean compileFlag) { mSql = sql.trim(); attachObjectToDatabase(db); db.acquireReference(); db.addSQLiteClosable(this); mDatabase = db; nHandle = db.mNativeHandle; if (compileFlag) { compileSql(); } Loading Loading @@ -120,7 +150,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { nStatement = mCompiledSql.nStatement; } private int getSqlStatementType(String sql) { /* package */ int getSqlStatementType(String sql) { if (mSql.length() < 6) { return OTHER_STMT; } Loading @@ -136,46 +166,11 @@ public abstract class SQLiteProgram extends SQLiteClosable { return OTHER_STMT; } private synchronized void attachObjectToDatabase(SQLiteDatabase db) { db.acquireReference(); db.addSQLiteClosable(this); mDatabase = db; nHandle = db.mNativeHandle; } private synchronized void detachObjectFromDatabase() { mDatabase.removeSQLiteClosable(this); mDatabase.releaseReference(); } /* package */ synchronized void verifyDbAndCompileSql() { mDatabase.verifyDbIsOpen(); // use pooled database connection handles for SELECT SQL statements SQLiteDatabase db = (getSqlStatementType(mSql) != SELECT_STMT) ? mDatabase : mDatabase.getDbConnection(mSql); if (!db.equals(mDatabase)) { // the database connection handle to be used is not the same as the one supplied // in the constructor. do some housekeeping. detachObjectFromDatabase(); attachObjectToDatabase(db); } // compile the sql statement if (nStatement > 0) { // already compiled. return; } mDatabase.lock(); try { compileSql(); } finally { mDatabase.unlock(); } } @Override protected void onAllReferencesReleased() { releaseCompiledSqlIfNotInCache(); detachObjectFromDatabase(); mDatabase.removeSQLiteClosable(this); mDatabase.releaseReference(); } @Override Loading Loading @@ -246,11 +241,17 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @param index The 1-based index to the parameter to bind null to */ public void bindNull(int index) { mDatabase.verifyDbIsOpen(); synchronized (this) { verifyDbAndCompileSql(); acquireReference(); try { if (this.nStatement == 0) { // since the SQL statement is not compiled, don't do the binding yet. // can be done before executing the SQL statement addToBindArgs(index, null); } else { native_bind_null(index); } } finally { releaseReference(); } Loading @@ -265,11 +266,15 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @param value The value to bind */ public void bindLong(int index, long value) { mDatabase.verifyDbIsOpen(); synchronized (this) { verifyDbAndCompileSql(); acquireReference(); try { if (this.nStatement == 0) { addToBindArgs(index, value); } else { native_bind_long(index, value); } } finally { releaseReference(); } Loading @@ -284,11 +289,15 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @param value The value to bind */ public void bindDouble(int index, double value) { mDatabase.verifyDbIsOpen(); synchronized (this) { verifyDbAndCompileSql(); acquireReference(); try { if (this.nStatement == 0) { addToBindArgs(index, value); } else { native_bind_double(index, value); } } finally { releaseReference(); } Loading @@ -306,11 +315,15 @@ public abstract class SQLiteProgram extends SQLiteClosable { if (value == null) { throw new IllegalArgumentException("the bind value at index " + index + " is null"); } mDatabase.verifyDbIsOpen(); synchronized (this) { verifyDbAndCompileSql(); acquireReference(); try { if (this.nStatement == 0) { addToBindArgs(index, value); } else { native_bind_string(index, value); } } finally { releaseReference(); } Loading @@ -328,11 +341,15 @@ public abstract class SQLiteProgram extends SQLiteClosable { if (value == null) { throw new IllegalArgumentException("the bind value at index " + index + " is null"); } mDatabase.verifyDbIsOpen(); synchronized (this) { verifyDbAndCompileSql(); acquireReference(); try { if (this.nStatement == 0) { addToBindArgs(index, value); } else { native_bind_blob(index, value); } } finally { releaseReference(); } Loading @@ -344,6 +361,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { */ public void clearBindings() { synchronized (this) { bindArgs = null; if (this.nStatement == 0) { return; } Loading @@ -362,6 +380,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { */ public void close() { synchronized (this) { bindArgs = null; if (nHandle == 0 || !mDatabase.isOpen()) { return; } Loading @@ -369,6 +388,34 @@ public abstract class SQLiteProgram extends SQLiteClosable { } } private synchronized void addToBindArgs(int index, Object value) { if (bindArgs == null) { bindArgs = new ArrayList<Pair<Integer, Object>>(); } bindArgs.add(new Pair<Integer, Object>(index, value)); } /* package */ synchronized void compileAndbindAllArgs() { assert nStatement == 0; compileSql(); if (bindArgs == null) { return; } for (Pair<Integer, Object> p : bindArgs) { if (p.second == null) { native_bind_null(p.first); } else if (p.second instanceof Long) { native_bind_long(p.first, (Long)p.second); } else if (p.second instanceof Double) { native_bind_double(p.first, (Double)p.second); } else if (p.second instanceof byte[]) { native_bind_blob(p.first, (byte[])p.second); } else { native_bind_string(p.first, (String)p.second); } } } /** * @deprecated This method is deprecated and must not be used. * Compiles SQL into a SQLite program. Loading
core/java/android/database/sqlite/SQLiteStatement.java +50 −28 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ public class SQLiteStatement extends SQLiteProgram private static final boolean READ = true; private static final boolean WRITE = false; private SQLiteDatabase mOrigDb; /** * Don't use SQLiteStatement constructor directly, please use * {@link SQLiteDatabase#compileStatement(String)} Loading @@ -53,6 +55,7 @@ public class SQLiteStatement extends SQLiteProgram * some reason */ public void execute() { synchronized(this) { long timeStart = acquireAndLock(WRITE); try { native_execute(); Loading @@ -61,6 +64,7 @@ public class SQLiteStatement extends SQLiteProgram releaseAndUnlock(); } } } /** * Execute this SQL statement and return the ID of the row inserted due to this call. Loading @@ -72,6 +76,7 @@ public class SQLiteStatement extends SQLiteProgram * some reason */ public long executeInsert() { synchronized(this) { long timeStart = acquireAndLock(WRITE); try { native_execute(); Loading @@ -81,6 +86,7 @@ public class SQLiteStatement extends SQLiteProgram releaseAndUnlock(); } } } /** * Execute a statement that returns a 1 by 1 table with a numeric value. Loading @@ -91,6 +97,7 @@ public class SQLiteStatement extends SQLiteProgram * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows */ public long simpleQueryForLong() { synchronized(this) { long timeStart = acquireAndLock(READ); try { long retValue = native_1x1_long(); Loading @@ -100,6 +107,7 @@ public class SQLiteStatement extends SQLiteProgram releaseAndUnlock(); } } } /** * Execute a statement that returns a 1 by 1 table with a text value. Loading @@ -110,6 +118,7 @@ public class SQLiteStatement extends SQLiteProgram * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows */ public String simpleQueryForString() { synchronized(this) { long timeStart = acquireAndLock(READ); try { String retValue = native_1x1_string(); Loading @@ -119,12 +128,14 @@ public class SQLiteStatement extends SQLiteProgram 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>get a database connection from the connection pool,if possible</li> * <li>notifies {@link BlockGuard} of read/write</li> * <li>get lock on the database</li> * <li>acquire reference on this object</li> Loading @@ -135,7 +146,14 @@ public class SQLiteStatement extends SQLiteProgram * methods in this class. */ private long acquireAndLock(boolean rwFlag) { verifyDbAndCompileSql(); // use pooled database connection handles for SELECT SQL statements mDatabase.verifyDbIsOpen(); SQLiteDatabase db = (getSqlStatementType(mSql) != SELECT_STMT) ? mDatabase : mDatabase.getDbConnection(mSql); // use the database connection obtained above mOrigDb = mDatabase; mDatabase = db; nHandle = mDatabase.mNativeHandle; if (rwFlag == WRITE) { BlockGuard.getThreadPolicy().onWriteToDisk(); } else { Loading @@ -145,6 +163,7 @@ public class SQLiteStatement extends SQLiteProgram mDatabase.lock(); acquireReference(); mDatabase.closePendingStatements(); compileAndbindAllArgs(); return startTime; } Loading @@ -158,6 +177,9 @@ public class SQLiteStatement extends SQLiteProgram // 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(); // restore the database connection handle to the original value mDatabase = mOrigDb; nHandle = mDatabase.mNativeHandle; } private final native void native_execute(); Loading
core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java +53 −40 Original line number Diff line number Diff line Loading @@ -279,11 +279,24 @@ public class SQLiteDatabaseTest extends AndroidTestCase { } } private static class ClassToTestSqlCompilationAndCaching extends SQLiteProgram { private ClassToTestSqlCompilationAndCaching(SQLiteDatabase db, String sql) { super(db, sql); } private static ClassToTestSqlCompilationAndCaching create(SQLiteDatabase db, String sql) { db.lock(); try { return new ClassToTestSqlCompilationAndCaching(db, sql); } finally { db.unlock(); } } } @SmallTest public void testLruCachingOfSqliteCompiledSqlObjs() { mDatabase.disableWriteAheadLogging(); 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); Loading @@ -292,22 +305,24 @@ public class SQLiteDatabaseTest extends AndroidTestCase { // 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; int stmt0 = 0; for (int i = 0; i < N+1; i++) { String s = "select * from test where i = " + i + " and j = ?"; String s = "insert into test values(" + i + ",?);"; sqlStrings.add(s); SQLiteStatement c = mDatabase.compileStatement(s); c.bindLong(1, 1); stmtObjs.add(i, c.getSqlStatementId()); ClassToTestSqlCompilationAndCaching c = ClassToTestSqlCompilationAndCaching.create(mDatabase, s); int n = c.getSqlStatementId(); stmtObjs.add(i, n); 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; // save the statementId of this obj. we want to make sure it is thrown out of // the cache at the end of this test. stmt0 = n; } c.close(); } // is 0'th entry out of the cache? assertEquals(0, stmt0.getSqlStatementId()); // is 0'th entry out of the cache? it should be in the list of statementIds // corresponding to the pre-compiled sql statements to be finalized. assertTrue(mDatabase.getQueuedUpStmtList().contains(stmt0)); for (int i = 1; i < N+1; i++) { SQLiteCompiledSql compSql = mDatabase.getCompiledStatementForSql(sqlStrings.get(i)); assertNotNull(compSql); Loading @@ -321,11 +336,7 @@ public class SQLiteDatabaseTest extends AndroidTestCase { "num1 INTEGER, num2 INTEGER, image BLOB);"); final String statement = "DELETE FROM test WHERE _id=?;"; SQLiteStatement statementDoNotClose = mDatabase.compileStatement(statement); // SQl statement is compiled only at find bind or execute call assertTrue(statementDoNotClose.getSqlStatementId() == 0); statementDoNotClose.bindLong(1, 1); assertTrue(statementDoNotClose.getSqlStatementId() > 0); int nStatement = statementDoNotClose.getSqlStatementId(); /* do not close statementDoNotClose object. * That should leave it in SQLiteDatabase.mPrograms. * mDatabase.close() in tearDown() should release it. Loading @@ -340,24 +351,25 @@ public class SQLiteDatabaseTest extends AndroidTestCase { public void testStatementClose() { mDatabase.execSQL("CREATE TABLE test (i int, j int);"); // fill up statement cache in mDatabase\ int N = 26; int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE; mDatabase.setMaxSqlCacheSize(N); SQLiteStatement stmt; int stmt0Id = 0; for (int i = 0; i < N; i ++) { stmt = mDatabase.compileStatement("insert into test values(" + i + ", ?);"); stmt.bindLong(1, 1); ClassToTestSqlCompilationAndCaching c = ClassToTestSqlCompilationAndCaching.create(mDatabase, "insert into test values(" + i + ", ?);"); // keep track of 0th entry if (i == 0) { stmt0Id = stmt.getSqlStatementId(); stmt0Id = c.getSqlStatementId(); } stmt.executeInsert(); stmt.close(); c.close(); } // add one more to the cache - and the above 'stmt0Id' should fall out of cache SQLiteStatement stmt1 = mDatabase.compileStatement("insert into test values(100, ?);"); stmt1.bindLong(1, 1); ClassToTestSqlCompilationAndCaching stmt1 = ClassToTestSqlCompilationAndCaching.create(mDatabase, "insert into test values(100, ?);"); stmt1.close(); // the above close() should have queuedUp the statement for finalization Loading @@ -381,18 +393,18 @@ public class SQLiteDatabaseTest extends AndroidTestCase { // fill up statement cache in mDatabase in a thread Thread t1 = new Thread() { @Override public void run() { int N = 26; int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE; mDatabase.setMaxSqlCacheSize(N); SQLiteStatement stmt; for (int i = 0; i < N; i ++) { stmt = mDatabase.compileStatement("insert into test values(" + i + ", ?);"); stmt.bindLong(1,1); ClassToTestSqlCompilationAndCaching c = ClassToTestSqlCompilationAndCaching.create(mDatabase, "insert into test values(" + i + ", ?);"); // keep track of 0th entry if (i == 0) { setStmt0Id(stmt.getSqlStatementId()); stmt0Id = c.getSqlStatementId(); } stmt.executeInsert(); stmt.close(); c.close(); } } }; Loading @@ -404,9 +416,9 @@ public class SQLiteDatabaseTest extends AndroidTestCase { // just for the heck of it, do it in a separate thread Thread t2 = new Thread() { @Override public void run() { SQLiteStatement stmt1 = mDatabase.compileStatement( ClassToTestSqlCompilationAndCaching stmt1 = ClassToTestSqlCompilationAndCaching.create(mDatabase, "insert into test values(100, ?);"); stmt1.bindLong(1, 1); stmt1.close(); } }; Loading Loading @@ -452,18 +464,18 @@ public class SQLiteDatabaseTest extends AndroidTestCase { // fill up statement cache in mDatabase in a thread Thread t1 = new Thread() { @Override public void run() { int N = 26; int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE; mDatabase.setMaxSqlCacheSize(N); SQLiteStatement stmt; for (int i = 0; i < N; i ++) { stmt = mDatabase.compileStatement("insert into test values(" + i + ", ?);"); stmt.bindLong(1, 1); ClassToTestSqlCompilationAndCaching c = ClassToTestSqlCompilationAndCaching.create(mDatabase, "insert into test values(" + i + ", ?);"); // keep track of 0th entry if (i == 0) { setStmt0Id(stmt.getSqlStatementId()); stmt0Id = c.getSqlStatementId(); } stmt.executeInsert(); stmt.close(); c.close(); } } }; Loading @@ -475,7 +487,8 @@ public class SQLiteDatabaseTest extends AndroidTestCase { // just for the heck of it, do it in a separate thread Thread t2 = new Thread() { @Override public void run() { SQLiteStatement stmt1 = mDatabase.compileStatement( ClassToTestSqlCompilationAndCaching stmt1 = ClassToTestSqlCompilationAndCaching.create(mDatabase, "insert into test values(100, ?);"); stmt1.bindLong(1, 1); stmt1.close(); Loading
core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java +16 −86 File changed.Preview size limit exceeded, changes collapsed. Show changes