diff options
author | Jeff Brown <jeffbrown@google.com> | 2012-01-18 15:29:57 -0800 |
---|---|---|
committer | Jeff Brown <jeffbrown@google.com> | 2012-01-18 18:29:20 -0800 |
commit | a9be4154e8dac0de3db5ee42e878beb0639e70e6 (patch) | |
tree | 55b9c6df70a28a36cf97df1e3ed0181b8434a20e /core/java/android/database | |
parent | dc89357810976556d20483c7fe161b68ed4d2acf (diff) | |
download | frameworks_base-a9be4154e8dac0de3db5ee42e878beb0639e70e6.zip frameworks_base-a9be4154e8dac0de3db5ee42e878beb0639e70e6.tar.gz frameworks_base-a9be4154e8dac0de3db5ee42e878beb0639e70e6.tar.bz2 |
Fix issues with reentrant SQLite triggers.
Modified SQLiteConnection and SQLiteSession to support
reentrant execution of SQLite operations, as might occur
when a custom function is invoked by a trigger.
Bug: 5884809
Change-Id: I253d828b2801bd06b1bbda7caa7da3f040a642bb
Diffstat (limited to 'core/java/android/database')
5 files changed, 186 insertions, 103 deletions
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index e45d66d..aeca62d 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -74,6 +74,12 @@ import java.util.regex.Pattern; * queues. * </p> * + * <h2>Reentrance</h2> + * <p> + * This class must tolerate reentrant execution of SQLite operations because + * triggers may call custom SQLite functions that perform additional queries. + * </p> + * * @hide */ public final class SQLiteConnection { @@ -205,13 +211,13 @@ public final class SQLiteConnection { } if (mConnectionPtr != 0) { - mRecentOperations.beginOperation("close", null, null); + final int cookie = mRecentOperations.beginOperation("close", null, null); try { mPreparedStatementCache.evictAll(); nativeClose(mConnectionPtr); mConnectionPtr = 0; } finally { - mRecentOperations.endOperation(); + mRecentOperations.endOperation(cookie); } } } @@ -304,9 +310,9 @@ public final class SQLiteConnection { throw new IllegalArgumentException("sql must not be null."); } - mRecentOperations.beginOperation("prepare", sql, null); + final int cookie = mRecentOperations.beginOperation("prepare", sql, null); try { - PreparedStatement statement = acquirePreparedStatement(sql); + final PreparedStatement statement = acquirePreparedStatement(sql); try { if (outStatementInfo != null) { outStatementInfo.numParameters = statement.mNumParameters; @@ -328,10 +334,10 @@ public final class SQLiteConnection { releasePreparedStatement(statement); } } catch (RuntimeException ex) { - mRecentOperations.failOperation(ex); + mRecentOperations.failOperation(cookie, ex); throw ex; } finally { - mRecentOperations.endOperation(); + mRecentOperations.endOperation(cookie); } } @@ -349,9 +355,9 @@ public final class SQLiteConnection { throw new IllegalArgumentException("sql must not be null."); } - mRecentOperations.beginOperation("execute", sql, bindArgs); + final int cookie = mRecentOperations.beginOperation("execute", sql, bindArgs); try { - PreparedStatement statement = acquirePreparedStatement(sql); + final PreparedStatement statement = acquirePreparedStatement(sql); try { throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); @@ -361,10 +367,10 @@ public final class SQLiteConnection { releasePreparedStatement(statement); } } catch (RuntimeException ex) { - mRecentOperations.failOperation(ex); + mRecentOperations.failOperation(cookie, ex); throw ex; } finally { - mRecentOperations.endOperation(); + mRecentOperations.endOperation(cookie); } } @@ -384,9 +390,9 @@ public final class SQLiteConnection { throw new IllegalArgumentException("sql must not be null."); } - mRecentOperations.beginOperation("executeForLong", sql, bindArgs); + final int cookie = mRecentOperations.beginOperation("executeForLong", sql, bindArgs); try { - PreparedStatement statement = acquirePreparedStatement(sql); + final PreparedStatement statement = acquirePreparedStatement(sql); try { throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); @@ -396,10 +402,10 @@ public final class SQLiteConnection { releasePreparedStatement(statement); } } catch (RuntimeException ex) { - mRecentOperations.failOperation(ex); + mRecentOperations.failOperation(cookie, ex); throw ex; } finally { - mRecentOperations.endOperation(); + mRecentOperations.endOperation(cookie); } } @@ -419,9 +425,9 @@ public final class SQLiteConnection { throw new IllegalArgumentException("sql must not be null."); } - mRecentOperations.beginOperation("executeForString", sql, bindArgs); + final int cookie = mRecentOperations.beginOperation("executeForString", sql, bindArgs); try { - PreparedStatement statement = acquirePreparedStatement(sql); + final PreparedStatement statement = acquirePreparedStatement(sql); try { throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); @@ -431,10 +437,10 @@ public final class SQLiteConnection { releasePreparedStatement(statement); } } catch (RuntimeException ex) { - mRecentOperations.failOperation(ex); + mRecentOperations.failOperation(cookie, ex); throw ex; } finally { - mRecentOperations.endOperation(); + mRecentOperations.endOperation(cookie); } } @@ -456,9 +462,10 @@ public final class SQLiteConnection { throw new IllegalArgumentException("sql must not be null."); } - mRecentOperations.beginOperation("executeForBlobFileDescriptor", sql, bindArgs); + final int cookie = mRecentOperations.beginOperation("executeForBlobFileDescriptor", + sql, bindArgs); try { - PreparedStatement statement = acquirePreparedStatement(sql); + final PreparedStatement statement = acquirePreparedStatement(sql); try { throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); @@ -470,10 +477,10 @@ public final class SQLiteConnection { releasePreparedStatement(statement); } } catch (RuntimeException ex) { - mRecentOperations.failOperation(ex); + mRecentOperations.failOperation(cookie, ex); throw ex; } finally { - mRecentOperations.endOperation(); + mRecentOperations.endOperation(cookie); } } @@ -493,9 +500,10 @@ public final class SQLiteConnection { throw new IllegalArgumentException("sql must not be null."); } - mRecentOperations.beginOperation("executeForChangedRowCount", sql, bindArgs); + final int cookie = mRecentOperations.beginOperation("executeForChangedRowCount", + sql, bindArgs); try { - PreparedStatement statement = acquirePreparedStatement(sql); + final PreparedStatement statement = acquirePreparedStatement(sql); try { throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); @@ -506,10 +514,10 @@ public final class SQLiteConnection { releasePreparedStatement(statement); } } catch (RuntimeException ex) { - mRecentOperations.failOperation(ex); + mRecentOperations.failOperation(cookie, ex); throw ex; } finally { - mRecentOperations.endOperation(); + mRecentOperations.endOperation(cookie); } } @@ -529,9 +537,10 @@ public final class SQLiteConnection { throw new IllegalArgumentException("sql must not be null."); } - mRecentOperations.beginOperation("executeForLastInsertedRowId", sql, bindArgs); + final int cookie = mRecentOperations.beginOperation("executeForLastInsertedRowId", + sql, bindArgs); try { - PreparedStatement statement = acquirePreparedStatement(sql); + final PreparedStatement statement = acquirePreparedStatement(sql); try { throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); @@ -542,10 +551,10 @@ public final class SQLiteConnection { releasePreparedStatement(statement); } } catch (RuntimeException ex) { - mRecentOperations.failOperation(ex); + mRecentOperations.failOperation(cookie, ex); throw ex; } finally { - mRecentOperations.endOperation(); + mRecentOperations.endOperation(cookie); } } @@ -581,9 +590,10 @@ public final class SQLiteConnection { int actualPos = -1; int countedRows = -1; int filledRows = -1; - mRecentOperations.beginOperation("executeForCursorWindow", sql, bindArgs); + final int cookie = mRecentOperations.beginOperation("executeForCursorWindow", + sql, bindArgs); try { - PreparedStatement statement = acquirePreparedStatement(sql); + final PreparedStatement statement = acquirePreparedStatement(sql); try { throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); @@ -600,11 +610,11 @@ public final class SQLiteConnection { releasePreparedStatement(statement); } } catch (RuntimeException ex) { - mRecentOperations.failOperation(ex); + mRecentOperations.failOperation(cookie, ex); throw ex; } finally { - if (mRecentOperations.endOperationDeferLog()) { - mRecentOperations.logOperation("window='" + window + if (mRecentOperations.endOperationDeferLog(cookie)) { + mRecentOperations.logOperation(cookie, "window='" + window + "', startPos=" + startPos + ", actualPos=" + actualPos + ", filledRows=" + filledRows @@ -615,8 +625,15 @@ public final class SQLiteConnection { private PreparedStatement acquirePreparedStatement(String sql) { PreparedStatement statement = mPreparedStatementCache.get(sql); + boolean skipCache = false; if (statement != null) { - return statement; + if (!statement.mInUse) { + return statement; + } + // The statement is already in the cache but is in use (this statement appears + // to be not only re-entrant but recursive!). So prepare a new copy of the + // statement but do not cache it. + skipCache = true; } final int statementPtr = nativePrepareStatement(mConnectionPtr, sql); @@ -625,7 +642,7 @@ public final class SQLiteConnection { final int type = DatabaseUtils.getSqlStatementType(sql); final boolean readOnly = nativeIsReadOnly(mConnectionPtr, statementPtr); statement = obtainPreparedStatement(sql, statementPtr, numParameters, type, readOnly); - if (isCacheable(type)) { + if (!skipCache && isCacheable(type)) { mPreparedStatementCache.put(sql, statement); statement.mInCache = true; } @@ -637,18 +654,20 @@ public final class SQLiteConnection { } throw ex; } + statement.mInUse = true; return statement; } private void releasePreparedStatement(PreparedStatement statement) { + statement.mInUse = false; if (statement.mInCache) { try { nativeResetStatementAndClearBindings(mConnectionPtr, statement.mStatementPtr); } catch (SQLiteException ex) { - // The statement could not be reset due to an error. - // The entryRemoved() callback for the cache will recursively call - // releasePreparedStatement() again, but this time mInCache will be false - // so the statement will be finalized and recycled. + // The statement could not be reset due to an error. Remove it from the cache. + // When remove() is called, the cache will invoke its entryRemoved() callback, + // which will in turn call finalizePreparedStatement() to finalize and + // recycle the statement. if (SQLiteDebug.DEBUG_SQL_CACHE) { Log.v(TAG, "Could not reset prepared statement due to an exception. " + "Removing it from the cache. SQL: " @@ -657,11 +676,15 @@ public final class SQLiteConnection { mPreparedStatementCache.remove(statement.mSql); } } else { - nativeFinalizeStatement(mConnectionPtr, statement.mStatementPtr); - recyclePreparedStatement(statement); + finalizePreparedStatement(statement); } } + private void finalizePreparedStatement(PreparedStatement statement) { + nativeFinalizeStatement(mConnectionPtr, statement.mStatementPtr); + recyclePreparedStatement(statement); + } + private void bindArguments(PreparedStatement statement, Object[] bindArgs) { final int count = bindArgs != null ? bindArgs.length : 0; if (count != statement.mNumParameters) { @@ -735,9 +758,10 @@ public final class SQLiteConnection { * Dumps debugging information about this connection. * * @param printer The printer to receive the dump, not null. + * @param verbose True to dump more verbose information. */ - public void dump(Printer printer) { - dumpUnsafe(printer); + public void dump(Printer printer, boolean verbose) { + dumpUnsafe(printer, verbose); } /** @@ -752,15 +776,21 @@ public final class SQLiteConnection { * it should not crash. This is ok as it is only used for diagnostic purposes. * * @param printer The printer to receive the dump, not null. + * @param verbose True to dump more verbose information. */ - void dumpUnsafe(Printer printer) { + void dumpUnsafe(Printer printer, boolean verbose) { printer.println("Connection #" + mConnectionId + ":"); + if (verbose) { + printer.println(" connectionPtr: 0x" + Integer.toHexString(mConnectionPtr)); + } printer.println(" isPrimaryConnection: " + mIsPrimaryConnection); - printer.println(" connectionPtr: 0x" + Integer.toHexString(mConnectionPtr)); printer.println(" onlyAllowReadOnlyOperations: " + mOnlyAllowReadOnlyOperations); mRecentOperations.dump(printer); - mPreparedStatementCache.dump(printer); + + if (verbose) { + mPreparedStatementCache.dump(printer); + } } /** @@ -917,6 +947,12 @@ public final class SQLiteConnection { // True if the statement is in the cache. public boolean mInCache; + + // True if the statement is in use (currently executing). + // We need this flag because due to the use of custom functions in triggers, it's + // possible for SQLite calls to be re-entrant. Consequently we need to prevent + // in use statements from being finalized until they are no longer in use. + public boolean mInUse; } private final class PreparedStatementCache @@ -929,7 +965,9 @@ public final class SQLiteConnection { protected void entryRemoved(boolean evicted, String key, PreparedStatement oldValue, PreparedStatement newValue) { oldValue.mInCache = false; - releasePreparedStatement(oldValue); + if (!oldValue.mInUse) { + finalizePreparedStatement(oldValue); + } } public void dump(Printer printer) { @@ -958,11 +996,14 @@ public final class SQLiteConnection { private static final class OperationLog { private static final int MAX_RECENT_OPERATIONS = 10; + private static final int COOKIE_GENERATION_SHIFT = 8; + private static final int COOKIE_INDEX_MASK = 0xff; private final Operation[] mOperations = new Operation[MAX_RECENT_OPERATIONS]; private int mIndex; + private int mGeneration; - public void beginOperation(String kind, String sql, Object[] bindArgs) { + public int beginOperation(String kind, String sql, Object[] bindArgs) { synchronized (mOperations) { final int index = (mIndex + 1) % MAX_RECENT_OPERATIONS; Operation operation = mOperations[index]; @@ -995,47 +1036,54 @@ public final class SQLiteConnection { } } } + operation.mCookie = newOperationCookieLocked(index); mIndex = index; + return operation.mCookie; } } - public void failOperation(Exception ex) { + public void failOperation(int cookie, Exception ex) { synchronized (mOperations) { - final Operation operation = mOperations[mIndex]; - operation.mException = ex; + final Operation operation = getOperationLocked(cookie); + if (operation != null) { + operation.mException = ex; + } } } - public boolean endOperationDeferLog() { + public void endOperation(int cookie) { synchronized (mOperations) { - return endOperationDeferLogLocked(); + if (endOperationDeferLogLocked(cookie)) { + logOperationLocked(cookie, null); + } } } - private boolean endOperationDeferLogLocked() { - final Operation operation = mOperations[mIndex]; - operation.mEndTime = System.currentTimeMillis(); - operation.mFinished = true; - return SQLiteDebug.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery( - operation.mEndTime - operation.mStartTime); + public boolean endOperationDeferLog(int cookie) { + synchronized (mOperations) { + return endOperationDeferLogLocked(cookie); + } } - public void endOperation() { + public void logOperation(int cookie, String detail) { synchronized (mOperations) { - if (endOperationDeferLogLocked()) { - logOperationLocked(null); - } + logOperationLocked(cookie, detail); } } - public void logOperation(String detail) { - synchronized (mOperations) { - logOperationLocked(detail); + private boolean endOperationDeferLogLocked(int cookie) { + final Operation operation = getOperationLocked(cookie); + if (operation != null) { + operation.mEndTime = System.currentTimeMillis(); + operation.mFinished = true; + return SQLiteDebug.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery( + operation.mEndTime - operation.mStartTime); } + return false; } - private void logOperationLocked(String detail) { - final Operation operation = mOperations[mIndex]; + private void logOperationLocked(int cookie, String detail) { + final Operation operation = getOperationLocked(cookie); StringBuilder msg = new StringBuilder(); operation.describe(msg); if (detail != null) { @@ -1044,6 +1092,17 @@ public final class SQLiteConnection { Log.d(TAG, msg.toString()); } + private int newOperationCookieLocked(int index) { + final int generation = mGeneration++; + return generation << COOKIE_GENERATION_SHIFT | index; + } + + private Operation getOperationLocked(int cookie) { + final int index = cookie & COOKIE_INDEX_MASK; + final Operation operation = mOperations[index]; + return operation.mCookie == cookie ? operation : null; + } + public String describeCurrentOperation() { synchronized (mOperations) { final Operation operation = mOperations[mIndex]; @@ -1097,6 +1156,7 @@ public final class SQLiteConnection { public ArrayList<Object> mBindArgs; public boolean mFinished; public Exception mException; + public int mCookie; public void describe(StringBuilder msg) { msg.append(mKind); diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java index b88bfee..5469213 100644 --- a/core/java/android/database/sqlite/SQLiteConnectionPool.java +++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java @@ -833,8 +833,9 @@ public final class SQLiteConnectionPool implements Closeable { * Dumps debugging information about this connection pool. * * @param printer The printer to receive the dump, not null. + * @param verbose True to dump more verbose information. */ - public void dump(Printer printer) { + public void dump(Printer printer, boolean verbose) { Printer indentedPrinter = PrefixPrinter.create(printer, " "); synchronized (mLock) { printer.println("Connection pool for " + mConfiguration.path + ":"); @@ -843,7 +844,7 @@ public final class SQLiteConnectionPool implements Closeable { printer.println(" Available primary connection:"); if (mAvailablePrimaryConnection != null) { - mAvailablePrimaryConnection.dump(indentedPrinter); + mAvailablePrimaryConnection.dump(indentedPrinter, verbose); } else { indentedPrinter.println("<none>"); } @@ -852,7 +853,7 @@ public final class SQLiteConnectionPool implements Closeable { if (!mAvailableNonPrimaryConnections.isEmpty()) { final int count = mAvailableNonPrimaryConnections.size(); for (int i = 0; i < count; i++) { - mAvailableNonPrimaryConnections.get(i).dump(indentedPrinter); + mAvailableNonPrimaryConnections.get(i).dump(indentedPrinter, verbose); } } else { indentedPrinter.println("<none>"); @@ -863,7 +864,7 @@ public final class SQLiteConnectionPool implements Closeable { for (Map.Entry<SQLiteConnection, Boolean> entry : mAcquiredConnections.entrySet()) { final SQLiteConnection connection = entry.getKey(); - connection.dumpUnsafe(indentedPrinter); + connection.dumpUnsafe(indentedPrinter, verbose); indentedPrinter.println(" Pending reconfiguration: " + entry.getValue()); } } else { diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 377a680..9cb6480 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -1665,17 +1665,17 @@ public class SQLiteDatabase extends SQLiteClosable { * Dump detailed information about all open databases in the current process. * Used by bug report. */ - static void dumpAll(Printer printer) { + static void dumpAll(Printer printer, boolean verbose) { for (SQLiteDatabase db : getActiveDatabases()) { - db.dump(printer); + db.dump(printer, verbose); } } - private void dump(Printer printer) { + private void dump(Printer printer, boolean verbose) { synchronized (mLock) { if (mConnectionPoolLocked != null) { printer.println(""); - mConnectionPoolLocked.dump(printer); + mConnectionPoolLocked.dump(printer, verbose); } } } diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java index d87c3e4..a64251b 100644 --- a/core/java/android/database/sqlite/SQLiteDebug.java +++ b/core/java/android/database/sqlite/SQLiteDebug.java @@ -190,9 +190,17 @@ public final class SQLiteDebug { /** * Dumps detailed information about all databases used by the process. * @param printer The printer for dumping database state. + * @param args Command-line arguments supplied to dumpsys dbinfo */ public static void dump(Printer printer, String[] args) { - SQLiteDatabase.dumpAll(printer); + boolean verbose = false; + for (String arg : args) { + if (arg.equals("-v")) { + verbose = true; + } + } + + SQLiteDatabase.dumpAll(printer, verbose); } /** diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java index 61fe45a..a933051 100644 --- a/core/java/android/database/sqlite/SQLiteSession.java +++ b/core/java/android/database/sqlite/SQLiteSession.java @@ -150,6 +150,12 @@ import android.os.ParcelFileDescriptor; * A query that works well on 100 rows may struggle with 10,000.</li> * </ul> * + * <h2>Reentrance</h2> + * <p> + * This class must tolerate reentrant execution of SQLite operations because + * triggers may call custom SQLite functions that perform additional queries. + * </p> + * * TODO: Support timeouts on all possibly blocking operations. * * @hide @@ -159,6 +165,7 @@ public final class SQLiteSession { private SQLiteConnection mConnection; private int mConnectionFlags; + private int mConnectionUseCount; private Transaction mTransactionPool; private Transaction mTransactionStack; @@ -289,7 +296,9 @@ public final class SQLiteSession { private void beginTransactionUnchecked(int transactionMode, SQLiteTransactionListener transactionListener, int connectionFlags) { - acquireConnectionIfNoTransaction(null, connectionFlags); // might throw + if (mTransactionStack == null) { + acquireConnection(null, connectionFlags); // might throw + } try { // Set up the transaction such that we can back out safely // in case we fail part way. @@ -325,7 +334,9 @@ public final class SQLiteSession { transaction.mParent = mTransactionStack; mTransactionStack = transaction; } finally { - releaseConnectionIfNoTransaction(); // might throw + if (mTransactionStack == null) { + releaseConnection(); // might throw + } } } @@ -408,7 +419,7 @@ public final class SQLiteSession { mConnection.execute("ROLLBACK;", null); // might throw } } finally { - releaseConnectionIfNoTransaction(); // might throw + releaseConnection(); // might throw } } @@ -534,11 +545,11 @@ public final class SQLiteSession { throw new IllegalArgumentException("sql must not be null."); } - acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw + acquireConnection(sql, connectionFlags); // might throw try { mConnection.prepare(sql, outStatementInfo); // might throw } finally { - releaseConnectionIfNoTransaction(); // might throw + releaseConnection(); // might throw } } @@ -562,11 +573,11 @@ public final class SQLiteSession { return; } - acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw + acquireConnection(sql, connectionFlags); // might throw try { mConnection.execute(sql, bindArgs); // might throw } finally { - releaseConnectionIfNoTransaction(); // might throw + releaseConnection(); // might throw } } @@ -592,11 +603,11 @@ public final class SQLiteSession { return 0; } - acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw + acquireConnection(sql, connectionFlags); // might throw try { return mConnection.executeForLong(sql, bindArgs); // might throw } finally { - releaseConnectionIfNoTransaction(); // might throw + releaseConnection(); // might throw } } @@ -622,11 +633,11 @@ public final class SQLiteSession { return null; } - acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw + acquireConnection(sql, connectionFlags); // might throw try { return mConnection.executeForString(sql, bindArgs); // might throw } finally { - releaseConnectionIfNoTransaction(); // might throw + releaseConnection(); // might throw } } @@ -655,11 +666,11 @@ public final class SQLiteSession { return null; } - acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw + acquireConnection(sql, connectionFlags); // might throw try { return mConnection.executeForBlobFileDescriptor(sql, bindArgs); // might throw } finally { - releaseConnectionIfNoTransaction(); // might throw + releaseConnection(); // might throw } } @@ -685,11 +696,11 @@ public final class SQLiteSession { return 0; } - acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw + acquireConnection(sql, connectionFlags); // might throw try { return mConnection.executeForChangedRowCount(sql, bindArgs); // might throw } finally { - releaseConnectionIfNoTransaction(); // might throw + releaseConnection(); // might throw } } @@ -715,11 +726,11 @@ public final class SQLiteSession { return 0; } - acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw + acquireConnection(sql, connectionFlags); // might throw try { return mConnection.executeForLastInsertedRowId(sql, bindArgs); // might throw } finally { - releaseConnectionIfNoTransaction(); // might throw + releaseConnection(); // might throw } } @@ -760,12 +771,12 @@ public final class SQLiteSession { return 0; } - acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw + acquireConnection(sql, connectionFlags); // might throw try { return mConnection.executeForCursorWindow(sql, bindArgs, window, startPos, requiredPos, countAllRows); // might throw } finally { - releaseConnectionIfNoTransaction(); // might throw + releaseConnection(); // might throw } } @@ -807,16 +818,19 @@ public final class SQLiteSession { return false; } - private void acquireConnectionIfNoTransaction(String sql, int connectionFlags) { - if (mTransactionStack == null) { - assert mConnection == null; + private void acquireConnection(String sql, int connectionFlags) { + if (mConnection == null) { + assert mConnectionUseCount == 0; mConnection = mConnectionPool.acquireConnection(sql, connectionFlags); // might throw mConnectionFlags = connectionFlags; } + mConnectionUseCount += 1; } - private void releaseConnectionIfNoTransaction() { - if (mTransactionStack == null && mConnection != null) { + private void releaseConnection() { + assert mConnection != null; + assert mConnectionUseCount > 0; + if (--mConnectionUseCount == 0) { try { mConnectionPool.releaseConnection(mConnection); // might throw } finally { |