• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.database.sqlite;
18 
19 import android.annotation.UnsupportedAppUsage;
20 import android.database.CursorWindow;
21 import android.database.DatabaseUtils;
22 import android.os.CancellationSignal;
23 import android.os.OperationCanceledException;
24 import 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  *
156  * <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  *
162  * @hide
163  */
164 public final class SQLiteSession {
165     private final SQLiteConnectionPool mConnectionPool;
166 
167     private SQLiteConnection mConnection;
168     private int mConnectionFlags;
169     private int mConnectionUseCount;
170     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      */
SQLiteSession(SQLiteConnectionPool connectionPool)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      */
hasTransaction()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      */
hasNestedTransaction()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      */
hasConnection()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}.
284      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
285      *
286      * @throws IllegalStateException if {@link #setTransactionSuccessful} has already been
287      * called for the current transaction.
288      * @throws SQLiteException if an error occurs.
289      * @throws OperationCanceledException if the operation was canceled.
290      *
291      * @see #setTransactionSuccessful
292      * @see #yieldTransaction
293      * @see #endTransaction
294      */
295     @UnsupportedAppUsage
beginTransaction(int transactionMode, SQLiteTransactionListener transactionListener, int connectionFlags, CancellationSignal cancellationSignal)296     public void beginTransaction(int transactionMode,
297             SQLiteTransactionListener transactionListener, int connectionFlags,
298             CancellationSignal cancellationSignal) {
299         throwIfTransactionMarkedSuccessful();
300         beginTransactionUnchecked(transactionMode, transactionListener, connectionFlags,
301                 cancellationSignal);
302     }
303 
beginTransactionUnchecked(int transactionMode, SQLiteTransactionListener transactionListener, int connectionFlags, CancellationSignal cancellationSignal)304     private void beginTransactionUnchecked(int transactionMode,
305             SQLiteTransactionListener transactionListener, int connectionFlags,
306             CancellationSignal cancellationSignal) {
307         if (cancellationSignal != null) {
308             cancellationSignal.throwIfCanceled();
309         }
310 
311         if (mTransactionStack == null) {
312             acquireConnection(null, connectionFlags, cancellationSignal); // might throw
313         }
314         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:
321                         mConnection.execute("BEGIN IMMEDIATE;", null,
322                                 cancellationSignal); // might throw
323                         break;
324                     case TRANSACTION_MODE_EXCLUSIVE:
325                         mConnection.execute("BEGIN EXCLUSIVE;", null,
326                                 cancellationSignal); // might throw
327                         break;
328                     default:
329                         mConnection.execute("BEGIN;", null, cancellationSignal); // might throw
330                         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) {
340                         mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
341                     }
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 {
351             if (mTransactionStack == null) {
352                 releaseConnection(); // might throw
353             }
354         }
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      */
setTransactionSuccessful()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      *
389      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
390      *
391      * @throws IllegalStateException if there is no current transaction.
392      * @throws SQLiteException if an error occurs.
393      * @throws OperationCanceledException if the operation was canceled.
394      *
395      * @see #beginTransaction
396      * @see #setTransactionSuccessful
397      * @see #yieldTransaction
398      */
endTransaction(CancellationSignal cancellationSignal)399     public void endTransaction(CancellationSignal cancellationSignal) {
400         throwIfNoTransaction();
401         assert mConnection != null;
402 
403         endTransactionUnchecked(cancellationSignal, false);
404     }
405 
endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding)406     private void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) {
407         if (cancellationSignal != null) {
408             cancellationSignal.throwIfCanceled();
409         }
410 
411         final Transaction top = mTransactionStack;
412         boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed;
413 
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) {
439                     mConnection.execute("COMMIT;", null, cancellationSignal); // might throw
440                 } else {
441                     mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
442                 }
443             } finally {
444                 releaseConnection(); // might throw
445             }
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}.
492      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
493      * @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.
498      * @throws SQLiteException if an error occurs.
499      * @throws OperationCanceledException if the operation was canceled.
500      *
501      * @see #beginTransaction
502      * @see #endTransaction
503      */
yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe, CancellationSignal cancellationSignal)504     public boolean yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe,
505             CancellationSignal cancellationSignal) {
506         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 
522         return yieldTransactionUnchecked(sleepAfterYieldDelayMillis,
523                 cancellationSignal); // might throw
524     }
525 
yieldTransactionUnchecked(long sleepAfterYieldDelayMillis, CancellationSignal cancellationSignal)526     private boolean yieldTransactionUnchecked(long sleepAfterYieldDelayMillis,
527             CancellationSignal cancellationSignal) {
528         if (cancellationSignal != null) {
529             cancellationSignal.throwIfCanceled();
530         }
531 
532         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;
539         endTransactionUnchecked(cancellationSignal, true); // might throw
540 
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 
549         beginTransactionUnchecked(transactionMode, listener, connectionFlags,
550                 cancellationSignal); // might throw
551         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}.
571      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
572      * @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.
576      * @throws OperationCanceledException if the operation was canceled.
577      */
prepare(String sql, int connectionFlags, CancellationSignal cancellationSignal, SQLiteStatementInfo outStatementInfo)578     public void prepare(String sql, int connectionFlags, CancellationSignal cancellationSignal,
579             SQLiteStatementInfo outStatementInfo) {
580         if (sql == null) {
581             throw new IllegalArgumentException("sql must not be null.");
582         }
583 
584         if (cancellationSignal != null) {
585             cancellationSignal.throwIfCanceled();
586         }
587 
588         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
589         try {
590             mConnection.prepare(sql, outStatementInfo); // might throw
591         } finally {
592             releaseConnection(); // might throw
593         }
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}.
603      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
604      *
605      * @throws SQLiteException if an error occurs, such as a syntax error
606      * or invalid number of bind arguments.
607      * @throws OperationCanceledException if the operation was canceled.
608      */
execute(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)609     public void execute(String sql, Object[] bindArgs, int connectionFlags,
610             CancellationSignal cancellationSignal) {
611         if (sql == null) {
612             throw new IllegalArgumentException("sql must not be null.");
613         }
614 
615         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
616             return;
617         }
618 
619         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
620         try {
621             mConnection.execute(sql, bindArgs, cancellationSignal); // might throw
622         } finally {
623             releaseConnection(); // might throw
624         }
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}.
634      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
635      * @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.
640      * @throws OperationCanceledException if the operation was canceled.
641      */
executeForLong(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)642     public long executeForLong(String sql, Object[] bindArgs, int connectionFlags,
643             CancellationSignal cancellationSignal) {
644         if (sql == null) {
645             throw new IllegalArgumentException("sql must not be null.");
646         }
647 
648         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
649             return 0;
650         }
651 
652         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
653         try {
654             return mConnection.executeForLong(sql, bindArgs, cancellationSignal); // might throw
655         } finally {
656             releaseConnection(); // might throw
657         }
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}.
667      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
668      * @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.
673      * @throws OperationCanceledException if the operation was canceled.
674      */
executeForString(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)675     public String executeForString(String sql, Object[] bindArgs, int connectionFlags,
676             CancellationSignal cancellationSignal) {
677         if (sql == null) {
678             throw new IllegalArgumentException("sql must not be null.");
679         }
680 
681         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
682             return null;
683         }
684 
685         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
686         try {
687             return mConnection.executeForString(sql, bindArgs, cancellationSignal); // might throw
688         } finally {
689             releaseConnection(); // might throw
690         }
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}.
701      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
702      * @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.
708      * @throws OperationCanceledException if the operation was canceled.
709      */
executeForBlobFileDescriptor(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)710     public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs,
711             int connectionFlags, CancellationSignal cancellationSignal) {
712         if (sql == null) {
713             throw new IllegalArgumentException("sql must not be null.");
714         }
715 
716         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
717             return null;
718         }
719 
720         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
721         try {
722             return mConnection.executeForBlobFileDescriptor(sql, bindArgs,
723                     cancellationSignal); // might throw
724         } finally {
725             releaseConnection(); // might throw
726         }
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}.
737      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
738      * @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.
742      * @throws OperationCanceledException if the operation was canceled.
743      */
executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)744     public int executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags,
745             CancellationSignal cancellationSignal) {
746         if (sql == null) {
747             throw new IllegalArgumentException("sql must not be null.");
748         }
749 
750         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
751             return 0;
752         }
753 
754         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
755         try {
756             return mConnection.executeForChangedRowCount(sql, bindArgs,
757                     cancellationSignal); // might throw
758         } finally {
759             releaseConnection(); // might throw
760         }
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}.
771      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
772      * @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.
776      * @throws OperationCanceledException if the operation was canceled.
777      */
executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)778     public long executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags,
779             CancellationSignal cancellationSignal) {
780         if (sql == null) {
781             throw new IllegalArgumentException("sql must not be null.");
782         }
783 
784         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
785             return 0;
786         }
787 
788         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
789         try {
790             return mConnection.executeForLastInsertedRowId(sql, bindArgs,
791                     cancellationSignal); // might throw
792         } finally {
793             releaseConnection(); // might throw
794         }
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}.
813      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
814      * @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.
819      * @throws OperationCanceledException if the operation was canceled.
820      */
executeForCursorWindow(String sql, Object[] bindArgs, CursorWindow window, int startPos, int requiredPos, boolean countAllRows, int connectionFlags, CancellationSignal cancellationSignal)821     public int executeForCursorWindow(String sql, Object[] bindArgs,
822             CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
823             int connectionFlags, CancellationSignal cancellationSignal) {
824         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 
831         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
832             window.clear();
833             return 0;
834         }
835 
836         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
837         try {
838             return mConnection.executeForCursorWindow(sql, bindArgs,
839                     window, startPos, requiredPos, countAllRows,
840                     cancellationSignal); // might throw
841         } finally {
842             releaseConnection(); // might throw
843         }
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}.
859      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
860      * @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.
865      * @throws OperationCanceledException if the operation was canceled.
866      */
executeSpecial(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)867     private boolean executeSpecial(String sql, Object[] bindArgs, int connectionFlags,
868             CancellationSignal cancellationSignal) {
869         if (cancellationSignal != null) {
870             cancellationSignal.throwIfCanceled();
871         }
872 
873         final int type = DatabaseUtils.getSqlStatementType(sql);
874         switch (type) {
875             case DatabaseUtils.STATEMENT_BEGIN:
876                 beginTransaction(TRANSACTION_MODE_EXCLUSIVE, null, connectionFlags,
877                         cancellationSignal);
878                 return true;
879 
880             case DatabaseUtils.STATEMENT_COMMIT:
881                 setTransactionSuccessful();
882                 endTransaction(cancellationSignal);
883                 return true;
884 
885             case DatabaseUtils.STATEMENT_ABORT:
886                 endTransaction(cancellationSignal);
887                 return true;
888         }
889         return false;
890     }
891 
acquireConnection(String sql, int connectionFlags, CancellationSignal cancellationSignal)892     private void acquireConnection(String sql, int connectionFlags,
893             CancellationSignal cancellationSignal) {
894         if (mConnection == null) {
895             assert mConnectionUseCount == 0;
896             mConnection = mConnectionPool.acquireConnection(sql, connectionFlags,
897                     cancellationSignal); // might throw
898             mConnectionFlags = connectionFlags;
899         }
900         mConnectionUseCount += 1;
901     }
902 
releaseConnection()903     private void releaseConnection() {
904         assert mConnection != null;
905         assert mConnectionUseCount > 0;
906         if (--mConnectionUseCount == 0) {
907             try {
908                 mConnectionPool.releaseConnection(mConnection); // might throw
909             } finally {
910                 mConnection = null;
911             }
912         }
913     }
914 
throwIfNoTransaction()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 
throwIfTransactionMarkedSuccessful()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 
throwIfNestedTransaction()930     private void throwIfNestedTransaction() {
931         if (hasNestedTransaction()) {
932             throw new IllegalStateException("Cannot perform this operation because "
933                     + "a nested transaction is in progress.");
934         }
935     }
936 
obtainTransaction(int mode, SQLiteTransactionListener listener)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 
recycleTransaction(Transaction transaction)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 }
966