blob: a9ac9e7f882fc115bea108d45a8216c6562b56e0 [file] [log] [blame]
Jeff Browne5360fb2011-10-31 17:48:13 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.database.sqlite;
18
Mathew Inwood41b31942018-08-10 16:00:53 +010019import android.annotation.UnsupportedAppUsage;
Jeff Browne5360fb2011-10-31 17:48:13 -070020import android.database.CursorWindow;
21import android.database.DatabaseUtils;
Jeff Browna7771df2012-05-07 20:06:46 -070022import android.os.CancellationSignal;
23import android.os.OperationCanceledException;
Jeff Browne5360fb2011-10-31 17:48:13 -070024import android.os.ParcelFileDescriptor;
25
26/**
27 * Provides a single client the ability to use a database.
28 *
29 * <h2>About database sessions</h2>
30 * <p>
31 * Database access is always performed using a session. The session
32 * manages the lifecycle of transactions and database connections.
33 * </p><p>
34 * Sessions can be used to perform both read-only and read-write operations.
35 * There is some advantage to knowing when a session is being used for
36 * read-only purposes because the connection pool can optimize the use
37 * of the available connections to permit multiple read-only operations
38 * to execute in parallel whereas read-write operations may need to be serialized.
39 * </p><p>
40 * When <em>Write Ahead Logging (WAL)</em> is enabled, the database can
41 * execute simultaneous read-only and read-write transactions, provided that
42 * at most one read-write transaction is performed at a time. When WAL is not
43 * enabled, read-only transactions can execute in parallel but read-write
44 * transactions are mutually exclusive.
45 * </p>
46 *
47 * <h2>Ownership and concurrency guarantees</h2>
48 * <p>
49 * Session objects are not thread-safe. In fact, session objects are thread-bound.
50 * The {@link SQLiteDatabase} uses a thread-local variable to associate a session
51 * with each thread for the use of that thread alone. Consequently, each thread
52 * has its own session object and therefore its own transaction state independent
53 * of other threads.
54 * </p><p>
55 * A thread has at most one session per database. This constraint ensures that
56 * a thread can never use more than one database connection at a time for a
57 * given database. As the number of available database connections is limited,
58 * if a single thread tried to acquire multiple connections for the same database
59 * at the same time, it might deadlock. Therefore we allow there to be only
60 * one session (so, at most one connection) per thread per database.
61 * </p>
62 *
63 * <h2>Transactions</h2>
64 * <p>
65 * There are two kinds of transaction: implicit transactions and explicit
66 * transactions.
67 * </p><p>
68 * An implicit transaction is created whenever a database operation is requested
69 * and there is no explicit transaction currently in progress. An implicit transaction
70 * only lasts for the duration of the database operation in question and then it
71 * is ended. If the database operation was successful, then its changes are committed.
72 * </p><p>
73 * An explicit transaction is started by calling {@link #beginTransaction} and
74 * specifying the desired transaction mode. Once an explicit transaction has begun,
75 * all subsequent database operations will be performed as part of that transaction.
76 * To end an explicit transaction, first call {@link #setTransactionSuccessful} if the
77 * transaction was successful, then call {@link #end}. If the transaction was
78 * marked successful, its changes will be committed, otherwise they will be rolled back.
79 * </p><p>
80 * Explicit transactions can also be nested. A nested explicit transaction is
81 * started with {@link #beginTransaction}, marked successful with
82 * {@link #setTransactionSuccessful}and ended with {@link #endTransaction}.
83 * If any nested transaction is not marked successful, then the entire transaction
84 * including all of its nested transactions will be rolled back
85 * when the outermost transaction is ended.
86 * </p><p>
87 * To improve concurrency, an explicit transaction can be yielded by calling
88 * {@link #yieldTransaction}. If there is contention for use of the database,
89 * then yielding ends the current transaction, commits its changes, releases the
90 * database connection for use by another session for a little while, and starts a
91 * new transaction with the same properties as the original one.
92 * Changes committed by {@link #yieldTransaction} cannot be rolled back.
93 * </p><p>
94 * When a transaction is started, the client can provide a {@link SQLiteTransactionListener}
95 * to listen for notifications of transaction-related events.
96 * </p><p>
97 * Recommended usage:
98 * <code><pre>
99 * // First, begin the transaction.
100 * session.beginTransaction(SQLiteSession.TRANSACTION_MODE_DEFERRED, 0);
101 * try {
102 * // Then do stuff...
103 * session.execute("INSERT INTO ...", null, 0);
104 *
105 * // As the very last step before ending the transaction, mark it successful.
106 * session.setTransactionSuccessful();
107 * } finally {
108 * // Finally, end the transaction.
109 * // This statement will commit the transaction if it was marked successful or
110 * // roll it back otherwise.
111 * session.endTransaction();
112 * }
113 * </pre></code>
114 * </p>
115 *
116 * <h2>Database connections</h2>
117 * <p>
118 * A {@link SQLiteDatabase} can have multiple active sessions at the same
119 * time. Each session acquires and releases connections to the database
120 * as needed to perform each requested database transaction. If all connections
121 * are in use, then database transactions on some sessions will block until a
122 * connection becomes available.
123 * </p><p>
124 * The session acquires a single database connection only for the duration
125 * of a single (implicit or explicit) database transaction, then releases it.
126 * This characteristic allows a small pool of database connections to be shared
127 * efficiently by multiple sessions as long as they are not all trying to perform
128 * database transactions at the same time.
129 * </p>
130 *
131 * <h2>Responsiveness</h2>
132 * <p>
133 * Because there are a limited number of database connections and the session holds
134 * a database connection for the entire duration of a database transaction,
135 * it is important to keep transactions short. This is especially important
136 * for read-write transactions since they may block other transactions
137 * from executing. Consider calling {@link #yieldTransaction} periodically
138 * during long-running transactions.
139 * </p><p>
140 * Another important consideration is that transactions that take too long to
141 * run may cause the application UI to become unresponsive. Even if the transaction
142 * is executed in a background thread, the user will get bored and
143 * frustrated if the application shows no data for several seconds while
144 * a transaction runs.
145 * </p><p>
146 * Guidelines:
147 * <ul>
148 * <li>Do not perform database transactions on the UI thread.</li>
149 * <li>Keep database transactions as short as possible.</li>
150 * <li>Simple queries often run faster than complex queries.</li>
151 * <li>Measure the performance of your database transactions.</li>
152 * <li>Consider what will happen when the size of the data set grows.
153 * A query that works well on 100 rows may struggle with 10,000.</li>
154 * </ul>
155 *
Jeff Browna9be4152012-01-18 15:29:57 -0800156 * <h2>Reentrance</h2>
157 * <p>
158 * This class must tolerate reentrant execution of SQLite operations because
159 * triggers may call custom SQLite functions that perform additional queries.
160 * </p>
161 *
Jeff Browne5360fb2011-10-31 17:48:13 -0700162 * @hide
163 */
164public final class SQLiteSession {
165 private final SQLiteConnectionPool mConnectionPool;
166
167 private SQLiteConnection mConnection;
168 private int mConnectionFlags;
Jeff Browna9be4152012-01-18 15:29:57 -0800169 private int mConnectionUseCount;
Jeff Browne5360fb2011-10-31 17:48:13 -0700170 private Transaction mTransactionPool;
171 private Transaction mTransactionStack;
172
173 /**
174 * Transaction mode: Deferred.
175 * <p>
176 * In a deferred transaction, no locks are acquired on the database
177 * until the first operation is performed. If the first operation is
178 * read-only, then a <code>SHARED</code> lock is acquired, otherwise
179 * a <code>RESERVED</code> lock is acquired.
180 * </p><p>
181 * While holding a <code>SHARED</code> lock, this session is only allowed to
182 * read but other sessions are allowed to read or write.
183 * While holding a <code>RESERVED</code> lock, this session is allowed to read
184 * or write but other sessions are only allowed to read.
185 * </p><p>
186 * Because the lock is only acquired when needed in a deferred transaction,
187 * it is possible for another session to write to the database first before
188 * this session has a chance to do anything.
189 * </p><p>
190 * Corresponds to the SQLite <code>BEGIN DEFERRED</code> transaction mode.
191 * </p>
192 */
193 public static final int TRANSACTION_MODE_DEFERRED = 0;
194
195 /**
196 * Transaction mode: Immediate.
197 * <p>
198 * When an immediate transaction begins, the session acquires a
199 * <code>RESERVED</code> lock.
200 * </p><p>
201 * While holding a <code>RESERVED</code> lock, this session is allowed to read
202 * or write but other sessions are only allowed to read.
203 * </p><p>
204 * Corresponds to the SQLite <code>BEGIN IMMEDIATE</code> transaction mode.
205 * </p>
206 */
207 public static final int TRANSACTION_MODE_IMMEDIATE = 1;
208
209 /**
210 * Transaction mode: Exclusive.
211 * <p>
212 * When an exclusive transaction begins, the session acquires an
213 * <code>EXCLUSIVE</code> lock.
214 * </p><p>
215 * While holding an <code>EXCLUSIVE</code> lock, this session is allowed to read
216 * or write but no other sessions are allowed to access the database.
217 * </p><p>
218 * Corresponds to the SQLite <code>BEGIN EXCLUSIVE</code> transaction mode.
219 * </p>
220 */
221 public static final int TRANSACTION_MODE_EXCLUSIVE = 2;
222
223 /**
224 * Creates a session bound to the specified connection pool.
225 *
226 * @param connectionPool The connection pool.
227 */
228 public SQLiteSession(SQLiteConnectionPool connectionPool) {
229 if (connectionPool == null) {
230 throw new IllegalArgumentException("connectionPool must not be null");
231 }
232
233 mConnectionPool = connectionPool;
234 }
235
236 /**
237 * Returns true if the session has a transaction in progress.
238 *
239 * @return True if the session has a transaction in progress.
240 */
241 public boolean hasTransaction() {
242 return mTransactionStack != null;
243 }
244
245 /**
246 * Returns true if the session has a nested transaction in progress.
247 *
248 * @return True if the session has a nested transaction in progress.
249 */
250 public boolean hasNestedTransaction() {
251 return mTransactionStack != null && mTransactionStack.mParent != null;
252 }
253
254 /**
255 * Returns true if the session has an active database connection.
256 *
257 * @return True if the session has an active database connection.
258 */
259 public boolean hasConnection() {
260 return mConnection != null;
261 }
262
263 /**
264 * Begins a transaction.
265 * <p>
266 * Transactions may nest. If the transaction is not in progress,
267 * then a database connection is obtained and a new transaction is started.
268 * Otherwise, a nested transaction is started.
269 * </p><p>
270 * Each call to {@link #beginTransaction} must be matched exactly by a call
271 * to {@link #endTransaction}. To mark a transaction as successful,
272 * call {@link #setTransactionSuccessful} before calling {@link #endTransaction}.
273 * If the transaction is not successful, or if any of its nested
274 * transactions were not successful, then the entire transaction will
275 * be rolled back when the outermost transaction is ended.
276 * </p>
277 *
278 * @param transactionMode The transaction mode. One of: {@link #TRANSACTION_MODE_DEFERRED},
279 * {@link #TRANSACTION_MODE_IMMEDIATE}, or {@link #TRANSACTION_MODE_EXCLUSIVE}.
280 * Ignored when creating a nested transaction.
281 * @param transactionListener The transaction listener, or null if none.
282 * @param connectionFlags The connection flags to use if a connection must be
283 * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
Jeff Brown4c1241d2012-02-02 17:05:00 -0800284 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
Jeff Browne5360fb2011-10-31 17:48:13 -0700285 *
286 * @throws IllegalStateException if {@link #setTransactionSuccessful} has already been
287 * called for the current transaction.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800288 * @throws SQLiteException if an error occurs.
289 * @throws OperationCanceledException if the operation was canceled.
Jeff Browne5360fb2011-10-31 17:48:13 -0700290 *
291 * @see #setTransactionSuccessful
292 * @see #yieldTransaction
293 * @see #endTransaction
294 */
Mathew Inwood41b31942018-08-10 16:00:53 +0100295 @UnsupportedAppUsage
Jeff Browne5360fb2011-10-31 17:48:13 -0700296 public void beginTransaction(int transactionMode,
Jeff Brown75ea64f2012-01-25 19:37:13 -0800297 SQLiteTransactionListener transactionListener, int connectionFlags,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800298 CancellationSignal cancellationSignal) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700299 throwIfTransactionMarkedSuccessful();
Jeff Brown75ea64f2012-01-25 19:37:13 -0800300 beginTransactionUnchecked(transactionMode, transactionListener, connectionFlags,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800301 cancellationSignal);
Jeff Browne5360fb2011-10-31 17:48:13 -0700302 }
303
304 private void beginTransactionUnchecked(int transactionMode,
Jeff Brown75ea64f2012-01-25 19:37:13 -0800305 SQLiteTransactionListener transactionListener, int connectionFlags,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800306 CancellationSignal cancellationSignal) {
307 if (cancellationSignal != null) {
308 cancellationSignal.throwIfCanceled();
Jeff Brown75ea64f2012-01-25 19:37:13 -0800309 }
310
Jeff Browna9be4152012-01-18 15:29:57 -0800311 if (mTransactionStack == null) {
Jeff Brown4c1241d2012-02-02 17:05:00 -0800312 acquireConnection(null, connectionFlags, cancellationSignal); // might throw
Jeff Browna9be4152012-01-18 15:29:57 -0800313 }
Jeff Browne5360fb2011-10-31 17:48:13 -0700314 try {
315 // Set up the transaction such that we can back out safely
316 // in case we fail part way.
317 if (mTransactionStack == null) {
318 // Execute SQL might throw a runtime exception.
319 switch (transactionMode) {
320 case TRANSACTION_MODE_IMMEDIATE:
Jeff Brown75ea64f2012-01-25 19:37:13 -0800321 mConnection.execute("BEGIN IMMEDIATE;", null,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800322 cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700323 break;
324 case TRANSACTION_MODE_EXCLUSIVE:
Jeff Brown75ea64f2012-01-25 19:37:13 -0800325 mConnection.execute("BEGIN EXCLUSIVE;", null,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800326 cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700327 break;
328 default:
Jeff Brown4c1241d2012-02-02 17:05:00 -0800329 mConnection.execute("BEGIN;", null, cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700330 break;
331 }
332 }
333
334 // Listener might throw a runtime exception.
335 if (transactionListener != null) {
336 try {
337 transactionListener.onBegin(); // might throw
338 } catch (RuntimeException ex) {
339 if (mTransactionStack == null) {
Jeff Brown4c1241d2012-02-02 17:05:00 -0800340 mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700341 }
342 throw ex;
343 }
344 }
345
346 // Bookkeeping can't throw, except an OOM, which is just too bad...
347 Transaction transaction = obtainTransaction(transactionMode, transactionListener);
348 transaction.mParent = mTransactionStack;
349 mTransactionStack = transaction;
350 } finally {
Jeff Browna9be4152012-01-18 15:29:57 -0800351 if (mTransactionStack == null) {
352 releaseConnection(); // might throw
353 }
Jeff Browne5360fb2011-10-31 17:48:13 -0700354 }
355 }
356
357 /**
358 * Marks the current transaction as having completed successfully.
359 * <p>
360 * This method can be called at most once between {@link #beginTransaction} and
361 * {@link #endTransaction} to indicate that the changes made by the transaction should be
362 * committed. If this method is not called, the changes will be rolled back
363 * when the transaction is ended.
364 * </p>
365 *
366 * @throws IllegalStateException if there is no current transaction, or if
367 * {@link #setTransactionSuccessful} has already been called for the current transaction.
368 *
369 * @see #beginTransaction
370 * @see #endTransaction
371 */
372 public void setTransactionSuccessful() {
373 throwIfNoTransaction();
374 throwIfTransactionMarkedSuccessful();
375
376 mTransactionStack.mMarkedSuccessful = true;
377 }
378
379 /**
380 * Ends the current transaction and commits or rolls back changes.
381 * <p>
382 * If this is the outermost transaction (not nested within any other
383 * transaction), then the changes are committed if {@link #setTransactionSuccessful}
384 * was called or rolled back otherwise.
385 * </p><p>
386 * This method must be called exactly once for each call to {@link #beginTransaction}.
387 * </p>
388 *
Jeff Brown4c1241d2012-02-02 17:05:00 -0800389 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800390 *
Jeff Browne5360fb2011-10-31 17:48:13 -0700391 * @throws IllegalStateException if there is no current transaction.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800392 * @throws SQLiteException if an error occurs.
393 * @throws OperationCanceledException if the operation was canceled.
Jeff Browne5360fb2011-10-31 17:48:13 -0700394 *
395 * @see #beginTransaction
396 * @see #setTransactionSuccessful
397 * @see #yieldTransaction
398 */
Jeff Brown4c1241d2012-02-02 17:05:00 -0800399 public void endTransaction(CancellationSignal cancellationSignal) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700400 throwIfNoTransaction();
401 assert mConnection != null;
402
Jeff Brownd19ff5f2012-03-27 18:12:23 -0700403 endTransactionUnchecked(cancellationSignal, false);
Jeff Browne5360fb2011-10-31 17:48:13 -0700404 }
405
Jeff Brownd19ff5f2012-03-27 18:12:23 -0700406 private void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) {
Jeff Brown4c1241d2012-02-02 17:05:00 -0800407 if (cancellationSignal != null) {
408 cancellationSignal.throwIfCanceled();
Jeff Brown75ea64f2012-01-25 19:37:13 -0800409 }
410
Jeff Browne5360fb2011-10-31 17:48:13 -0700411 final Transaction top = mTransactionStack;
Jeff Brownd19ff5f2012-03-27 18:12:23 -0700412 boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed;
Jeff Browne5360fb2011-10-31 17:48:13 -0700413
414 RuntimeException listenerException = null;
415 final SQLiteTransactionListener listener = top.mListener;
416 if (listener != null) {
417 try {
418 if (successful) {
419 listener.onCommit(); // might throw
420 } else {
421 listener.onRollback(); // might throw
422 }
423 } catch (RuntimeException ex) {
424 listenerException = ex;
425 successful = false;
426 }
427 }
428
429 mTransactionStack = top.mParent;
430 recycleTransaction(top);
431
432 if (mTransactionStack != null) {
433 if (!successful) {
434 mTransactionStack.mChildFailed = true;
435 }
436 } else {
437 try {
438 if (successful) {
Jeff Brown4c1241d2012-02-02 17:05:00 -0800439 mConnection.execute("COMMIT;", null, cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700440 } else {
Jeff Brown4c1241d2012-02-02 17:05:00 -0800441 mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700442 }
443 } finally {
Jeff Browna9be4152012-01-18 15:29:57 -0800444 releaseConnection(); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700445 }
446 }
447
448 if (listenerException != null) {
449 throw listenerException;
450 }
451 }
452
453 /**
454 * Temporarily ends a transaction to let other threads have use of
455 * the database. Begins a new transaction after a specified delay.
456 * <p>
457 * If there are other threads waiting to acquire connections,
458 * then the current transaction is committed and the database
459 * connection is released. After a short delay, a new transaction
460 * is started.
461 * </p><p>
462 * The transaction is assumed to be successful so far. Do not call
463 * {@link #setTransactionSuccessful()} before calling this method.
464 * This method will fail if the transaction has already been marked
465 * successful.
466 * </p><p>
467 * The changes that were committed by a yield cannot be rolled back later.
468 * </p><p>
469 * Before this method was called, there must already have been
470 * a transaction in progress. When this method returns, there will
471 * still be a transaction in progress, either the same one as before
472 * or a new one if the transaction was actually yielded.
473 * </p><p>
474 * This method should not be called when there is a nested transaction
475 * in progress because it is not possible to yield a nested transaction.
476 * If <code>throwIfNested</code> is true, then attempting to yield
477 * a nested transaction will throw {@link IllegalStateException}, otherwise
478 * the method will return <code>false</code> in that case.
479 * </p><p>
480 * If there is no nested transaction in progress but a previous nested
481 * transaction failed, then the transaction is not yielded (because it
482 * must be rolled back) and this method returns <code>false</code>.
483 * </p>
484 *
485 * @param sleepAfterYieldDelayMillis A delay time to wait after yielding
486 * the database connection to allow other threads some time to run.
487 * If the value is less than or equal to zero, there will be no additional
488 * delay beyond the time it will take to begin a new transaction.
489 * @param throwIfUnsafe If true, then instead of returning false when no
490 * transaction is in progress, a nested transaction is in progress, or when
491 * the transaction has already been marked successful, throws {@link IllegalStateException}.
Jeff Brown4c1241d2012-02-02 17:05:00 -0800492 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
Jeff Browne5360fb2011-10-31 17:48:13 -0700493 * @return True if the transaction was actually yielded.
494 *
495 * @throws IllegalStateException if <code>throwIfNested</code> is true and
496 * there is no current transaction, there is a nested transaction in progress or
497 * if {@link #setTransactionSuccessful} has already been called for the current transaction.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800498 * @throws SQLiteException if an error occurs.
499 * @throws OperationCanceledException if the operation was canceled.
Jeff Browne5360fb2011-10-31 17:48:13 -0700500 *
501 * @see #beginTransaction
502 * @see #endTransaction
503 */
Jeff Brown75ea64f2012-01-25 19:37:13 -0800504 public boolean yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800505 CancellationSignal cancellationSignal) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700506 if (throwIfUnsafe) {
507 throwIfNoTransaction();
508 throwIfTransactionMarkedSuccessful();
509 throwIfNestedTransaction();
510 } else {
511 if (mTransactionStack == null || mTransactionStack.mMarkedSuccessful
512 || mTransactionStack.mParent != null) {
513 return false;
514 }
515 }
516 assert mConnection != null;
517
518 if (mTransactionStack.mChildFailed) {
519 return false;
520 }
521
Jeff Brown75ea64f2012-01-25 19:37:13 -0800522 return yieldTransactionUnchecked(sleepAfterYieldDelayMillis,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800523 cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700524 }
525
Jeff Brown75ea64f2012-01-25 19:37:13 -0800526 private boolean yieldTransactionUnchecked(long sleepAfterYieldDelayMillis,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800527 CancellationSignal cancellationSignal) {
528 if (cancellationSignal != null) {
529 cancellationSignal.throwIfCanceled();
Jeff Brown75ea64f2012-01-25 19:37:13 -0800530 }
531
Jeff Browne5360fb2011-10-31 17:48:13 -0700532 if (!mConnectionPool.shouldYieldConnection(mConnection, mConnectionFlags)) {
533 return false;
534 }
535
536 final int transactionMode = mTransactionStack.mMode;
537 final SQLiteTransactionListener listener = mTransactionStack.mListener;
538 final int connectionFlags = mConnectionFlags;
Jeff Brownd19ff5f2012-03-27 18:12:23 -0700539 endTransactionUnchecked(cancellationSignal, true); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700540
541 if (sleepAfterYieldDelayMillis > 0) {
542 try {
543 Thread.sleep(sleepAfterYieldDelayMillis);
544 } catch (InterruptedException ex) {
545 // we have been interrupted, that's all we need to do
546 }
547 }
548
Jeff Brown75ea64f2012-01-25 19:37:13 -0800549 beginTransactionUnchecked(transactionMode, listener, connectionFlags,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800550 cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700551 return true;
552 }
553
554 /**
555 * Prepares a statement for execution but does not bind its parameters or execute it.
556 * <p>
557 * This method can be used to check for syntax errors during compilation
558 * prior to execution of the statement. If the {@code outStatementInfo} argument
559 * is not null, the provided {@link SQLiteStatementInfo} object is populated
560 * with information about the statement.
561 * </p><p>
562 * A prepared statement makes no reference to the arguments that may eventually
563 * be bound to it, consequently it it possible to cache certain prepared statements
564 * such as SELECT or INSERT/UPDATE statements. If the statement is cacheable,
565 * then it will be stored in the cache for later and reused if possible.
566 * </p>
567 *
568 * @param sql The SQL statement to prepare.
569 * @param connectionFlags The connection flags to use if a connection must be
570 * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
Jeff Brown4c1241d2012-02-02 17:05:00 -0800571 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
Jeff Browne5360fb2011-10-31 17:48:13 -0700572 * @param outStatementInfo The {@link SQLiteStatementInfo} object to populate
573 * with information about the statement, or null if none.
574 *
575 * @throws SQLiteException if an error occurs, such as a syntax error.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800576 * @throws OperationCanceledException if the operation was canceled.
Jeff Browne5360fb2011-10-31 17:48:13 -0700577 */
Jeff Brown4c1241d2012-02-02 17:05:00 -0800578 public void prepare(String sql, int connectionFlags, CancellationSignal cancellationSignal,
Jeff Brown75ea64f2012-01-25 19:37:13 -0800579 SQLiteStatementInfo outStatementInfo) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700580 if (sql == null) {
581 throw new IllegalArgumentException("sql must not be null.");
582 }
583
Jeff Brown4c1241d2012-02-02 17:05:00 -0800584 if (cancellationSignal != null) {
585 cancellationSignal.throwIfCanceled();
Jeff Brown75ea64f2012-01-25 19:37:13 -0800586 }
587
Jeff Brown4c1241d2012-02-02 17:05:00 -0800588 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700589 try {
590 mConnection.prepare(sql, outStatementInfo); // might throw
591 } finally {
Jeff Browna9be4152012-01-18 15:29:57 -0800592 releaseConnection(); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700593 }
594 }
595
596 /**
597 * Executes a statement that does not return a result.
598 *
599 * @param sql The SQL statement to execute.
600 * @param bindArgs The arguments to bind, or null if none.
601 * @param connectionFlags The connection flags to use if a connection must be
602 * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
Jeff Brown4c1241d2012-02-02 17:05:00 -0800603 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
Jeff Browne5360fb2011-10-31 17:48:13 -0700604 *
605 * @throws SQLiteException if an error occurs, such as a syntax error
606 * or invalid number of bind arguments.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800607 * @throws OperationCanceledException if the operation was canceled.
Jeff Browne5360fb2011-10-31 17:48:13 -0700608 */
Jeff Brown75ea64f2012-01-25 19:37:13 -0800609 public void execute(String sql, Object[] bindArgs, int connectionFlags,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800610 CancellationSignal cancellationSignal) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700611 if (sql == null) {
612 throw new IllegalArgumentException("sql must not be null.");
613 }
614
Jeff Brown4c1241d2012-02-02 17:05:00 -0800615 if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700616 return;
617 }
618
Jeff Brown4c1241d2012-02-02 17:05:00 -0800619 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700620 try {
Jeff Brown4c1241d2012-02-02 17:05:00 -0800621 mConnection.execute(sql, bindArgs, cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700622 } finally {
Jeff Browna9be4152012-01-18 15:29:57 -0800623 releaseConnection(); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700624 }
625 }
626
627 /**
628 * Executes a statement that returns a single <code>long</code> result.
629 *
630 * @param sql The SQL statement to execute.
631 * @param bindArgs The arguments to bind, or null if none.
632 * @param connectionFlags The connection flags to use if a connection must be
633 * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
Jeff Brown4c1241d2012-02-02 17:05:00 -0800634 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
Jeff Browne5360fb2011-10-31 17:48:13 -0700635 * @return The value of the first column in the first row of the result set
636 * as a <code>long</code>, or zero if none.
637 *
638 * @throws SQLiteException if an error occurs, such as a syntax error
639 * or invalid number of bind arguments.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800640 * @throws OperationCanceledException if the operation was canceled.
Jeff Browne5360fb2011-10-31 17:48:13 -0700641 */
Jeff Brown75ea64f2012-01-25 19:37:13 -0800642 public long executeForLong(String sql, Object[] bindArgs, int connectionFlags,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800643 CancellationSignal cancellationSignal) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700644 if (sql == null) {
645 throw new IllegalArgumentException("sql must not be null.");
646 }
647
Jeff Brown4c1241d2012-02-02 17:05:00 -0800648 if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700649 return 0;
650 }
651
Jeff Brown4c1241d2012-02-02 17:05:00 -0800652 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700653 try {
Jeff Brown4c1241d2012-02-02 17:05:00 -0800654 return mConnection.executeForLong(sql, bindArgs, cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700655 } finally {
Jeff Browna9be4152012-01-18 15:29:57 -0800656 releaseConnection(); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700657 }
658 }
659
660 /**
661 * Executes a statement that returns a single {@link String} result.
662 *
663 * @param sql The SQL statement to execute.
664 * @param bindArgs The arguments to bind, or null if none.
665 * @param connectionFlags The connection flags to use if a connection must be
666 * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
Jeff Brown4c1241d2012-02-02 17:05:00 -0800667 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
Jeff Browne5360fb2011-10-31 17:48:13 -0700668 * @return The value of the first column in the first row of the result set
669 * as a <code>String</code>, or null if none.
670 *
671 * @throws SQLiteException if an error occurs, such as a syntax error
672 * or invalid number of bind arguments.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800673 * @throws OperationCanceledException if the operation was canceled.
Jeff Browne5360fb2011-10-31 17:48:13 -0700674 */
Jeff Brown75ea64f2012-01-25 19:37:13 -0800675 public String executeForString(String sql, Object[] bindArgs, int connectionFlags,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800676 CancellationSignal cancellationSignal) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700677 if (sql == null) {
678 throw new IllegalArgumentException("sql must not be null.");
679 }
680
Jeff Brown4c1241d2012-02-02 17:05:00 -0800681 if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700682 return null;
683 }
684
Jeff Brown4c1241d2012-02-02 17:05:00 -0800685 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700686 try {
Jeff Brown4c1241d2012-02-02 17:05:00 -0800687 return mConnection.executeForString(sql, bindArgs, cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700688 } finally {
Jeff Browna9be4152012-01-18 15:29:57 -0800689 releaseConnection(); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700690 }
691 }
692
693 /**
694 * Executes a statement that returns a single BLOB result as a
695 * file descriptor to a shared memory region.
696 *
697 * @param sql The SQL statement to execute.
698 * @param bindArgs The arguments to bind, or null if none.
699 * @param connectionFlags The connection flags to use if a connection must be
700 * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
Jeff Brown4c1241d2012-02-02 17:05:00 -0800701 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
Jeff Browne5360fb2011-10-31 17:48:13 -0700702 * @return The file descriptor for a shared memory region that contains
703 * the value of the first column in the first row of the result set as a BLOB,
704 * or null if none.
705 *
706 * @throws SQLiteException if an error occurs, such as a syntax error
707 * or invalid number of bind arguments.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800708 * @throws OperationCanceledException if the operation was canceled.
Jeff Browne5360fb2011-10-31 17:48:13 -0700709 */
710 public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800711 int connectionFlags, CancellationSignal cancellationSignal) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700712 if (sql == null) {
713 throw new IllegalArgumentException("sql must not be null.");
714 }
715
Jeff Brown4c1241d2012-02-02 17:05:00 -0800716 if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700717 return null;
718 }
719
Jeff Brown4c1241d2012-02-02 17:05:00 -0800720 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700721 try {
Jeff Brown75ea64f2012-01-25 19:37:13 -0800722 return mConnection.executeForBlobFileDescriptor(sql, bindArgs,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800723 cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700724 } finally {
Jeff Browna9be4152012-01-18 15:29:57 -0800725 releaseConnection(); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700726 }
727 }
728
729 /**
730 * Executes a statement that returns a count of the number of rows
731 * that were changed. Use for UPDATE or DELETE SQL statements.
732 *
733 * @param sql The SQL statement to execute.
734 * @param bindArgs The arguments to bind, or null if none.
735 * @param connectionFlags The connection flags to use if a connection must be
736 * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
Jeff Brown4c1241d2012-02-02 17:05:00 -0800737 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
Jeff Browne5360fb2011-10-31 17:48:13 -0700738 * @return The number of rows that were changed.
739 *
740 * @throws SQLiteException if an error occurs, such as a syntax error
741 * or invalid number of bind arguments.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800742 * @throws OperationCanceledException if the operation was canceled.
Jeff Browne5360fb2011-10-31 17:48:13 -0700743 */
Jeff Brown75ea64f2012-01-25 19:37:13 -0800744 public int executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800745 CancellationSignal cancellationSignal) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700746 if (sql == null) {
747 throw new IllegalArgumentException("sql must not be null.");
748 }
749
Jeff Brown4c1241d2012-02-02 17:05:00 -0800750 if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700751 return 0;
752 }
753
Jeff Brown4c1241d2012-02-02 17:05:00 -0800754 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700755 try {
Jeff Brown75ea64f2012-01-25 19:37:13 -0800756 return mConnection.executeForChangedRowCount(sql, bindArgs,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800757 cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700758 } finally {
Jeff Browna9be4152012-01-18 15:29:57 -0800759 releaseConnection(); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700760 }
761 }
762
763 /**
764 * Executes a statement that returns the row id of the last row inserted
765 * by the statement. Use for INSERT SQL statements.
766 *
767 * @param sql The SQL statement to execute.
768 * @param bindArgs The arguments to bind, or null if none.
769 * @param connectionFlags The connection flags to use if a connection must be
770 * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
Jeff Brown4c1241d2012-02-02 17:05:00 -0800771 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
Jeff Browne5360fb2011-10-31 17:48:13 -0700772 * @return The row id of the last row that was inserted, or 0 if none.
773 *
774 * @throws SQLiteException if an error occurs, such as a syntax error
775 * or invalid number of bind arguments.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800776 * @throws OperationCanceledException if the operation was canceled.
Jeff Browne5360fb2011-10-31 17:48:13 -0700777 */
Jeff Brown75ea64f2012-01-25 19:37:13 -0800778 public long executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800779 CancellationSignal cancellationSignal) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700780 if (sql == null) {
781 throw new IllegalArgumentException("sql must not be null.");
782 }
783
Jeff Brown4c1241d2012-02-02 17:05:00 -0800784 if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700785 return 0;
786 }
787
Jeff Brown4c1241d2012-02-02 17:05:00 -0800788 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700789 try {
Jeff Brown75ea64f2012-01-25 19:37:13 -0800790 return mConnection.executeForLastInsertedRowId(sql, bindArgs,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800791 cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700792 } finally {
Jeff Browna9be4152012-01-18 15:29:57 -0800793 releaseConnection(); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700794 }
795 }
796
797 /**
798 * Executes a statement and populates the specified {@link CursorWindow}
799 * with a range of results. Returns the number of rows that were counted
800 * during query execution.
801 *
802 * @param sql The SQL statement to execute.
803 * @param bindArgs The arguments to bind, or null if none.
804 * @param window The cursor window to clear and fill.
805 * @param startPos The start position for filling the window.
806 * @param requiredPos The position of a row that MUST be in the window.
807 * If it won't fit, then the query should discard part of what it filled
808 * so that it does. Must be greater than or equal to <code>startPos</code>.
809 * @param countAllRows True to count all rows that the query would return
810 * regagless of whether they fit in the window.
811 * @param connectionFlags The connection flags to use if a connection must be
812 * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
Jeff Brown4c1241d2012-02-02 17:05:00 -0800813 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
Jeff Browne5360fb2011-10-31 17:48:13 -0700814 * @return The number of rows that were counted during query execution. Might
815 * not be all rows in the result set unless <code>countAllRows</code> is true.
816 *
817 * @throws SQLiteException if an error occurs, such as a syntax error
818 * or invalid number of bind arguments.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800819 * @throws OperationCanceledException if the operation was canceled.
Jeff Browne5360fb2011-10-31 17:48:13 -0700820 */
821 public int executeForCursorWindow(String sql, Object[] bindArgs,
822 CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800823 int connectionFlags, CancellationSignal cancellationSignal) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700824 if (sql == null) {
825 throw new IllegalArgumentException("sql must not be null.");
826 }
827 if (window == null) {
828 throw new IllegalArgumentException("window must not be null.");
829 }
830
Jeff Brown4c1241d2012-02-02 17:05:00 -0800831 if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700832 window.clear();
833 return 0;
834 }
835
Jeff Brown4c1241d2012-02-02 17:05:00 -0800836 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700837 try {
838 return mConnection.executeForCursorWindow(sql, bindArgs,
Jeff Brown75ea64f2012-01-25 19:37:13 -0800839 window, startPos, requiredPos, countAllRows,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800840 cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700841 } finally {
Jeff Browna9be4152012-01-18 15:29:57 -0800842 releaseConnection(); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700843 }
844 }
845
846 /**
847 * Performs special reinterpretation of certain SQL statements such as "BEGIN",
848 * "COMMIT" and "ROLLBACK" to ensure that transaction state invariants are
849 * maintained.
850 *
851 * This function is mainly used to support legacy apps that perform their
852 * own transactions by executing raw SQL rather than calling {@link #beginTransaction}
853 * and the like.
854 *
855 * @param sql The SQL statement to execute.
856 * @param bindArgs The arguments to bind, or null if none.
857 * @param connectionFlags The connection flags to use if a connection must be
858 * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
Jeff Brown4c1241d2012-02-02 17:05:00 -0800859 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
Jeff Browne5360fb2011-10-31 17:48:13 -0700860 * @return True if the statement was of a special form that was handled here,
861 * false otherwise.
862 *
863 * @throws SQLiteException if an error occurs, such as a syntax error
864 * or invalid number of bind arguments.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800865 * @throws OperationCanceledException if the operation was canceled.
Jeff Browne5360fb2011-10-31 17:48:13 -0700866 */
Jeff Brown75ea64f2012-01-25 19:37:13 -0800867 private boolean executeSpecial(String sql, Object[] bindArgs, int connectionFlags,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800868 CancellationSignal cancellationSignal) {
869 if (cancellationSignal != null) {
870 cancellationSignal.throwIfCanceled();
Jeff Brown75ea64f2012-01-25 19:37:13 -0800871 }
872
Jeff Browne5360fb2011-10-31 17:48:13 -0700873 final int type = DatabaseUtils.getSqlStatementType(sql);
874 switch (type) {
875 case DatabaseUtils.STATEMENT_BEGIN:
Jeff Brown75ea64f2012-01-25 19:37:13 -0800876 beginTransaction(TRANSACTION_MODE_EXCLUSIVE, null, connectionFlags,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800877 cancellationSignal);
Jeff Browne5360fb2011-10-31 17:48:13 -0700878 return true;
879
880 case DatabaseUtils.STATEMENT_COMMIT:
881 setTransactionSuccessful();
Jeff Brown4c1241d2012-02-02 17:05:00 -0800882 endTransaction(cancellationSignal);
Jeff Browne5360fb2011-10-31 17:48:13 -0700883 return true;
884
885 case DatabaseUtils.STATEMENT_ABORT:
Jeff Brown4c1241d2012-02-02 17:05:00 -0800886 endTransaction(cancellationSignal);
Jeff Browne5360fb2011-10-31 17:48:13 -0700887 return true;
888 }
889 return false;
890 }
891
Jeff Brown75ea64f2012-01-25 19:37:13 -0800892 private void acquireConnection(String sql, int connectionFlags,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800893 CancellationSignal cancellationSignal) {
Jeff Browna9be4152012-01-18 15:29:57 -0800894 if (mConnection == null) {
895 assert mConnectionUseCount == 0;
Jeff Brown75ea64f2012-01-25 19:37:13 -0800896 mConnection = mConnectionPool.acquireConnection(sql, connectionFlags,
Jeff Brown4c1241d2012-02-02 17:05:00 -0800897 cancellationSignal); // might throw
Jeff Browne5360fb2011-10-31 17:48:13 -0700898 mConnectionFlags = connectionFlags;
899 }
Jeff Browna9be4152012-01-18 15:29:57 -0800900 mConnectionUseCount += 1;
Jeff Browne5360fb2011-10-31 17:48:13 -0700901 }
902
Jeff Browna9be4152012-01-18 15:29:57 -0800903 private void releaseConnection() {
904 assert mConnection != null;
905 assert mConnectionUseCount > 0;
906 if (--mConnectionUseCount == 0) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700907 try {
908 mConnectionPool.releaseConnection(mConnection); // might throw
909 } finally {
910 mConnection = null;
911 }
912 }
913 }
914
915 private void throwIfNoTransaction() {
916 if (mTransactionStack == null) {
917 throw new IllegalStateException("Cannot perform this operation because "
918 + "there is no current transaction.");
919 }
920 }
921
922 private void throwIfTransactionMarkedSuccessful() {
923 if (mTransactionStack != null && mTransactionStack.mMarkedSuccessful) {
924 throw new IllegalStateException("Cannot perform this operation because "
925 + "the transaction has already been marked successful. The only "
926 + "thing you can do now is call endTransaction().");
927 }
928 }
929
930 private void throwIfNestedTransaction() {
László Dávid0b6cc9a2012-10-25 00:00:18 +0200931 if (hasNestedTransaction()) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700932 throw new IllegalStateException("Cannot perform this operation because "
933 + "a nested transaction is in progress.");
934 }
935 }
936
937 private Transaction obtainTransaction(int mode, SQLiteTransactionListener listener) {
938 Transaction transaction = mTransactionPool;
939 if (transaction != null) {
940 mTransactionPool = transaction.mParent;
941 transaction.mParent = null;
942 transaction.mMarkedSuccessful = false;
943 transaction.mChildFailed = false;
944 } else {
945 transaction = new Transaction();
946 }
947 transaction.mMode = mode;
948 transaction.mListener = listener;
949 return transaction;
950 }
951
952 private void recycleTransaction(Transaction transaction) {
953 transaction.mParent = mTransactionPool;
954 transaction.mListener = null;
955 mTransactionPool = transaction;
956 }
957
958 private static final class Transaction {
959 public Transaction mParent;
960 public int mMode;
961 public SQLiteTransactionListener mListener;
962 public boolean mMarkedSuccessful;
963 public boolean mChildFailed;
964 }
965}