summaryrefslogtreecommitdiffstats
path: root/core/java/android/database
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2012-01-18 15:29:57 -0800
committerJeff Brown <jeffbrown@google.com>2012-01-18 18:29:20 -0800
commita9be4154e8dac0de3db5ee42e878beb0639e70e6 (patch)
tree55b9c6df70a28a36cf97df1e3ed0181b8434a20e /core/java/android/database
parentdc89357810976556d20483c7fe161b68ed4d2acf (diff)
downloadframeworks_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')
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java200
-rw-r--r--core/java/android/database/sqlite/SQLiteConnectionPool.java9
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java8
-rw-r--r--core/java/android/database/sqlite/SQLiteDebug.java10
-rw-r--r--core/java/android/database/sqlite/SQLiteSession.java62
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 {