summaryrefslogtreecommitdiffstats
path: root/core/java/android/database
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/database')
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java168
-rw-r--r--core/java/android/database/sqlite/SQLiteConnectionPool.java86
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java146
-rw-r--r--core/java/android/database/sqlite/SQLiteDirectCursorDriver.java8
-rw-r--r--core/java/android/database/sqlite/SQLiteProgram.java7
-rw-r--r--core/java/android/database/sqlite/SQLiteQuery.java16
-rw-r--r--core/java/android/database/sqlite/SQLiteQueryBuilder.java62
-rw-r--r--core/java/android/database/sqlite/SQLiteSession.java183
-rw-r--r--core/java/android/database/sqlite/SQLiteStatement.java14
9 files changed, 560 insertions, 130 deletions
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 72f62fd..710bd53 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -19,6 +19,8 @@ package android.database.sqlite;
import dalvik.system.BlockGuard;
import dalvik.system.CloseGuard;
+import android.content.CancelationSignal;
+import android.content.OperationCanceledException;
import android.database.Cursor;
import android.database.CursorWindow;
import android.database.DatabaseUtils;
@@ -82,7 +84,7 @@ import java.util.regex.Pattern;
*
* @hide
*/
-public final class SQLiteConnection {
+public final class SQLiteConnection implements CancelationSignal.OnCancelListener {
private static final String TAG = "SQLiteConnection";
private static final boolean DEBUG = false;
@@ -108,6 +110,12 @@ public final class SQLiteConnection {
private boolean mOnlyAllowReadOnlyOperations;
+ // The number of times attachCancelationSignal has been called.
+ // Because SQLite statement execution can be re-entrant, we keep track of how many
+ // times we have attempted to attach a cancelation signal to the connection so that
+ // we can ensure that we detach the signal at the right time.
+ private int mCancelationSignalAttachCount;
+
private static native int nativeOpen(String path, int openFlags, String label,
boolean enableTrace, boolean enableProfile);
private static native void nativeClose(int connectionPtr);
@@ -145,6 +153,8 @@ public final class SQLiteConnection {
int connectionPtr, int statementPtr, int windowPtr,
int startPos, int requiredPos, boolean countAllRows);
private static native int nativeGetDbLookaside(int connectionPtr);
+ private static native void nativeCancel(int connectionPtr);
+ private static native void nativeResetCancel(int connectionPtr, boolean cancelable);
private SQLiteConnection(SQLiteConnectionPool pool,
SQLiteDatabaseConfiguration configuration,
@@ -345,11 +355,14 @@ public final class SQLiteConnection {
*
* @param sql The SQL statement to execute.
* @param bindArgs The arguments to bind, or null if none.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
+ * @throws OperationCanceledException if the operation was canceled.
*/
- public void execute(String sql, Object[] bindArgs) {
+ public void execute(String sql, Object[] bindArgs,
+ CancelationSignal cancelationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -361,7 +374,12 @@ public final class SQLiteConnection {
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
applyBlockGuardPolicy(statement);
- nativeExecute(mConnectionPtr, statement.mStatementPtr);
+ attachCancelationSignal(cancelationSignal);
+ try {
+ nativeExecute(mConnectionPtr, statement.mStatementPtr);
+ } finally {
+ detachCancelationSignal(cancelationSignal);
+ }
} finally {
releasePreparedStatement(statement);
}
@@ -378,13 +396,16 @@ public final class SQLiteConnection {
*
* @param sql The SQL statement to execute.
* @param bindArgs The arguments to bind, or null if none.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @return The value of the first column in the first row of the result set
* as a <code>long</code>, or zero if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
+ * @throws OperationCanceledException if the operation was canceled.
*/
- public long executeForLong(String sql, Object[] bindArgs) {
+ public long executeForLong(String sql, Object[] bindArgs,
+ CancelationSignal cancelationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -396,7 +417,12 @@ public final class SQLiteConnection {
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
applyBlockGuardPolicy(statement);
- return nativeExecuteForLong(mConnectionPtr, statement.mStatementPtr);
+ attachCancelationSignal(cancelationSignal);
+ try {
+ return nativeExecuteForLong(mConnectionPtr, statement.mStatementPtr);
+ } finally {
+ detachCancelationSignal(cancelationSignal);
+ }
} finally {
releasePreparedStatement(statement);
}
@@ -413,13 +439,16 @@ public final class SQLiteConnection {
*
* @param sql The SQL statement to execute.
* @param bindArgs The arguments to bind, or null if none.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @return The value of the first column in the first row of the result set
* as a <code>String</code>, or null if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
+ * @throws OperationCanceledException if the operation was canceled.
*/
- public String executeForString(String sql, Object[] bindArgs) {
+ public String executeForString(String sql, Object[] bindArgs,
+ CancelationSignal cancelationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -431,7 +460,12 @@ public final class SQLiteConnection {
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
applyBlockGuardPolicy(statement);
- return nativeExecuteForString(mConnectionPtr, statement.mStatementPtr);
+ attachCancelationSignal(cancelationSignal);
+ try {
+ return nativeExecuteForString(mConnectionPtr, statement.mStatementPtr);
+ } finally {
+ detachCancelationSignal(cancelationSignal);
+ }
} finally {
releasePreparedStatement(statement);
}
@@ -449,14 +483,17 @@ public final class SQLiteConnection {
*
* @param sql The SQL statement to execute.
* @param bindArgs The arguments to bind, or null if none.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @return The file descriptor for a shared memory region that contains
* the value of the first column in the first row of the result set as a BLOB,
* or null if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
+ * @throws OperationCanceledException if the operation was canceled.
*/
- public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs) {
+ public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs,
+ CancelationSignal cancelationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -469,9 +506,14 @@ public final class SQLiteConnection {
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
applyBlockGuardPolicy(statement);
- int fd = nativeExecuteForBlobFileDescriptor(
- mConnectionPtr, statement.mStatementPtr);
- return fd >= 0 ? ParcelFileDescriptor.adoptFd(fd) : null;
+ attachCancelationSignal(cancelationSignal);
+ try {
+ int fd = nativeExecuteForBlobFileDescriptor(
+ mConnectionPtr, statement.mStatementPtr);
+ return fd >= 0 ? ParcelFileDescriptor.adoptFd(fd) : null;
+ } finally {
+ detachCancelationSignal(cancelationSignal);
+ }
} finally {
releasePreparedStatement(statement);
}
@@ -489,12 +531,15 @@ public final class SQLiteConnection {
*
* @param sql The SQL statement to execute.
* @param bindArgs The arguments to bind, or null if none.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @return The number of rows that were changed.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
+ * @throws OperationCanceledException if the operation was canceled.
*/
- public int executeForChangedRowCount(String sql, Object[] bindArgs) {
+ public int executeForChangedRowCount(String sql, Object[] bindArgs,
+ CancelationSignal cancelationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -507,8 +552,13 @@ public final class SQLiteConnection {
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
applyBlockGuardPolicy(statement);
- return nativeExecuteForChangedRowCount(
- mConnectionPtr, statement.mStatementPtr);
+ attachCancelationSignal(cancelationSignal);
+ try {
+ return nativeExecuteForChangedRowCount(
+ mConnectionPtr, statement.mStatementPtr);
+ } finally {
+ detachCancelationSignal(cancelationSignal);
+ }
} finally {
releasePreparedStatement(statement);
}
@@ -526,12 +576,15 @@ public final class SQLiteConnection {
*
* @param sql The SQL statement to execute.
* @param bindArgs The arguments to bind, or null if none.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @return The row id of the last row that was inserted, or 0 if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
+ * @throws OperationCanceledException if the operation was canceled.
*/
- public long executeForLastInsertedRowId(String sql, Object[] bindArgs) {
+ public long executeForLastInsertedRowId(String sql, Object[] bindArgs,
+ CancelationSignal cancelationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -544,8 +597,13 @@ public final class SQLiteConnection {
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
applyBlockGuardPolicy(statement);
- return nativeExecuteForLastInsertedRowId(
- mConnectionPtr, statement.mStatementPtr);
+ attachCancelationSignal(cancelationSignal);
+ try {
+ return nativeExecuteForLastInsertedRowId(
+ mConnectionPtr, statement.mStatementPtr);
+ } finally {
+ detachCancelationSignal(cancelationSignal);
+ }
} finally {
releasePreparedStatement(statement);
}
@@ -571,14 +629,17 @@ public final class SQLiteConnection {
* so that it does. Must be greater than or equal to <code>startPos</code>.
* @param countAllRows True to count all rows that the query would return
* regagless of whether they fit in the window.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @return The number of rows that were counted during query execution. Might
* not be all rows in the result set unless <code>countAllRows</code> is true.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
+ * @throws OperationCanceledException if the operation was canceled.
*/
public int executeForCursorWindow(String sql, Object[] bindArgs,
- CursorWindow window, int startPos, int requiredPos, boolean countAllRows) {
+ CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
+ CancelationSignal cancelationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -597,14 +658,19 @@ public final class SQLiteConnection {
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
applyBlockGuardPolicy(statement);
- final long result = nativeExecuteForCursorWindow(
- mConnectionPtr, statement.mStatementPtr, window.mWindowPtr,
- startPos, requiredPos, countAllRows);
- actualPos = (int)(result >> 32);
- countedRows = (int)result;
- filledRows = window.getNumRows();
- window.setStartPosition(actualPos);
- return countedRows;
+ attachCancelationSignal(cancelationSignal);
+ try {
+ final long result = nativeExecuteForCursorWindow(
+ mConnectionPtr, statement.mStatementPtr, window.mWindowPtr,
+ startPos, requiredPos, countAllRows);
+ actualPos = (int)(result >> 32);
+ countedRows = (int)result;
+ filledRows = window.getNumRows();
+ window.setStartPosition(actualPos);
+ return countedRows;
+ } finally {
+ detachCancelationSignal(cancelationSignal);
+ }
} finally {
releasePreparedStatement(statement);
}
@@ -685,6 +751,46 @@ public final class SQLiteConnection {
recyclePreparedStatement(statement);
}
+ private void attachCancelationSignal(CancelationSignal cancelationSignal) {
+ if (cancelationSignal != null) {
+ cancelationSignal.throwIfCanceled();
+
+ mCancelationSignalAttachCount += 1;
+ if (mCancelationSignalAttachCount == 1) {
+ // Reset cancelation flag before executing the statement.
+ nativeResetCancel(mConnectionPtr, true /*cancelable*/);
+
+ // After this point, onCancel() may be called concurrently.
+ cancelationSignal.setOnCancelListener(this);
+ }
+ }
+ }
+
+ private void detachCancelationSignal(CancelationSignal cancelationSignal) {
+ if (cancelationSignal != null) {
+ assert mCancelationSignalAttachCount > 0;
+
+ mCancelationSignalAttachCount -= 1;
+ if (mCancelationSignalAttachCount == 0) {
+ // After this point, onCancel() cannot be called concurrently.
+ cancelationSignal.setOnCancelListener(null);
+
+ // Reset cancelation flag after executing the statement.
+ nativeResetCancel(mConnectionPtr, false /*cancelable*/);
+ }
+ }
+ }
+
+ // CancelationSignal.OnCancelationListener callback.
+ // This method may be called on a different thread than the executing statement.
+ // However, it will only be called between calls to attachCancelationSignal and
+ // detachCancelationSignal, while a statement is executing. We can safely assume
+ // that the SQLite connection is still alive.
+ @Override
+ public void onCancel() {
+ nativeCancel(mConnectionPtr);
+ }
+
private void bindArguments(PreparedStatement statement, Object[] bindArgs) {
final int count = bindArgs != null ? bindArgs.length : 0;
if (count != statement.mNumParameters) {
@@ -822,8 +928,8 @@ public final class SQLiteConnection {
long pageCount = 0;
long pageSize = 0;
try {
- pageCount = executeForLong("PRAGMA page_count;", null);
- pageSize = executeForLong("PRAGMA page_size;", null);
+ pageCount = executeForLong("PRAGMA page_count;", null, null);
+ pageSize = executeForLong("PRAGMA page_size;", null, null);
} catch (SQLiteException ex) {
// Ignore.
}
@@ -834,15 +940,15 @@ public final class SQLiteConnection {
// the main database which we have already described.
CursorWindow window = new CursorWindow("collectDbStats");
try {
- executeForCursorWindow("PRAGMA database_list;", null, window, 0, 0, false);
+ executeForCursorWindow("PRAGMA database_list;", null, window, 0, 0, false, null);
for (int i = 1; i < window.getNumRows(); i++) {
String name = window.getString(i, 1);
String path = window.getString(i, 2);
pageCount = 0;
pageSize = 0;
try {
- pageCount = executeForLong("PRAGMA " + name + ".page_count;", null);
- pageSize = executeForLong("PRAGMA " + name + ".page_size;", null);
+ pageCount = executeForLong("PRAGMA " + name + ".page_count;", null, null);
+ pageSize = executeForLong("PRAGMA " + name + ".page_size;", null, null);
} catch (SQLiteException ex) {
// Ignore.
}
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index 5469213..d335738 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -18,6 +18,8 @@ package android.database.sqlite;
import dalvik.system.CloseGuard;
+import android.content.CancelationSignal;
+import android.content.OperationCanceledException;
import android.database.sqlite.SQLiteDebug.DbStats;
import android.os.SystemClock;
import android.util.Log;
@@ -282,13 +284,16 @@ public final class SQLiteConnectionPool implements Closeable {
* @param sql If not null, try to find a connection that already has
* the specified SQL statement in its prepared statement cache.
* @param connectionFlags The connection request flags.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @return The connection that was acquired, never null.
*
* @throws IllegalStateException if the pool has been closed.
* @throws SQLiteException if a database error occurs.
+ * @throws OperationCanceledException if the operation was canceled.
*/
- public SQLiteConnection acquireConnection(String sql, int connectionFlags) {
- return waitForConnection(sql, connectionFlags);
+ public SQLiteConnection acquireConnection(String sql, int connectionFlags,
+ CancelationSignal cancelationSignal) {
+ return waitForConnection(sql, connectionFlags, cancelationSignal);
}
/**
@@ -497,7 +502,8 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Might throw.
- private SQLiteConnection waitForConnection(String sql, int connectionFlags) {
+ private SQLiteConnection waitForConnection(String sql, int connectionFlags,
+ CancelationSignal cancelationSignal) {
final boolean wantPrimaryConnection =
(connectionFlags & CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY) != 0;
@@ -505,6 +511,11 @@ public final class SQLiteConnectionPool implements Closeable {
synchronized (mLock) {
throwIfClosedLocked();
+ // Abort if canceled.
+ if (cancelationSignal != null) {
+ cancelationSignal.throwIfCanceled();
+ }
+
// Try to acquire a connection.
SQLiteConnection connection = null;
if (!wantPrimaryConnection) {
@@ -538,6 +549,18 @@ public final class SQLiteConnectionPool implements Closeable {
} else {
mConnectionWaiterQueue = waiter;
}
+
+ if (cancelationSignal != null) {
+ final int nonce = waiter.mNonce;
+ cancelationSignal.setOnCancelListener(new CancelationSignal.OnCancelListener() {
+ @Override
+ public void onCancel() {
+ synchronized (mLock) {
+ cancelConnectionWaiterLocked(waiter, nonce);
+ }
+ }
+ });
+ }
}
// Park the thread until a connection is assigned or the pool is closed.
@@ -547,7 +570,9 @@ public final class SQLiteConnectionPool implements Closeable {
for (;;) {
// Detect and recover from connection leaks.
if (mConnectionLeaked.compareAndSet(true, false)) {
- wakeConnectionWaitersLocked();
+ synchronized (mLock) {
+ wakeConnectionWaitersLocked();
+ }
}
// Wait to be unparked (may already have happened), a timeout, or interruption.
@@ -560,15 +585,16 @@ public final class SQLiteConnectionPool implements Closeable {
synchronized (mLock) {
throwIfClosedLocked();
- SQLiteConnection connection = waiter.mAssignedConnection;
- if (connection != null) {
- recycleConnectionWaiterLocked(waiter);
- return connection;
- }
-
- RuntimeException ex = waiter.mException;
- if (ex != null) {
+ final SQLiteConnection connection = waiter.mAssignedConnection;
+ final RuntimeException ex = waiter.mException;
+ if (connection != null || ex != null) {
+ if (cancelationSignal != null) {
+ cancelationSignal.setOnCancelListener(null);
+ }
recycleConnectionWaiterLocked(waiter);
+ if (connection != null) {
+ return connection;
+ }
throw ex; // rethrow!
}
@@ -585,6 +611,40 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Can't throw.
+ private void cancelConnectionWaiterLocked(ConnectionWaiter waiter, int nonce) {
+ if (waiter.mNonce != nonce) {
+ // Waiter already removed and recycled.
+ return;
+ }
+
+ if (waiter.mAssignedConnection != null || waiter.mException != null) {
+ // Waiter is done waiting but has not woken up yet.
+ return;
+ }
+
+ // Waiter must still be waiting. Dequeue it.
+ ConnectionWaiter predecessor = null;
+ ConnectionWaiter current = mConnectionWaiterQueue;
+ while (current != waiter) {
+ assert current != null;
+ predecessor = current;
+ current = current.mNext;
+ }
+ if (predecessor != null) {
+ predecessor.mNext = waiter.mNext;
+ } else {
+ mConnectionWaiterQueue = waiter.mNext;
+ }
+
+ // Send the waiter an exception and unpark it.
+ waiter.mException = new OperationCanceledException();
+ LockSupport.unpark(waiter.mThread);
+
+ // Check whether removing this waiter will enable other waiters to make progress.
+ wakeConnectionWaitersLocked();
+ }
+
+ // Can't throw.
private void logConnectionPoolBusyLocked(long waitMillis, int connectionFlags) {
final Thread thread = Thread.currentThread();
StringBuilder msg = new StringBuilder();
@@ -826,6 +886,7 @@ public final class SQLiteConnectionPool implements Closeable {
waiter.mSql = null;
waiter.mAssignedConnection = null;
waiter.mException = null;
+ waiter.mNonce += 1;
mConnectionWaiterPool = waiter;
}
@@ -904,5 +965,6 @@ public final class SQLiteConnectionPool implements Closeable {
public int mConnectionFlags;
public SQLiteConnection mAssignedConnection;
public RuntimeException mException;
+ public int mNonce;
}
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 9cb6480..7db7bfb 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -16,7 +16,9 @@
package android.database.sqlite;
+import android.content.CancelationSignal;
import android.content.ContentValues;
+import android.content.OperationCanceledException;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.DatabaseErrorHandler;
@@ -492,7 +494,7 @@ public class SQLiteDatabase extends SQLiteClosable {
boolean exclusive) {
getThreadSession().beginTransaction(exclusive ? SQLiteSession.TRANSACTION_MODE_EXCLUSIVE :
SQLiteSession.TRANSACTION_MODE_IMMEDIATE, transactionListener,
- getThreadDefaultConnectionFlags(false /*readOnly*/));
+ getThreadDefaultConnectionFlags(false /*readOnly*/), null);
}
/**
@@ -500,7 +502,7 @@ public class SQLiteDatabase extends SQLiteClosable {
* are committed and rolled back.
*/
public void endTransaction() {
- getThreadSession().endTransaction();
+ getThreadSession().endTransaction(null);
}
/**
@@ -597,7 +599,7 @@ public class SQLiteDatabase extends SQLiteClosable {
}
private boolean yieldIfContendedHelper(boolean throwIfUnsafe, long sleepAfterYieldDelay) {
- return getThreadSession().yieldTransaction(sleepAfterYieldDelay, throwIfUnsafe);
+ return getThreadSession().yieldTransaction(sleepAfterYieldDelay, throwIfUnsafe, null);
}
/**
@@ -935,7 +937,48 @@ public class SQLiteDatabase extends SQLiteClosable {
String selection, String[] selectionArgs, String groupBy,
String having, String orderBy, String limit) {
return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
- groupBy, having, orderBy, limit);
+ groupBy, having, orderBy, limit, null);
+ }
+
+ /**
+ * Query the given URL, returning a {@link Cursor} over the result set.
+ *
+ * @param distinct true if you want each row to be unique, false otherwise.
+ * @param table The table name to compile the query against.
+ * @param columns A list of which columns to return. Passing null will
+ * return all columns, which is discouraged to prevent reading
+ * data from storage that isn't going to be used.
+ * @param selection A filter declaring which rows to return, formatted as an
+ * SQL WHERE clause (excluding the WHERE itself). Passing null
+ * will return all rows for the given table.
+ * @param selectionArgs You may include ?s in selection, which will be
+ * replaced by the values from selectionArgs, in order that they
+ * appear in the selection. The values will be bound as Strings.
+ * @param groupBy A filter declaring how to group rows, formatted as an SQL
+ * GROUP BY clause (excluding the GROUP BY itself). Passing null
+ * will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in the cursor,
+ * if row grouping is being used, formatted as an SQL HAVING
+ * clause (excluding the HAVING itself). Passing null will cause
+ * all row groups to be included, and is required when row
+ * grouping is not being used.
+ * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
+ * (excluding the ORDER BY itself). Passing null will use the
+ * default sort order, which may be unordered.
+ * @param limit Limits the number of rows returned by the query,
+ * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+ * when the query is executed.
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
+ * @see Cursor
+ */
+ public Cursor query(boolean distinct, String table, String[] columns,
+ String selection, String[] selectionArgs, String groupBy,
+ String having, String orderBy, String limit, CancelationSignal cancelationSignal) {
+ return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
+ groupBy, having, orderBy, limit, cancelationSignal);
}
/**
@@ -974,12 +1017,55 @@ public class SQLiteDatabase extends SQLiteClosable {
boolean distinct, String table, String[] columns,
String selection, String[] selectionArgs, String groupBy,
String having, String orderBy, String limit) {
+ return queryWithFactory(cursorFactory, distinct, table, columns, selection,
+ selectionArgs, groupBy, having, orderBy, limit, null);
+ }
+
+ /**
+ * Query the given URL, returning a {@link Cursor} over the result set.
+ *
+ * @param cursorFactory the cursor factory to use, or null for the default factory
+ * @param distinct true if you want each row to be unique, false otherwise.
+ * @param table The table name to compile the query against.
+ * @param columns A list of which columns to return. Passing null will
+ * return all columns, which is discouraged to prevent reading
+ * data from storage that isn't going to be used.
+ * @param selection A filter declaring which rows to return, formatted as an
+ * SQL WHERE clause (excluding the WHERE itself). Passing null
+ * will return all rows for the given table.
+ * @param selectionArgs You may include ?s in selection, which will be
+ * replaced by the values from selectionArgs, in order that they
+ * appear in the selection. The values will be bound as Strings.
+ * @param groupBy A filter declaring how to group rows, formatted as an SQL
+ * GROUP BY clause (excluding the GROUP BY itself). Passing null
+ * will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in the cursor,
+ * if row grouping is being used, formatted as an SQL HAVING
+ * clause (excluding the HAVING itself). Passing null will cause
+ * all row groups to be included, and is required when row
+ * grouping is not being used.
+ * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
+ * (excluding the ORDER BY itself). Passing null will use the
+ * default sort order, which may be unordered.
+ * @param limit Limits the number of rows returned by the query,
+ * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+ * when the query is executed.
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
+ * @see Cursor
+ */
+ public Cursor queryWithFactory(CursorFactory cursorFactory,
+ boolean distinct, String table, String[] columns,
+ String selection, String[] selectionArgs, String groupBy,
+ String having, String orderBy, String limit, CancelationSignal cancelationSignal) {
throwIfNotOpen(); // fail fast
String sql = SQLiteQueryBuilder.buildQueryString(
distinct, table, columns, selection, groupBy, having, orderBy, limit);
- return rawQueryWithFactory(
- cursorFactory, sql, selectionArgs, findEditTable(table));
+ return rawQueryWithFactory(cursorFactory, sql, selectionArgs,
+ findEditTable(table), cancelationSignal);
}
/**
@@ -1067,7 +1153,25 @@ public class SQLiteDatabase extends SQLiteClosable {
* {@link Cursor}s are not synchronized, see the documentation for more details.
*/
public Cursor rawQuery(String sql, String[] selectionArgs) {
- return rawQueryWithFactory(null, sql, selectionArgs, null);
+ return rawQueryWithFactory(null, sql, selectionArgs, null, null);
+ }
+
+ /**
+ * Runs the provided SQL and returns a {@link Cursor} over the result set.
+ *
+ * @param sql the SQL query. The SQL string must not be ; terminated
+ * @param selectionArgs You may include ?s in where clause in the query,
+ * which will be replaced by the values from selectionArgs. The
+ * values will be bound as Strings.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+ * when the query is executed.
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
+ */
+ public Cursor rawQuery(String sql, String[] selectionArgs,
+ CancelationSignal cancelationSignal) {
+ return rawQueryWithFactory(null, sql, selectionArgs, null, cancelationSignal);
}
/**
@@ -1085,9 +1189,31 @@ public class SQLiteDatabase extends SQLiteClosable {
public Cursor rawQueryWithFactory(
CursorFactory cursorFactory, String sql, String[] selectionArgs,
String editTable) {
+ return rawQueryWithFactory(cursorFactory, sql, selectionArgs, editTable, null);
+ }
+
+ /**
+ * Runs the provided SQL and returns a cursor over the result set.
+ *
+ * @param cursorFactory the cursor factory to use, or null for the default factory
+ * @param sql the SQL query. The SQL string must not be ; terminated
+ * @param selectionArgs You may include ?s in where clause in the query,
+ * which will be replaced by the values from selectionArgs. The
+ * values will be bound as Strings.
+ * @param editTable the name of the first table, which is editable
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+ * when the query is executed.
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
+ */
+ public Cursor rawQueryWithFactory(
+ CursorFactory cursorFactory, String sql, String[] selectionArgs,
+ String editTable, CancelationSignal cancelationSignal) {
throwIfNotOpen(); // fail fast
- SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable);
+ SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,
+ cancelationSignal);
return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,
selectionArgs);
}
@@ -1786,7 +1912,7 @@ public class SQLiteDatabase extends SQLiteClosable {
*/
void lockPrimaryConnection() {
getThreadSession().beginTransaction(SQLiteSession.TRANSACTION_MODE_DEFERRED,
- null, SQLiteConnectionPool.CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY);
+ null, SQLiteConnectionPool.CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY, null);
}
/**
@@ -1795,7 +1921,7 @@ public class SQLiteDatabase extends SQLiteClosable {
* @see #lockPrimaryConnection()
*/
void unlockPrimaryConnection() {
- getThreadSession().endTransaction();
+ getThreadSession().endTransaction(null);
}
@Override
diff --git a/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java b/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
index 52fd1d2..c490dc6 100644
--- a/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
+++ b/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
@@ -16,6 +16,7 @@
package android.database.sqlite;
+import android.content.CancelationSignal;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
@@ -28,16 +29,19 @@ public class SQLiteDirectCursorDriver implements SQLiteCursorDriver {
private final SQLiteDatabase mDatabase;
private final String mEditTable;
private final String mSql;
+ private final CancelationSignal mCancelationSignal;
private SQLiteQuery mQuery;
- public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable) {
+ public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable,
+ CancelationSignal cancelationSignal) {
mDatabase = db;
mEditTable = editTable;
mSql = sql;
+ mCancelationSignal = cancelationSignal;
}
public Cursor query(CursorFactory factory, String[] selectionArgs) {
- final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql);
+ final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancelationSignal);
final Cursor cursor;
try {
query.bindAllArgsAsStrings(selectionArgs);
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 8194458..f3da2a6 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -16,6 +16,7 @@
package android.database.sqlite;
+import android.content.CancelationSignal;
import android.database.DatabaseUtils;
import java.util.Arrays;
@@ -36,7 +37,8 @@ public abstract class SQLiteProgram extends SQLiteClosable {
private final int mNumParameters;
private final Object[] mBindArgs;
- SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs) {
+ SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs,
+ CancelationSignal cancelationSignalForPrepare) {
mDatabase = db;
mSql = sql.trim();
@@ -54,7 +56,8 @@ public abstract class SQLiteProgram extends SQLiteClosable {
boolean assumeReadOnly = (n == DatabaseUtils.STATEMENT_SELECT);
SQLiteStatementInfo info = new SQLiteStatementInfo();
db.getThreadSession().prepare(mSql,
- db.getThreadDefaultConnectionFlags(assumeReadOnly), info);
+ db.getThreadDefaultConnectionFlags(assumeReadOnly),
+ cancelationSignalForPrepare, info);
mReadOnly = info.readOnly;
mColumnNames = info.columnNames;
mNumParameters = info.numParameters;
diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java
index 17aa886..df2e260 100644
--- a/core/java/android/database/sqlite/SQLiteQuery.java
+++ b/core/java/android/database/sqlite/SQLiteQuery.java
@@ -16,6 +16,8 @@
package android.database.sqlite;
+import android.content.CancelationSignal;
+import android.content.OperationCanceledException;
import android.database.CursorWindow;
import android.util.Log;
@@ -29,8 +31,12 @@ import android.util.Log;
public final class SQLiteQuery extends SQLiteProgram {
private static final String TAG = "SQLiteQuery";
- SQLiteQuery(SQLiteDatabase db, String query) {
- super(db, query, null);
+ private final CancelationSignal mCancelationSignal;
+
+ SQLiteQuery(SQLiteDatabase db, String query, CancelationSignal cancelationSignal) {
+ super(db, query, null, cancelationSignal);
+
+ mCancelationSignal = cancelationSignal;
}
/**
@@ -44,6 +50,9 @@ public final class SQLiteQuery extends SQLiteProgram {
* return regardless of whether they fit in the window.
* @return Number of rows that were enumerated. Might not be all rows
* unless countAllRows is true.
+ *
+ * @throws SQLiteException if an error occurs.
+ * @throws OperationCanceledException if the operation was canceled.
*/
int fillWindow(CursorWindow window, int startPos, int requiredPos, boolean countAllRows) {
acquireReference();
@@ -51,7 +60,8 @@ public final class SQLiteQuery extends SQLiteProgram {
window.acquireReference();
try {
int numRows = getSession().executeForCursorWindow(getSql(), getBindArgs(),
- window, startPos, requiredPos, countAllRows, getConnectionFlags());
+ window, startPos, requiredPos, countAllRows, getConnectionFlags(),
+ mCancelationSignal);
return numRows;
} catch (SQLiteDatabaseCorruptException ex) {
onCorruption();
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index 1b7b398..89469cb 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -16,6 +16,8 @@
package android.database.sqlite;
+import android.content.CancelationSignal;
+import android.content.OperationCanceledException;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.provider.BaseColumns;
@@ -137,8 +139,9 @@ public class SQLiteQueryBuilder
/**
* Sets the cursor factory to be used for the query. You can use
* one factory for all queries on a database but it is normally
- * easier to specify the factory when doing this query. @param
- * factory the factor to use
+ * easier to specify the factory when doing this query.
+ *
+ * @param factory the factory to use.
*/
public void setCursorFactory(SQLiteDatabase.CursorFactory factory) {
mFactory = factory;
@@ -289,7 +292,7 @@ public class SQLiteQueryBuilder
String selection, String[] selectionArgs, String groupBy,
String having, String sortOrder) {
return query(db, projectionIn, selection, selectionArgs, groupBy, having, sortOrder,
- null /* limit */);
+ null /* limit */, null /* cancelationSignal */);
}
/**
@@ -327,6 +330,48 @@ public class SQLiteQueryBuilder
public Cursor query(SQLiteDatabase db, String[] projectionIn,
String selection, String[] selectionArgs, String groupBy,
String having, String sortOrder, String limit) {
+ return query(db, projectionIn, selection, selectionArgs,
+ groupBy, having, sortOrder, limit, null);
+ }
+
+ /**
+ * Perform a query by combining all current settings and the
+ * information passed into this method.
+ *
+ * @param db the database to query on
+ * @param projectionIn A list of which columns to return. Passing
+ * null will return all columns, which is discouraged to prevent
+ * reading data from storage that isn't going to be used.
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given URL.
+ * @param selectionArgs You may include ?s in selection, which
+ * will be replaced by the values from selectionArgs, in order
+ * that they appear in the selection. The values will be bound
+ * as Strings.
+ * @param groupBy A filter declaring how to group rows, formatted
+ * as an SQL GROUP BY clause (excluding the GROUP BY
+ * itself). Passing null will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in
+ * the cursor, if row grouping is being used, formatted as an
+ * SQL HAVING clause (excluding the HAVING itself). Passing
+ * null will cause all row groups to be included, and is
+ * required when row grouping is not being used.
+ * @param sortOrder How to order the rows, formatted as an SQL
+ * ORDER BY clause (excluding the ORDER BY itself). Passing null
+ * will use the default sort order, which may be unordered.
+ * @param limit Limits the number of rows returned by the query,
+ * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+ * when the query is executed.
+ * @return a cursor over the result set
+ * @see android.content.ContentResolver#query(android.net.Uri, String[],
+ * String, String[], String)
+ */
+ public Cursor query(SQLiteDatabase db, String[] projectionIn,
+ String selection, String[] selectionArgs, String groupBy,
+ String having, String sortOrder, String limit, CancelationSignal cancelationSignal) {
if (mTables == null) {
return null;
}
@@ -341,7 +386,8 @@ public class SQLiteQueryBuilder
// in both the wrapped and original forms.
String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy,
having, sortOrder, limit);
- validateQuerySql(db, sqlForValidation); // will throw if query is invalid
+ validateQuerySql(db, sqlForValidation,
+ cancelationSignal); // will throw if query is invalid
}
String sql = buildQuery(
@@ -353,16 +399,18 @@ public class SQLiteQueryBuilder
}
return db.rawQueryWithFactory(
mFactory, sql, selectionArgs,
- SQLiteDatabase.findEditTable(mTables)); // will throw if query is invalid
+ SQLiteDatabase.findEditTable(mTables),
+ cancelationSignal); // will throw if query is invalid
}
/**
* Verifies that a SQL SELECT statement is valid by compiling it.
* If the SQL statement is not valid, this method will throw a {@link SQLiteException}.
*/
- private void validateQuerySql(SQLiteDatabase db, String sql) {
+ private void validateQuerySql(SQLiteDatabase db, String sql,
+ CancelationSignal cancelationSignal) {
db.getThreadSession().prepare(sql,
- db.getThreadDefaultConnectionFlags(true /*readOnly*/), null);
+ db.getThreadDefaultConnectionFlags(true /*readOnly*/), cancelationSignal, null);
}
/**
diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java
index a933051..b5a3e31 100644
--- a/core/java/android/database/sqlite/SQLiteSession.java
+++ b/core/java/android/database/sqlite/SQLiteSession.java
@@ -16,6 +16,8 @@
package android.database.sqlite;
+import android.content.CancelationSignal;
+import android.content.OperationCanceledException;
import android.database.CursorWindow;
import android.database.DatabaseUtils;
import android.os.ParcelFileDescriptor;
@@ -156,8 +158,6 @@ import android.os.ParcelFileDescriptor;
* triggers may call custom SQLite functions that perform additional queries.
* </p>
*
- * TODO: Support timeouts on all possibly blocking operations.
- *
* @hide
*/
public final class SQLiteSession {
@@ -280,24 +280,34 @@ public final class SQLiteSession {
* @param transactionListener The transaction listener, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
*
* @throws IllegalStateException if {@link #setTransactionSuccessful} has already been
* called for the current transaction.
+ * @throws SQLiteException if an error occurs.
+ * @throws OperationCanceledException if the operation was canceled.
*
* @see #setTransactionSuccessful
* @see #yieldTransaction
* @see #endTransaction
*/
public void beginTransaction(int transactionMode,
- SQLiteTransactionListener transactionListener, int connectionFlags) {
+ SQLiteTransactionListener transactionListener, int connectionFlags,
+ CancelationSignal cancelationSignal) {
throwIfTransactionMarkedSuccessful();
- beginTransactionUnchecked(transactionMode, transactionListener, connectionFlags);
+ beginTransactionUnchecked(transactionMode, transactionListener, connectionFlags,
+ cancelationSignal);
}
private void beginTransactionUnchecked(int transactionMode,
- SQLiteTransactionListener transactionListener, int connectionFlags) {
+ SQLiteTransactionListener transactionListener, int connectionFlags,
+ CancelationSignal cancelationSignal) {
+ if (cancelationSignal != null) {
+ cancelationSignal.throwIfCanceled();
+ }
+
if (mTransactionStack == null) {
- acquireConnection(null, connectionFlags); // might throw
+ acquireConnection(null, connectionFlags, cancelationSignal); // might throw
}
try {
// Set up the transaction such that we can back out safely
@@ -306,13 +316,15 @@ public final class SQLiteSession {
// Execute SQL might throw a runtime exception.
switch (transactionMode) {
case TRANSACTION_MODE_IMMEDIATE:
- mConnection.execute("BEGIN IMMEDIATE;", null); // might throw
+ mConnection.execute("BEGIN IMMEDIATE;", null,
+ cancelationSignal); // might throw
break;
case TRANSACTION_MODE_EXCLUSIVE:
- mConnection.execute("BEGIN EXCLUSIVE;", null); // might throw
+ mConnection.execute("BEGIN EXCLUSIVE;", null,
+ cancelationSignal); // might throw
break;
default:
- mConnection.execute("BEGIN;", null); // might throw
+ mConnection.execute("BEGIN;", null, cancelationSignal); // might throw
break;
}
}
@@ -323,7 +335,7 @@ public final class SQLiteSession {
transactionListener.onBegin(); // might throw
} catch (RuntimeException ex) {
if (mTransactionStack == null) {
- mConnection.execute("ROLLBACK;", null); // might throw
+ mConnection.execute("ROLLBACK;", null, cancelationSignal); // might throw
}
throw ex;
}
@@ -372,20 +384,28 @@ public final class SQLiteSession {
* This method must be called exactly once for each call to {@link #beginTransaction}.
* </p>
*
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ *
* @throws IllegalStateException if there is no current transaction.
+ * @throws SQLiteException if an error occurs.
+ * @throws OperationCanceledException if the operation was canceled.
*
* @see #beginTransaction
* @see #setTransactionSuccessful
* @see #yieldTransaction
*/
- public void endTransaction() {
+ public void endTransaction(CancelationSignal cancelationSignal) {
throwIfNoTransaction();
assert mConnection != null;
- endTransactionUnchecked();
+ endTransactionUnchecked(cancelationSignal);
}
- private void endTransactionUnchecked() {
+ private void endTransactionUnchecked(CancelationSignal cancelationSignal) {
+ if (cancelationSignal != null) {
+ cancelationSignal.throwIfCanceled();
+ }
+
final Transaction top = mTransactionStack;
boolean successful = top.mMarkedSuccessful && !top.mChildFailed;
@@ -414,9 +434,9 @@ public final class SQLiteSession {
} else {
try {
if (successful) {
- mConnection.execute("COMMIT;", null); // might throw
+ mConnection.execute("COMMIT;", null, cancelationSignal); // might throw
} else {
- mConnection.execute("ROLLBACK;", null); // might throw
+ mConnection.execute("ROLLBACK;", null, cancelationSignal); // might throw
}
} finally {
releaseConnection(); // might throw
@@ -467,16 +487,20 @@ public final class SQLiteSession {
* @param throwIfUnsafe If true, then instead of returning false when no
* transaction is in progress, a nested transaction is in progress, or when
* the transaction has already been marked successful, throws {@link IllegalStateException}.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @return True if the transaction was actually yielded.
*
* @throws IllegalStateException if <code>throwIfNested</code> is true and
* there is no current transaction, there is a nested transaction in progress or
* if {@link #setTransactionSuccessful} has already been called for the current transaction.
+ * @throws SQLiteException if an error occurs.
+ * @throws OperationCanceledException if the operation was canceled.
*
* @see #beginTransaction
* @see #endTransaction
*/
- public boolean yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe) {
+ public boolean yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe,
+ CancelationSignal cancelationSignal) {
if (throwIfUnsafe) {
throwIfNoTransaction();
throwIfTransactionMarkedSuccessful();
@@ -493,10 +517,16 @@ public final class SQLiteSession {
return false;
}
- return yieldTransactionUnchecked(sleepAfterYieldDelayMillis); // might throw
+ return yieldTransactionUnchecked(sleepAfterYieldDelayMillis,
+ cancelationSignal); // might throw
}
- private boolean yieldTransactionUnchecked(long sleepAfterYieldDelayMillis) {
+ private boolean yieldTransactionUnchecked(long sleepAfterYieldDelayMillis,
+ CancelationSignal cancelationSignal) {
+ if (cancelationSignal != null) {
+ cancelationSignal.throwIfCanceled();
+ }
+
if (!mConnectionPool.shouldYieldConnection(mConnection, mConnectionFlags)) {
return false;
}
@@ -504,7 +534,7 @@ public final class SQLiteSession {
final int transactionMode = mTransactionStack.mMode;
final SQLiteTransactionListener listener = mTransactionStack.mListener;
final int connectionFlags = mConnectionFlags;
- endTransactionUnchecked(); // might throw
+ endTransactionUnchecked(cancelationSignal); // might throw
if (sleepAfterYieldDelayMillis > 0) {
try {
@@ -514,7 +544,8 @@ public final class SQLiteSession {
}
}
- beginTransactionUnchecked(transactionMode, listener, connectionFlags); // might throw
+ beginTransactionUnchecked(transactionMode, listener, connectionFlags,
+ cancelationSignal); // might throw
return true;
}
@@ -535,17 +566,24 @@ public final class SQLiteSession {
* @param sql The SQL statement to prepare.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @param outStatementInfo The {@link SQLiteStatementInfo} object to populate
* with information about the statement, or null if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error.
+ * @throws OperationCanceledException if the operation was canceled.
*/
- public void prepare(String sql, int connectionFlags, SQLiteStatementInfo outStatementInfo) {
+ public void prepare(String sql, int connectionFlags, CancelationSignal cancelationSignal,
+ SQLiteStatementInfo outStatementInfo) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
- acquireConnection(sql, connectionFlags); // might throw
+ if (cancelationSignal != null) {
+ cancelationSignal.throwIfCanceled();
+ }
+
+ acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
try {
mConnection.prepare(sql, outStatementInfo); // might throw
} finally {
@@ -560,22 +598,25 @@ public final class SQLiteSession {
* @param bindArgs The arguments to bind, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
+ * @throws OperationCanceledException if the operation was canceled.
*/
- public void execute(String sql, Object[] bindArgs, int connectionFlags) {
+ public void execute(String sql, Object[] bindArgs, int connectionFlags,
+ CancelationSignal cancelationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
- if (executeSpecial(sql, bindArgs, connectionFlags)) {
+ if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
return;
}
- acquireConnection(sql, connectionFlags); // might throw
+ acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
try {
- mConnection.execute(sql, bindArgs); // might throw
+ mConnection.execute(sql, bindArgs, cancelationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
@@ -588,24 +629,27 @@ public final class SQLiteSession {
* @param bindArgs The arguments to bind, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @return The value of the first column in the first row of the result set
* as a <code>long</code>, or zero if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
+ * @throws OperationCanceledException if the operation was canceled.
*/
- public long executeForLong(String sql, Object[] bindArgs, int connectionFlags) {
+ public long executeForLong(String sql, Object[] bindArgs, int connectionFlags,
+ CancelationSignal cancelationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
- if (executeSpecial(sql, bindArgs, connectionFlags)) {
+ if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
return 0;
}
- acquireConnection(sql, connectionFlags); // might throw
+ acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
try {
- return mConnection.executeForLong(sql, bindArgs); // might throw
+ return mConnection.executeForLong(sql, bindArgs, cancelationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
@@ -618,24 +662,27 @@ public final class SQLiteSession {
* @param bindArgs The arguments to bind, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @return The value of the first column in the first row of the result set
* as a <code>String</code>, or null if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
+ * @throws OperationCanceledException if the operation was canceled.
*/
- public String executeForString(String sql, Object[] bindArgs, int connectionFlags) {
+ public String executeForString(String sql, Object[] bindArgs, int connectionFlags,
+ CancelationSignal cancelationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
- if (executeSpecial(sql, bindArgs, connectionFlags)) {
+ if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
return null;
}
- acquireConnection(sql, connectionFlags); // might throw
+ acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
try {
- return mConnection.executeForString(sql, bindArgs); // might throw
+ return mConnection.executeForString(sql, bindArgs, cancelationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
@@ -649,26 +696,29 @@ public final class SQLiteSession {
* @param bindArgs The arguments to bind, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @return The file descriptor for a shared memory region that contains
* the value of the first column in the first row of the result set as a BLOB,
* or null if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
+ * @throws OperationCanceledException if the operation was canceled.
*/
public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs,
- int connectionFlags) {
+ int connectionFlags, CancelationSignal cancelationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
- if (executeSpecial(sql, bindArgs, connectionFlags)) {
+ if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
return null;
}
- acquireConnection(sql, connectionFlags); // might throw
+ acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
try {
- return mConnection.executeForBlobFileDescriptor(sql, bindArgs); // might throw
+ return mConnection.executeForBlobFileDescriptor(sql, bindArgs,
+ cancelationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
@@ -682,23 +732,27 @@ public final class SQLiteSession {
* @param bindArgs The arguments to bind, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @return The number of rows that were changed.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
+ * @throws OperationCanceledException if the operation was canceled.
*/
- public int executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags) {
+ public int executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags,
+ CancelationSignal cancelationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
- if (executeSpecial(sql, bindArgs, connectionFlags)) {
+ if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
return 0;
}
- acquireConnection(sql, connectionFlags); // might throw
+ acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
try {
- return mConnection.executeForChangedRowCount(sql, bindArgs); // might throw
+ return mConnection.executeForChangedRowCount(sql, bindArgs,
+ cancelationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
@@ -712,23 +766,27 @@ public final class SQLiteSession {
* @param bindArgs The arguments to bind, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @return The row id of the last row that was inserted, or 0 if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
+ * @throws OperationCanceledException if the operation was canceled.
*/
- public long executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags) {
+ public long executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags,
+ CancelationSignal cancelationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
- if (executeSpecial(sql, bindArgs, connectionFlags)) {
+ if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
return 0;
}
- acquireConnection(sql, connectionFlags); // might throw
+ acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
try {
- return mConnection.executeForLastInsertedRowId(sql, bindArgs); // might throw
+ return mConnection.executeForLastInsertedRowId(sql, bindArgs,
+ cancelationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
@@ -750,15 +808,17 @@ public final class SQLiteSession {
* regagless of whether they fit in the window.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @return The number of rows that were counted during query execution. Might
* not be all rows in the result set unless <code>countAllRows</code> is true.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
+ * @throws OperationCanceledException if the operation was canceled.
*/
public int executeForCursorWindow(String sql, Object[] bindArgs,
CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
- int connectionFlags) {
+ int connectionFlags, CancelationSignal cancelationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -766,15 +826,16 @@ public final class SQLiteSession {
throw new IllegalArgumentException("window must not be null.");
}
- if (executeSpecial(sql, bindArgs, connectionFlags)) {
+ if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
window.clear();
return 0;
}
- acquireConnection(sql, connectionFlags); // might throw
+ acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
try {
return mConnection.executeForCursorWindow(sql, bindArgs,
- window, startPos, requiredPos, countAllRows); // might throw
+ window, startPos, requiredPos, countAllRows,
+ cancelationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
@@ -793,35 +854,45 @@ public final class SQLiteSession {
* @param bindArgs The arguments to bind, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
+ * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
* @return True if the statement was of a special form that was handled here,
* false otherwise.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
+ * @throws OperationCanceledException if the operation was canceled.
*/
- private boolean executeSpecial(String sql, Object[] bindArgs, int connectionFlags) {
+ private boolean executeSpecial(String sql, Object[] bindArgs, int connectionFlags,
+ CancelationSignal cancelationSignal) {
+ if (cancelationSignal != null) {
+ cancelationSignal.throwIfCanceled();
+ }
+
final int type = DatabaseUtils.getSqlStatementType(sql);
switch (type) {
case DatabaseUtils.STATEMENT_BEGIN:
- beginTransaction(TRANSACTION_MODE_EXCLUSIVE, null, connectionFlags);
+ beginTransaction(TRANSACTION_MODE_EXCLUSIVE, null, connectionFlags,
+ cancelationSignal);
return true;
case DatabaseUtils.STATEMENT_COMMIT:
setTransactionSuccessful();
- endTransaction();
+ endTransaction(cancelationSignal);
return true;
case DatabaseUtils.STATEMENT_ABORT:
- endTransaction();
+ endTransaction(cancelationSignal);
return true;
}
return false;
}
- private void acquireConnection(String sql, int connectionFlags) {
+ private void acquireConnection(String sql, int connectionFlags,
+ CancelationSignal cancelationSignal) {
if (mConnection == null) {
assert mConnectionUseCount == 0;
- mConnection = mConnectionPool.acquireConnection(sql, connectionFlags); // might throw
+ mConnection = mConnectionPool.acquireConnection(sql, connectionFlags,
+ cancelationSignal); // might throw
mConnectionFlags = connectionFlags;
}
mConnectionUseCount += 1;
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index 4e20da0..b1092d7 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -28,7 +28,7 @@ import android.os.ParcelFileDescriptor;
*/
public final class SQLiteStatement extends SQLiteProgram {
SQLiteStatement(SQLiteDatabase db, String sql, Object[] bindArgs) {
- super(db, sql, bindArgs);
+ super(db, sql, bindArgs, null);
}
/**
@@ -41,7 +41,7 @@ public final class SQLiteStatement extends SQLiteProgram {
public void execute() {
acquireReference();
try {
- getSession().execute(getSql(), getBindArgs(), getConnectionFlags());
+ getSession().execute(getSql(), getBindArgs(), getConnectionFlags(), null);
} catch (SQLiteDatabaseCorruptException ex) {
onCorruption();
throw ex;
@@ -62,7 +62,7 @@ public final class SQLiteStatement extends SQLiteProgram {
acquireReference();
try {
return getSession().executeForChangedRowCount(
- getSql(), getBindArgs(), getConnectionFlags());
+ getSql(), getBindArgs(), getConnectionFlags(), null);
} catch (SQLiteDatabaseCorruptException ex) {
onCorruption();
throw ex;
@@ -84,7 +84,7 @@ public final class SQLiteStatement extends SQLiteProgram {
acquireReference();
try {
return getSession().executeForLastInsertedRowId(
- getSql(), getBindArgs(), getConnectionFlags());
+ getSql(), getBindArgs(), getConnectionFlags(), null);
} catch (SQLiteDatabaseCorruptException ex) {
onCorruption();
throw ex;
@@ -105,7 +105,7 @@ public final class SQLiteStatement extends SQLiteProgram {
acquireReference();
try {
return getSession().executeForLong(
- getSql(), getBindArgs(), getConnectionFlags());
+ getSql(), getBindArgs(), getConnectionFlags(), null);
} catch (SQLiteDatabaseCorruptException ex) {
onCorruption();
throw ex;
@@ -126,7 +126,7 @@ public final class SQLiteStatement extends SQLiteProgram {
acquireReference();
try {
return getSession().executeForString(
- getSql(), getBindArgs(), getConnectionFlags());
+ getSql(), getBindArgs(), getConnectionFlags(), null);
} catch (SQLiteDatabaseCorruptException ex) {
onCorruption();
throw ex;
@@ -147,7 +147,7 @@ public final class SQLiteStatement extends SQLiteProgram {
acquireReference();
try {
return getSession().executeForBlobFileDescriptor(
- getSql(), getBindArgs(), getConnectionFlags());
+ getSql(), getBindArgs(), getConnectionFlags(), null);
} catch (SQLiteDatabaseCorruptException ex) {
onCorruption();
throw ex;