Merge "reduce locking when using SQLiteStatement"

This commit is contained in:
Vasu Nori
2010-07-09 13:37:45 -07:00
committed by Android (Google) Code Review
4 changed files with 216 additions and 204 deletions

View File

@@ -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.

View File

@@ -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();

View File

@@ -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();
}

View File

@@ -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);
}
}