reduce locking when using SQLiteStatement

Do compiling of sql, binding of args and execution of the SQL
statement within one single database lock period.
This reduces the number of times the database lock
needs to be acquired during the course of compilation, binding
and execution of a SQLiteStatement.

Change-Id: I22b090ec9e10fc0aa2532a93bafe610af2546b58
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 4df6ad1..017b65f 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 @@
     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 @@
     @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 @@
         nStatement = mCompiledSql.nStatement;
     }
 
-    private int getSqlStatementType(String sql) {
+    /* package */ int getSqlStatementType(String sql) {
         if (mSql.length() < 6) {
             return OTHER_STMT;
         }
@@ -136,46 +166,11 @@
         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 @@
      * @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 @@
      * @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 @@
      * @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 @@
         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 @@
         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 void clearBindings() {
         synchronized (this) {
+            bindArgs = null;
             if (this.nStatement == 0) {
                 return;
             }
@@ -362,6 +380,7 @@
      */
     public void close() {
         synchronized (this) {
+            bindArgs = null;
             if (nHandle == 0 || !mDatabase.isOpen()) {
                 return;
             }
@@ -369,6 +388,34 @@
         }
     }
 
+    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 9664593..b902803 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -35,6 +35,8 @@
     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 @@
      *         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 @@
      *         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 @@
      * @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 @@
      * @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 @@
      * 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 @@
      * 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 @@
         mDatabase.lock();
         acquireReference();
         mDatabase.closePendingStatements();
+        compileAndbindAllArgs();
         return startTime;
     }
 
@@ -158,6 +177,9 @@
         // 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 ca8dac1..91ef0b7 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 @@
         }
     }
 
+    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 @@
         // 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 @@
                 "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 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 @@
         // 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 @@
         // 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 @@
         // 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 @@
         // 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 eb27551..217545f 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 @@
         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 @@
         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);
     }
 }