diff options
4 files changed, 216 insertions, 204 deletions
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java index 4df6ad102d6e..017b65f97ec5 100644 --- a/core/java/android/database/sqlite/SQLiteProgram.java +++ b/core/java/android/database/sqlite/SQLiteProgram.java @@ -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. @@ -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; @@ -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(); } @@ -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; } @@ -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 @@ -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 { - native_bind_null(index); + 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(); } @@ -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 { - native_bind_long(index, value); + if (this.nStatement == 0) { + addToBindArgs(index, value); + } else { + native_bind_long(index, value); + } } finally { releaseReference(); } @@ -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 { - native_bind_double(index, value); + if (this.nStatement == 0) { + addToBindArgs(index, value); + } else { + native_bind_double(index, value); + } } finally { releaseReference(); } @@ -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 { - native_bind_string(index, value); + if (this.nStatement == 0) { + addToBindArgs(index, value); + } else { + native_bind_string(index, value); + } } finally { releaseReference(); } @@ -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 { - native_bind_blob(index, value); + if (this.nStatement == 0) { + addToBindArgs(index, value); + } else { + native_bind_blob(index, value); + } } finally { releaseReference(); } @@ -344,6 +361,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { */ public void clearBindings() { synchronized (this) { + bindArgs = null; if (this.nStatement == 0) { return; } @@ -362,6 +380,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { */ public void close() { synchronized (this) { + bindArgs = null; if (nHandle == 0 || !mDatabase.isOpen()) { return; } @@ -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. diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java index 966459389623..b90280307ad6 100644 --- a/core/java/android/database/sqlite/SQLiteStatement.java +++ b/core/java/android/database/sqlite/SQLiteStatement.java @@ -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)} @@ -53,12 +55,14 @@ public class SQLiteStatement extends SQLiteProgram * some reason */ public void execute() { - long timeStart = acquireAndLock(WRITE); - try { - native_execute(); - mDatabase.logTimeStat(mSql, timeStart); - } finally { - releaseAndUnlock(); + synchronized(this) { + long timeStart = acquireAndLock(WRITE); + try { + native_execute(); + mDatabase.logTimeStat(mSql, timeStart); + } finally { + releaseAndUnlock(); + } } } @@ -72,13 +76,15 @@ public class SQLiteStatement extends SQLiteProgram * some reason */ public long executeInsert() { - long timeStart = acquireAndLock(WRITE); - try { - native_execute(); - mDatabase.logTimeStat(mSql, timeStart); - return (mDatabase.lastChangeCount() > 0) ? mDatabase.lastInsertRow() : -1; - } finally { - releaseAndUnlock(); + synchronized(this) { + long timeStart = acquireAndLock(WRITE); + try { + native_execute(); + mDatabase.logTimeStat(mSql, timeStart); + return (mDatabase.lastChangeCount() > 0) ? mDatabase.lastInsertRow() : -1; + } finally { + releaseAndUnlock(); + } } } @@ -91,13 +97,15 @@ public class SQLiteStatement extends SQLiteProgram * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows */ public long simpleQueryForLong() { - long timeStart = acquireAndLock(READ); - try { - long retValue = native_1x1_long(); - mDatabase.logTimeStat(mSql, timeStart); - return retValue; - } finally { - releaseAndUnlock(); + synchronized(this) { + long timeStart = acquireAndLock(READ); + try { + long retValue = native_1x1_long(); + mDatabase.logTimeStat(mSql, timeStart); + return retValue; + } finally { + releaseAndUnlock(); + } } } @@ -110,13 +118,15 @@ public class SQLiteStatement extends SQLiteProgram * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows */ public String simpleQueryForString() { - long timeStart = acquireAndLock(READ); - try { - String retValue = native_1x1_string(); - mDatabase.logTimeStat(mSql, timeStart); - return retValue; - } finally { - releaseAndUnlock(); + synchronized(this) { + long timeStart = acquireAndLock(READ); + try { + String retValue = native_1x1_string(); + mDatabase.logTimeStat(mSql, timeStart); + return retValue; + } finally { + releaseAndUnlock(); + } } } @@ -125,6 +135,7 @@ public class SQLiteStatement extends SQLiteProgram * 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> @@ -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 { @@ -145,6 +163,7 @@ public class SQLiteStatement extends SQLiteProgram mDatabase.lock(); acquireReference(); mDatabase.closePendingStatements(); + compileAndbindAllArgs(); return startTime; } @@ -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(); diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java index ca8dac13efd6..91ef0b7a3afc 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java @@ -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); @@ -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); @@ -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. @@ -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 @@ -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(); } } }; @@ -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( - "insert into test values(100, ?);"); - stmt1.bindLong(1, 1); + ClassToTestSqlCompilationAndCaching stmt1 = + ClassToTestSqlCompilationAndCaching.create(mDatabase, + "insert into test values(100, ?);"); stmt1.close(); } }; @@ -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(); } } }; @@ -475,8 +487,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( - "insert into test values(100, ?);"); + ClassToTestSqlCompilationAndCaching stmt1 = + ClassToTestSqlCompilationAndCaching.create(mDatabase, + "insert into test values(100, ?);"); stmt1.bindLong(1, 1); stmt1.close(); } diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java index eb27551b0add..217545f9cf6c 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java @@ -158,14 +158,16 @@ public class SQLiteStatementTest extends AndroidTestCase { assertEquals(0, stmt.getSqlStatementId()); int colValue = new Random().nextInt(); stmt.bindLong(1, colValue); - // verify that the sql statement is now compiled - int n = stmt.nStatement; - assertTrue(n > 0); - assertEquals(n, stmt.getSqlStatementId()); + // verify that the sql statement is still not compiled + assertEquals(0, stmt.getSqlStatementId()); // should still be using the mDatabase connection - verify assertEquals(mDatabase.mNativeHandle, stmt.nHandle); + assertEquals(mDatabase, stmt.mDatabase); stmt.bindString(2, "blah" + colValue); - assertEquals(n, stmt.nStatement); + // verify that the sql statement is still not compiled + assertEquals(0, stmt.getSqlStatementId()); + assertEquals(mDatabase.mNativeHandle, stmt.nHandle); + assertEquals(mDatabase, stmt.mDatabase); stmt.executeInsert(); // now that the statement is executed, pre-compiled statement should be released assertEquals(0, stmt.nStatement); @@ -187,97 +189,25 @@ public class SQLiteStatementTest extends AndroidTestCase { assertEquals(mDatabase.mNativeHandle, stmt.nHandle); assertEquals(mDatabase, stmt.mDatabase); stmt.bindString(1, "blah" + colValue); - // verify that the sql statement is now compiled - n = stmt.nStatement; - assertTrue(n > 0); - assertEquals(n, stmt.getSqlStatementId()); - SQLiteDatabase dbUsed = mDatabase; - if (wal) { - // if wal is set, should be using a pooled connection handle - dbUsed = mDatabase.mConnectionPool.getConnectionList().get(0); - assertTrue(mDatabase.mNativeHandle != dbUsed.mNativeHandle); - } - assertEquals(dbUsed.mNativeHandle, stmt.nHandle); - assertEquals(dbUsed, stmt.mDatabase); + // verify that the sql statement is still not compiled + assertEquals(0, stmt.nStatement); + assertEquals(0, stmt.getSqlStatementId()); + assertEquals(mDatabase.mNativeHandle, stmt.nHandle); + assertEquals(mDatabase, stmt.mDatabase); // execute the statement Long l = stmt.simpleQueryForLong(); assertEquals(colValue, l.intValue()); // now that the statement is executed, pre-compiled statement should be released assertEquals(0, stmt.nStatement); assertEquals(0, stmt.getSqlStatementId()); - // but the database handle should still remain attached to the statement - assertEquals(dbUsed.mNativeHandle, stmt.nHandle); - assertEquals(dbUsed, stmt.mDatabase); + assertEquals(mDatabase.mNativeHandle, stmt.nHandle); + assertEquals(mDatabase, stmt.mDatabase); stmt.close(); // pre-compiled SQL statement should still remain released from this object assertEquals(0, stmt.nStatement); assertEquals(0, stmt.getSqlStatementId()); // but the database handle should still remain attached to the statement - assertEquals(dbUsed, stmt.mDatabase); - } - - /** - * test to make sure SqliteStatement.nStatement is populated only during bind and execute calls. - */ - public void testGetSqlStatementId() { - mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, text1 TEXT, text2 TEXT, " + - "num1 INTEGER, num2 INTEGER, image BLOB);"); - final String statement = "DELETE FROM test WHERE _id=?;"; - SQLiteStatement statementOne = mDatabase.compileStatement(statement); - // sql statement is NOT compiled until the bind or execute call. - statementOne.bindLong(1, 1); - SQLiteStatement statementTwo = mDatabase.compileStatement(statement); - statementTwo.bindLong(1, 1); - // since the same compiled statement is being accessed at the same time by 2 different - // objects, they each get their own statement id - assertTrue(statementOne.getSqlStatementId() != statementTwo.getSqlStatementId()); - statementOne.close(); - statementTwo.close(); - - // two SQLiteStatements referring to the same SQL statement should refer to the same - // pre-compiled SQl statement id if the SQLiteStatement objects are NOT in use at the same - // time - statementOne = mDatabase.compileStatement(statement); - statementOne.bindLong(1, 1); - // now that the SQL statement is compiled, get its pre-compiled SQL statement id - int n = statementOne.getSqlStatementId(); - statementOne.close(); - statementTwo = mDatabase.compileStatement(statement); - statementTwo.bindLong(1, 2); // use different value for bindarg, just for the heck of it - assertEquals(n, statementTwo.getSqlStatementId()); - statementTwo.close(); - - // now try to compile 2 different statements and they should have different uniquerIds. - SQLiteStatement statement1 = mDatabase.compileStatement("DELETE FROM test WHERE _id > ?;"); - statement1.bindLong(1, 1); - SQLiteStatement statement2 = mDatabase.compileStatement("DELETE FROM test WHERE _id < ?;"); - statement2.bindLong(1, 11); - assertTrue(statement1.getSqlStatementId() != statement2.getSqlStatementId()); - statement1.close(); - statement2.close(); - } - - public void testOnAllReferencesReleased() { - mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, text1 TEXT, text2 TEXT, " + - "num1 INTEGER, num2 INTEGER, image BLOB);"); - final String statement = "DELETE FROM test WHERE _id=?;"; - SQLiteStatement statementOne = mDatabase.compileStatement(statement); - statementOne.bindLong(1, 1); - assertTrue(statementOne.getSqlStatementId() > 0); - statementOne.releaseReference(); - assertEquals(0, statementOne.getSqlStatementId()); - statementOne.close(); - } - - public void testOnAllReferencesReleasedFromContainer() { - mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, text1 TEXT, text2 TEXT, " + - "num1 INTEGER, num2 INTEGER, image BLOB);"); - final String statement = "DELETE FROM test WHERE _id=?;"; - SQLiteStatement statementOne = mDatabase.compileStatement(statement); - statementOne.bindLong(1, 1); - assertTrue(statementOne.getSqlStatementId() > 0); - statementOne.releaseReferenceFromContainer(); - assertEquals(0, statementOne.getSqlStatementId()); - statementOne.close(); + assertEquals(mDatabase.mNativeHandle, stmt.nHandle); + assertEquals(mDatabase, stmt.mDatabase); } } |