• 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.NonNull;
20 
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.database.CursorWindow;
23 import android.database.DatabaseUtils;
24 import android.os.CancellationSignal;
25 import android.os.OperationCanceledException;
26 import android.os.ParcelFileDescriptor;
27 
28 import java.io.Closeable;
29 import java.io.IOException;
30 import java.util.ArrayDeque;
31 
32 /**
33  * Provides a single client the ability to use a database.
34  *
35  * <h2>About database sessions</h2>
36  * <p>
37  * Database access is always performed using a session.  The session
38  * manages the lifecycle of transactions and database connections.
39  * </p><p>
40  * Sessions can be used to perform both read-only and read-write operations.
41  * There is some advantage to knowing when a session is being used for
42  * read-only purposes because the connection pool can optimize the use
43  * of the available connections to permit multiple read-only operations
44  * to execute in parallel whereas read-write operations may need to be serialized.
45  * </p><p>
46  * When <em>Write Ahead Logging (WAL)</em> is enabled, the database can
47  * execute simultaneous read-only and read-write transactions, provided that
48  * at most one read-write transaction is performed at a time.  When WAL is not
49  * enabled, read-only transactions can execute in parallel but read-write
50  * transactions are mutually exclusive.
51  * </p>
52  *
53  * <h2>Ownership and concurrency guarantees</h2>
54  * <p>
55  * Session objects are not thread-safe.  In fact, session objects are thread-bound.
56  * The {@link SQLiteDatabase} uses a thread-local variable to associate a session
57  * with each thread for the use of that thread alone.  Consequently, each thread
58  * has its own session object and therefore its own transaction state independent
59  * of other threads.
60  * </p><p>
61  * A thread has at most one session per database.  This constraint ensures that
62  * a thread can never use more than one database connection at a time for a
63  * given database.  As the number of available database connections is limited,
64  * if a single thread tried to acquire multiple connections for the same database
65  * at the same time, it might deadlock.  Therefore we allow there to be only
66  * one session (so, at most one connection) per thread per database.
67  * </p>
68  *
69  * <h2>Transactions</h2>
70  * <p>
71  * There are two kinds of transaction: implicit transactions and explicit
72  * transactions.
73  * </p><p>
74  * An implicit transaction is created whenever a database operation is requested
75  * and there is no explicit transaction currently in progress.  An implicit transaction
76  * only lasts for the duration of the database operation in question and then it
77  * is ended.  If the database operation was successful, then its changes are committed.
78  * </p><p>
79  * An explicit transaction is started by calling {@link #beginTransaction} and
80  * specifying the desired transaction mode.  Once an explicit transaction has begun,
81  * all subsequent database operations will be performed as part of that transaction.
82  * To end an explicit transaction, first call {@link #setTransactionSuccessful} if the
83  * transaction was successful, then call {@link #end}.  If the transaction was
84  * marked successful, its changes will be committed, otherwise they will be rolled back.
85  * </p><p>
86  * Explicit transactions can also be nested.  A nested explicit transaction is
87  * started with {@link #beginTransaction}, marked successful with
88  * {@link #setTransactionSuccessful}and ended with {@link #endTransaction}.
89  * If any nested transaction is not marked successful, then the entire transaction
90  * including all of its nested transactions will be rolled back
91  * when the outermost transaction is ended.
92  * </p><p>
93  * To improve concurrency, an explicit transaction can be yielded by calling
94  * {@link #yieldTransaction}.  If there is contention for use of the database,
95  * then yielding ends the current transaction, commits its changes, releases the
96  * database connection for use by another session for a little while, and starts a
97  * new transaction with the same properties as the original one.
98  * Changes committed by {@link #yieldTransaction} cannot be rolled back.
99  * </p><p>
100  * When a transaction is started, the client can provide a {@link SQLiteTransactionListener}
101  * to listen for notifications of transaction-related events.
102  * </p><p>
103  * Recommended usage:
104  * <code><pre>
105  * // First, begin the transaction.
106  * session.beginTransaction(SQLiteSession.TRANSACTION_MODE_DEFERRED, 0);
107  * try {
108  *     // Then do stuff...
109  *     session.execute("INSERT INTO ...", null, 0);
110  *
111  *     // As the very last step before ending the transaction, mark it successful.
112  *     session.setTransactionSuccessful();
113  * } finally {
114  *     // Finally, end the transaction.
115  *     // This statement will commit the transaction if it was marked successful or
116  *     // roll it back otherwise.
117  *     session.endTransaction();
118  * }
119  * </pre></code>
120  * </p>
121  *
122  * <h2>Database connections</h2>
123  * <p>
124  * A {@link SQLiteDatabase} can have multiple active sessions at the same
125  * time.  Each session acquires and releases connections to the database
126  * as needed to perform each requested database transaction.  If all connections
127  * are in use, then database transactions on some sessions will block until a
128  * connection becomes available.
129  * </p><p>
130  * The session acquires a single database connection only for the duration
131  * of a single (implicit or explicit) database transaction, then releases it.
132  * This characteristic allows a small pool of database connections to be shared
133  * efficiently by multiple sessions as long as they are not all trying to perform
134  * database transactions at the same time.
135  * </p>
136  *
137  * <h2>Responsiveness</h2>
138  * <p>
139  * Because there are a limited number of database connections and the session holds
140  * a database connection for the entire duration of a database transaction,
141  * it is important to keep transactions short.  This is especially important
142  * for read-write transactions since they may block other transactions
143  * from executing.  Consider calling {@link #yieldTransaction} periodically
144  * during long-running transactions.
145  * </p><p>
146  * Another important consideration is that transactions that take too long to
147  * run may cause the application UI to become unresponsive.  Even if the transaction
148  * is executed in a background thread, the user will get bored and
149  * frustrated if the application shows no data for several seconds while
150  * a transaction runs.
151  * </p><p>
152  * Guidelines:
153  * <ul>
154  * <li>Do not perform database transactions on the UI thread.</li>
155  * <li>Keep database transactions as short as possible.</li>
156  * <li>Simple queries often run faster than complex queries.</li>
157  * <li>Measure the performance of your database transactions.</li>
158  * <li>Consider what will happen when the size of the data set grows.
159  * A query that works well on 100 rows may struggle with 10,000.</li>
160  * </ul>
161  *
162  * <h2>Reentrance</h2>
163  * <p>
164  * This class must tolerate reentrant execution of SQLite operations because
165  * triggers may call custom SQLite functions that perform additional queries.
166  * </p>
167  *
168  * @hide
169  */
170 public final class SQLiteSession {
171     private final SQLiteConnectionPool mConnectionPool;
172 
173     private SQLiteConnection mConnection;
174     private int mConnectionFlags;
175     private int mConnectionUseCount;
176     private Transaction mTransactionPool;
177     private Transaction mTransactionStack;
178 
179     /**
180      * A list of dependents that should be closed when the transaction completes.
181      */
182     private final ArrayDeque<Closeable> mOpenDependents = new ArrayDeque<>();
183 
184     /**
185      * Transaction mode: Deferred.
186      * <p>
187      * In a deferred transaction, no locks are acquired on the database
188      * until the first operation is performed.  If the first operation is
189      * read-only, then a <code>SHARED</code> lock is acquired, otherwise
190      * a <code>RESERVED</code> lock is acquired.
191      * </p><p>
192      * While holding a <code>SHARED</code> lock, this session is only allowed to
193      * read but other sessions are allowed to read or write.
194      * While holding a <code>RESERVED</code> lock, this session is allowed to read
195      * or write but other sessions are only allowed to read.
196      * </p><p>
197      * Because the lock is only acquired when needed in a deferred transaction,
198      * it is possible for another session to write to the database first before
199      * this session has a chance to do anything.
200      * </p><p>
201      * Corresponds to the SQLite <code>BEGIN DEFERRED</code> transaction mode.
202      * </p>
203      */
204     public static final int TRANSACTION_MODE_DEFERRED = 0;
205 
206     /**
207      * Transaction mode: Immediate.
208      * <p>
209      * When an immediate transaction begins, the session acquires a
210      * <code>RESERVED</code> lock.
211      * </p><p>
212      * While holding a <code>RESERVED</code> lock, this session is allowed to read
213      * or write but other sessions are only allowed to read.
214      * </p><p>
215      * Corresponds to the SQLite <code>BEGIN IMMEDIATE</code> transaction mode.
216      * </p>
217      */
218     public static final int TRANSACTION_MODE_IMMEDIATE = 1;
219 
220     /**
221      * Transaction mode: Exclusive.
222      * <p>
223      * When an exclusive transaction begins, the session acquires an
224      * <code>EXCLUSIVE</code> lock.
225      * </p><p>
226      * While holding an <code>EXCLUSIVE</code> lock, this session is allowed to read
227      * or write but no other sessions are allowed to access the database.
228      * </p><p>
229      * Corresponds to the SQLite <code>BEGIN EXCLUSIVE</code> transaction mode.
230      * </p>
231      */
232     public static final int TRANSACTION_MODE_EXCLUSIVE = 2;
233 
234     /**
235      * Creates a session bound to the specified connection pool.
236      *
237      * @param connectionPool The connection pool.
238      */
SQLiteSession(SQLiteConnectionPool connectionPool)239     public SQLiteSession(SQLiteConnectionPool connectionPool) {
240         if (connectionPool == null) {
241             throw new IllegalArgumentException("connectionPool must not be null");
242         }
243 
244         mConnectionPool = connectionPool;
245     }
246 
247     /**
248      * Returns true if the session has a transaction in progress.
249      *
250      * @return True if the session has a transaction in progress.
251      */
hasTransaction()252     public boolean hasTransaction() {
253         return mTransactionStack != null;
254     }
255 
256     /**
257      * Returns true if the session has a nested transaction in progress.
258      *
259      * @return True if the session has a nested transaction in progress.
260      */
hasNestedTransaction()261     public boolean hasNestedTransaction() {
262         return mTransactionStack != null && mTransactionStack.mParent != null;
263     }
264 
265     /**
266      * Returns true if the session has an active database connection.
267      *
268      * @return True if the session has an active database connection.
269      */
hasConnection()270     public boolean hasConnection() {
271         return mConnection != null;
272     }
273 
274     /**
275      * Begins a transaction.
276      * <p>
277      * Transactions may nest.  If the transaction is not in progress,
278      * then a database connection is obtained and a new transaction is started.
279      * Otherwise, a nested transaction is started.
280      * </p><p>
281      * Each call to {@link #beginTransaction} must be matched exactly by a call
282      * to {@link #endTransaction}.  To mark a transaction as successful,
283      * call {@link #setTransactionSuccessful} before calling {@link #endTransaction}.
284      * If the transaction is not successful, or if any of its nested
285      * transactions were not successful, then the entire transaction will
286      * be rolled back when the outermost transaction is ended.
287      * </p>
288      *
289      * @param transactionMode The transaction mode.  One of: {@link #TRANSACTION_MODE_DEFERRED},
290      * {@link #TRANSACTION_MODE_IMMEDIATE}, or {@link #TRANSACTION_MODE_EXCLUSIVE}.
291      * Ignored when creating a nested transaction.
292      * @param transactionListener The transaction listener, or null if none.
293      * @param connectionFlags The connection flags to use if a connection must be
294      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
295      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
296      *
297      * @throws IllegalStateException if {@link #setTransactionSuccessful} has already been
298      * called for the current transaction.
299      * @throws SQLiteException if an error occurs.
300      * @throws OperationCanceledException if the operation was canceled.
301      *
302      * @see #setTransactionSuccessful
303      * @see #yieldTransaction
304      * @see #endTransaction
305      */
306     @UnsupportedAppUsage
beginTransaction(int transactionMode, SQLiteTransactionListener transactionListener, int connectionFlags, CancellationSignal cancellationSignal)307     public void beginTransaction(int transactionMode,
308             SQLiteTransactionListener transactionListener, int connectionFlags,
309             CancellationSignal cancellationSignal) {
310         throwIfTransactionMarkedSuccessful();
311         beginTransactionUnchecked(transactionMode, transactionListener, connectionFlags,
312                 cancellationSignal);
313     }
314 
modeString(int transactionMode)315     private String modeString(int transactionMode) {
316         switch (transactionMode) {
317             case TRANSACTION_MODE_IMMEDIATE: return "TRANSACTION-IMMEDIATE";
318             case TRANSACTION_MODE_EXCLUSIVE: return "TRANSACTION-EXCLUSIVE";
319             case TRANSACTION_MODE_DEFERRED:  return "TRANSACTION-DEFERRED";
320             default: return "TRANSACTION";
321         }
322     }
323 
beginTransactionUnchecked(int transactionMode, SQLiteTransactionListener transactionListener, int connectionFlags, CancellationSignal cancellationSignal)324     private void beginTransactionUnchecked(int transactionMode,
325             SQLiteTransactionListener transactionListener, int connectionFlags,
326             CancellationSignal cancellationSignal) {
327         if (cancellationSignal != null) {
328             cancellationSignal.throwIfCanceled();
329         }
330 
331         if (mTransactionStack == null) {
332             acquireConnection(null, connectionFlags, cancellationSignal); // might throw
333             mConnection.recordBeginTransaction(modeString(transactionMode));
334         }
335         try {
336             // Set up the transaction such that we can back out safely
337             // in case we fail part way.
338             if (mTransactionStack == null) {
339                 // Execute SQL might throw a runtime exception.
340                 switch (transactionMode) {
341                     case TRANSACTION_MODE_IMMEDIATE:
342                         mConnection.execute("BEGIN IMMEDIATE;", null,
343                                 cancellationSignal); // might throw
344                         break;
345                     case TRANSACTION_MODE_EXCLUSIVE:
346                         mConnection.execute("BEGIN EXCLUSIVE;", null,
347                                 cancellationSignal); // might throw
348                         break;
349                     case TRANSACTION_MODE_DEFERRED:
350                         mConnection.execute("BEGIN DEFERRED;", null,
351                                 cancellationSignal); // might throw
352                         break;
353                     default:
354                         // Per SQLite documentation, this executes in DEFERRED mode.
355                         mConnection.execute("BEGIN;", null, cancellationSignal); // might throw
356                         break;
357                 }
358             }
359 
360             // Listener might throw a runtime exception.
361             if (transactionListener != null) {
362                 try {
363                     transactionListener.onBegin(); // might throw
364                 } catch (RuntimeException ex) {
365                     if (mTransactionStack == null) {
366                         mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
367                     }
368                     throw ex;
369                 }
370             }
371 
372             // Bookkeeping can't throw, except an OOM, which is just too bad...
373             Transaction transaction = obtainTransaction(transactionMode, transactionListener);
374             transaction.mParent = mTransactionStack;
375             mTransactionStack = transaction;
376         } finally {
377             if (mTransactionStack == null) {
378                 releaseConnection(); // might throw
379             }
380         }
381     }
382 
383     /**
384      * Marks the current transaction as having completed successfully.
385      * <p>
386      * This method can be called at most once between {@link #beginTransaction} and
387      * {@link #endTransaction} to indicate that the changes made by the transaction should be
388      * committed.  If this method is not called, the changes will be rolled back
389      * when the transaction is ended.
390      * </p>
391      *
392      * @throws IllegalStateException if there is no current transaction, or if
393      * {@link #setTransactionSuccessful} has already been called for the current transaction.
394      *
395      * @see #beginTransaction
396      * @see #endTransaction
397      */
setTransactionSuccessful()398     public void setTransactionSuccessful() {
399         throwIfNoTransaction();
400         throwIfTransactionMarkedSuccessful();
401 
402         mTransactionStack.mMarkedSuccessful = true;
403         // Close open dependents, since the next thing that is supposed to happen is the transaction
404         // ends.
405         closeOpenDependents();
406     }
407 
408     /**
409      * Ends the current transaction and commits or rolls back changes.
410      * <p>
411      * If this is the outermost transaction (not nested within any other
412      * transaction), then the changes are committed if {@link #setTransactionSuccessful}
413      * was called or rolled back otherwise.
414      * </p><p>
415      * This method must be called exactly once for each call to {@link #beginTransaction}.
416      * </p>
417      *
418      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
419      *
420      * @throws IllegalStateException if there is no current transaction.
421      * @throws SQLiteException if an error occurs.
422      * @throws OperationCanceledException if the operation was canceled.
423      *
424      * @see #beginTransaction
425      * @see #setTransactionSuccessful
426      * @see #yieldTransaction
427      */
endTransaction(CancellationSignal cancellationSignal)428     public void endTransaction(CancellationSignal cancellationSignal) {
429         throwIfNoTransaction();
430         assert mConnection != null;
431 
432         endTransactionUnchecked(cancellationSignal, false);
433     }
434 
endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding)435     private void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) {
436         if (cancellationSignal != null) {
437             cancellationSignal.throwIfCanceled();
438         }
439 
440         final Transaction top = mTransactionStack;
441         boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed;
442 
443         RuntimeException listenerException = null;
444         final SQLiteTransactionListener listener = top.mListener;
445         if (listener != null) {
446             try {
447                 if (successful) {
448                     listener.onCommit(); // might throw
449                 } else {
450                     listener.onRollback(); // might throw
451                 }
452             } catch (RuntimeException ex) {
453                 listenerException = ex;
454                 successful = false;
455             }
456         }
457 
458         mTransactionStack = top.mParent;
459         recycleTransaction(top);
460 
461         if (mTransactionStack != null) {
462             if (!successful) {
463                 mTransactionStack.mChildFailed = true;
464             }
465         } else {
466             // Close all dependents before anything that might throw.  The list should have been
467             // cleared when the transaction was marked successful or unsuccessful.  The call here
468             // does nothing if the list is empty but is provided for insurance.
469             closeOpenDependents();
470 
471             try {
472                 if (successful) {
473                     mConnection.execute("COMMIT;", null, cancellationSignal); // might throw
474                 } else {
475                     mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
476                 }
477             } finally {
478                 mConnection.recordEndTransaction(successful);
479                 releaseConnection(); // might throw
480             }
481         }
482 
483         if (listenerException != null) {
484             throw listenerException;
485         }
486     }
487 
488     /**
489      * Temporarily ends a transaction to let other threads have use of
490      * the database.  Begins a new transaction after a specified delay.
491      * <p>
492      * If there are other threads waiting to acquire connections,
493      * then the current transaction is committed and the database
494      * connection is released.  After a short delay, a new transaction
495      * is started.
496      * </p><p>
497      * The transaction is assumed to be successful so far.  Do not call
498      * {@link #setTransactionSuccessful()} before calling this method.
499      * This method will fail if the transaction has already been marked
500      * successful.
501      * </p><p>
502      * The changes that were committed by a yield cannot be rolled back later.
503      * </p><p>
504      * Before this method was called, there must already have been
505      * a transaction in progress.  When this method returns, there will
506      * still be a transaction in progress, either the same one as before
507      * or a new one if the transaction was actually yielded.
508      * </p><p>
509      * This method should not be called when there is a nested transaction
510      * in progress because it is not possible to yield a nested transaction.
511      * If <code>throwIfNested</code> is true, then attempting to yield
512      * a nested transaction will throw {@link IllegalStateException}, otherwise
513      * the method will return <code>false</code> in that case.
514      * </p><p>
515      * If there is no nested transaction in progress but a previous nested
516      * transaction failed, then the transaction is not yielded (because it
517      * must be rolled back) and this method returns <code>false</code>.
518      * </p>
519      *
520      * @param sleepAfterYieldDelayMillis A delay time to wait after yielding
521      * the database connection to allow other threads some time to run.
522      * If the value is less than or equal to zero, there will be no additional
523      * delay beyond the time it will take to begin a new transaction.
524      * @param throwIfUnsafe If true, then instead of returning false when no
525      * transaction is in progress, a nested transaction is in progress, or when
526      * the transaction has already been marked successful, throws {@link IllegalStateException}.
527      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
528      * @return True if the transaction was actually yielded.
529      *
530      * @throws IllegalStateException if <code>throwIfNested</code> is true and
531      * there is no current transaction, there is a nested transaction in progress or
532      * if {@link #setTransactionSuccessful} has already been called for the current transaction.
533      * @throws SQLiteException if an error occurs.
534      * @throws OperationCanceledException if the operation was canceled.
535      *
536      * @see #beginTransaction
537      * @see #endTransaction
538      */
yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe, CancellationSignal cancellationSignal)539     public boolean yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe,
540             CancellationSignal cancellationSignal) {
541         if (throwIfUnsafe) {
542             throwIfNoTransaction();
543             throwIfTransactionMarkedSuccessful();
544             throwIfNestedTransaction();
545         } else {
546             if (mTransactionStack == null || mTransactionStack.mMarkedSuccessful
547                     || mTransactionStack.mParent != null) {
548                 return false;
549             }
550         }
551         assert mConnection != null;
552 
553         if (mTransactionStack.mChildFailed) {
554             return false;
555         }
556 
557         return yieldTransactionUnchecked(sleepAfterYieldDelayMillis,
558                 cancellationSignal); // might throw
559     }
560 
yieldTransactionUnchecked(long sleepAfterYieldDelayMillis, CancellationSignal cancellationSignal)561     private boolean yieldTransactionUnchecked(long sleepAfterYieldDelayMillis,
562             CancellationSignal cancellationSignal) {
563         if (cancellationSignal != null) {
564             cancellationSignal.throwIfCanceled();
565         }
566 
567         if (!mConnectionPool.shouldYieldConnection(mConnection, mConnectionFlags)) {
568             return false;
569         }
570 
571         final int transactionMode = mTransactionStack.mMode;
572         final SQLiteTransactionListener listener = mTransactionStack.mListener;
573         final int connectionFlags = mConnectionFlags;
574         endTransactionUnchecked(cancellationSignal, true); // might throw
575 
576         if (sleepAfterYieldDelayMillis > 0) {
577             try {
578                 Thread.sleep(sleepAfterYieldDelayMillis);
579             } catch (InterruptedException ex) {
580                 // we have been interrupted, that's all we need to do
581             }
582         }
583 
584         beginTransactionUnchecked(transactionMode, listener, connectionFlags,
585                 cancellationSignal); // might throw
586         return true;
587     }
588 
589     /**
590      * Prepares a statement for execution but does not bind its parameters or execute it.
591      * <p>
592      * This method can be used to check for syntax errors during compilation
593      * prior to execution of the statement.  If the {@code outStatementInfo} argument
594      * is not null, the provided {@link SQLiteStatementInfo} object is populated
595      * with information about the statement.
596      * </p><p>
597      * A prepared statement makes no reference to the arguments that may eventually
598      * be bound to it, consequently it it possible to cache certain prepared statements
599      * such as SELECT or INSERT/UPDATE statements.  If the statement is cacheable,
600      * then it will be stored in the cache for later and reused if possible.
601      * </p>
602      *
603      * @param sql The SQL statement to prepare.
604      * @param connectionFlags The connection flags to use if a connection must be
605      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
606      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
607      * @param outStatementInfo The {@link SQLiteStatementInfo} object to populate
608      * with information about the statement, or null if none.
609      *
610      * @throws SQLiteException if an error occurs, such as a syntax error.
611      * @throws OperationCanceledException if the operation was canceled.
612      */
prepare(String sql, int connectionFlags, CancellationSignal cancellationSignal, SQLiteStatementInfo outStatementInfo)613     public void prepare(String sql, int connectionFlags, CancellationSignal cancellationSignal,
614             SQLiteStatementInfo outStatementInfo) {
615         if (sql == null) {
616             throw new IllegalArgumentException("sql must not be null.");
617         }
618 
619         if (cancellationSignal != null) {
620             cancellationSignal.throwIfCanceled();
621         }
622 
623         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
624         try {
625             mConnection.prepare(sql, outStatementInfo); // might throw
626         } finally {
627             releaseConnection(); // might throw
628         }
629     }
630 
631     /**
632      * Executes a statement that does not return a result.
633      *
634      * @param sql The SQL statement to execute.
635      * @param bindArgs The arguments to bind, or null if none.
636      * @param connectionFlags The connection flags to use if a connection must be
637      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
638      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
639      *
640      * @throws SQLiteException if an error occurs, such as a syntax error
641      * or invalid number of bind arguments.
642      * @throws OperationCanceledException if the operation was canceled.
643      */
execute(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)644     public void execute(String sql, Object[] bindArgs, int connectionFlags,
645             CancellationSignal cancellationSignal) {
646         if (sql == null) {
647             throw new IllegalArgumentException("sql must not be null.");
648         }
649 
650         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
651             return;
652         }
653 
654         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
655         try {
656             mConnection.execute(sql, bindArgs, cancellationSignal); // might throw
657         } finally {
658             releaseConnection(); // might throw
659         }
660     }
661 
662     /**
663      * Executes a statement that returns a single <code>long</code> result.
664      *
665      * @param sql The SQL statement to execute.
666      * @param bindArgs The arguments to bind, or null if none.
667      * @param connectionFlags The connection flags to use if a connection must be
668      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
669      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
670      * @return The value of the first column in the first row of the result set
671      * as a <code>long</code>, or zero if none.
672      *
673      * @throws SQLiteException if an error occurs, such as a syntax error
674      * or invalid number of bind arguments.
675      * @throws OperationCanceledException if the operation was canceled.
676      */
executeForLong(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)677     public long executeForLong(String sql, Object[] bindArgs, int connectionFlags,
678             CancellationSignal cancellationSignal) {
679         if (sql == null) {
680             throw new IllegalArgumentException("sql must not be null.");
681         }
682 
683         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
684             return 0;
685         }
686 
687         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
688         try {
689             return mConnection.executeForLong(sql, bindArgs, cancellationSignal); // might throw
690         } finally {
691             releaseConnection(); // might throw
692         }
693     }
694 
695     /**
696      * Executes a statement that returns a single {@link String} result.
697      *
698      * @param sql The SQL statement to execute.
699      * @param bindArgs The arguments to bind, or null if none.
700      * @param connectionFlags The connection flags to use if a connection must be
701      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
702      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
703      * @return The value of the first column in the first row of the result set
704      * as a <code>String</code>, 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      */
executeForString(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)710     public String executeForString(String sql, Object[] bindArgs, int connectionFlags,
711             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.executeForString(sql, bindArgs, cancellationSignal); // might throw
723         } finally {
724             releaseConnection(); // might throw
725         }
726     }
727 
728     /**
729      * Executes a statement that returns a single BLOB result as a
730      * file descriptor to a shared memory region.
731      *
732      * @param sql The SQL statement to execute.
733      * @param bindArgs The arguments to bind, or null if none.
734      * @param connectionFlags The connection flags to use if a connection must be
735      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
736      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
737      * @return The file descriptor for a shared memory region that contains
738      * the value of the first column in the first row of the result set as a BLOB,
739      * or null if none.
740      *
741      * @throws SQLiteException if an error occurs, such as a syntax error
742      * or invalid number of bind arguments.
743      * @throws OperationCanceledException if the operation was canceled.
744      */
executeForBlobFileDescriptor(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)745     public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs,
746             int connectionFlags, CancellationSignal cancellationSignal) {
747         if (sql == null) {
748             throw new IllegalArgumentException("sql must not be null.");
749         }
750 
751         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
752             return null;
753         }
754 
755         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
756         try {
757             return mConnection.executeForBlobFileDescriptor(sql, bindArgs,
758                     cancellationSignal); // might throw
759         } finally {
760             releaseConnection(); // might throw
761         }
762     }
763 
764     /**
765      * Executes a statement that returns a count of the number of rows
766      * that were changed.  Use for UPDATE or DELETE SQL statements.
767      *
768      * @param sql The SQL statement to execute.
769      * @param bindArgs The arguments to bind, or null if none.
770      * @param connectionFlags The connection flags to use if a connection must be
771      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
772      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
773      * @return The number of rows that were changed.
774      *
775      * @throws SQLiteException if an error occurs, such as a syntax error
776      * or invalid number of bind arguments.
777      * @throws OperationCanceledException if the operation was canceled.
778      */
executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)779     public int executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags,
780             CancellationSignal cancellationSignal) {
781         if (sql == null) {
782             throw new IllegalArgumentException("sql must not be null.");
783         }
784 
785         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
786             return 0;
787         }
788 
789         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
790         try {
791             return mConnection.executeForChangedRowCount(sql, bindArgs,
792                     cancellationSignal); // might throw
793         } finally {
794             releaseConnection(); // might throw
795         }
796     }
797 
798     /**
799      * Executes a statement that returns the row id of the last row inserted
800      * by the statement.  Use for INSERT SQL statements.
801      *
802      * @param sql The SQL statement to execute.
803      * @param bindArgs The arguments to bind, or null if none.
804      * @param connectionFlags The connection flags to use if a connection must be
805      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
806      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
807      * @return The row id of the last row that was inserted, or 0 if none.
808      *
809      * @throws SQLiteException if an error occurs, such as a syntax error
810      * or invalid number of bind arguments.
811      * @throws OperationCanceledException if the operation was canceled.
812      */
executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)813     public long executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags,
814             CancellationSignal cancellationSignal) {
815         if (sql == null) {
816             throw new IllegalArgumentException("sql must not be null.");
817         }
818 
819         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
820             return 0;
821         }
822 
823         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
824         try {
825             return mConnection.executeForLastInsertedRowId(sql, bindArgs,
826                     cancellationSignal); // might throw
827         } finally {
828             releaseConnection(); // might throw
829         }
830     }
831 
832     /**
833      * Executes a statement and populates the specified {@link CursorWindow}
834      * with a range of results.  Returns the number of rows that were counted
835      * during query execution.
836      *
837      * @param sql The SQL statement to execute.
838      * @param bindArgs The arguments to bind, or null if none.
839      * @param window The cursor window to clear and fill.
840      * @param startPos The start position for filling the window.
841      * @param requiredPos The position of a row that MUST be in the window.
842      * If it won't fit, then the query should discard part of what it filled
843      * so that it does.  Must be greater than or equal to <code>startPos</code>.
844      * @param countAllRows True to count all rows that the query would return
845      * regagless of whether they fit in the window.
846      * @param connectionFlags The connection flags to use if a connection must be
847      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
848      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
849      * @return The number of rows that were counted during query execution.  Might
850      * not be all rows in the result set unless <code>countAllRows</code> is true.
851      *
852      * @throws SQLiteException if an error occurs, such as a syntax error
853      * or invalid number of bind arguments.
854      * @throws OperationCanceledException if the operation was canceled.
855      */
executeForCursorWindow(String sql, Object[] bindArgs, CursorWindow window, int startPos, int requiredPos, boolean countAllRows, int connectionFlags, CancellationSignal cancellationSignal)856     public int executeForCursorWindow(String sql, Object[] bindArgs,
857             CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
858             int connectionFlags, CancellationSignal cancellationSignal) {
859         if (sql == null) {
860             throw new IllegalArgumentException("sql must not be null.");
861         }
862         if (window == null) {
863             throw new IllegalArgumentException("window must not be null.");
864         }
865 
866         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
867             window.clear();
868             return 0;
869         }
870 
871         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
872         try {
873             return mConnection.executeForCursorWindow(sql, bindArgs,
874                     window, startPos, requiredPos, countAllRows,
875                     cancellationSignal); // might throw
876         } finally {
877             releaseConnection(); // might throw
878         }
879     }
880 
881     /**
882      * Performs special reinterpretation of certain SQL statements such as "BEGIN",
883      * "COMMIT" and "ROLLBACK" to ensure that transaction state invariants are
884      * maintained.
885      *
886      * This function is mainly used to support legacy apps that perform their
887      * own transactions by executing raw SQL rather than calling {@link #beginTransaction}
888      * and the like.
889      *
890      * @param sql The SQL statement to execute.
891      * @param bindArgs The arguments to bind, or null if none.
892      * @param connectionFlags The connection flags to use if a connection must be
893      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
894      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
895      * @return True if the statement was of a special form that was handled here,
896      * false otherwise.
897      *
898      * @throws SQLiteException if an error occurs, such as a syntax error
899      * or invalid number of bind arguments.
900      * @throws OperationCanceledException if the operation was canceled.
901      */
executeSpecial(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)902     private boolean executeSpecial(String sql, Object[] bindArgs, int connectionFlags,
903             CancellationSignal cancellationSignal) {
904         if (cancellationSignal != null) {
905             cancellationSignal.throwIfCanceled();
906         }
907 
908         final int type = DatabaseUtils.getSqlStatementType(sql);
909         switch (type) {
910             case DatabaseUtils.STATEMENT_BEGIN:
911                 beginTransaction(TRANSACTION_MODE_EXCLUSIVE, null, connectionFlags,
912                         cancellationSignal);
913                 return true;
914 
915             case DatabaseUtils.STATEMENT_COMMIT:
916                 setTransactionSuccessful();
917                 endTransaction(cancellationSignal);
918                 return true;
919 
920             case DatabaseUtils.STATEMENT_ABORT:
921                 endTransaction(cancellationSignal);
922                 return true;
923         }
924         return false;
925     }
926 
acquireConnection(String sql, int connectionFlags, CancellationSignal cancellationSignal)927     private void acquireConnection(String sql, int connectionFlags,
928             CancellationSignal cancellationSignal) {
929         if (mConnection == null) {
930             assert mConnectionUseCount == 0;
931             mConnection = mConnectionPool.acquireConnection(sql, connectionFlags,
932                     cancellationSignal); // might throw
933             mConnectionFlags = connectionFlags;
934         }
935         mConnectionUseCount += 1;
936     }
937 
releaseConnection()938     private void releaseConnection() {
939         assert mConnection != null;
940         assert mConnectionUseCount > 0;
941         if (--mConnectionUseCount == 0) {
942             try {
943                 mConnectionPool.releaseConnection(mConnection); // might throw
944             } finally {
945                 mConnection = null;
946             }
947         }
948     }
949 
950     /**
951      * Acquire a prepared statement for external use. A current transaction is required and that
952      * transaction may not have been marked successful. The dependent is registered its close()
953      * method is called when the transaction is closed.
954      */
955     @NonNull
acquirePersistentStatement(@onNull String query, @NonNull Closeable dependent)956     SQLiteConnection.PreparedStatement acquirePersistentStatement(@NonNull String query,
957             @NonNull Closeable dependent) {
958         throwIfNoTransaction();
959         throwIfTransactionMarkedSuccessful();
960         mOpenDependents.addFirst(dependent);
961         try {
962             return mConnection.acquirePersistentStatement(query);
963         } catch (Throwable e) {
964             mOpenDependents.remove(dependent);
965             throw e;
966         }
967     }
968 
969     /**
970      * Release a prepared statement.  The dependent should be in list of open dependents.
971      */
releasePersistentStatement(@onNull SQLiteConnection.PreparedStatement statement, @NonNull Closeable dependent)972     void releasePersistentStatement(@NonNull SQLiteConnection.PreparedStatement statement,
973             @NonNull Closeable dependent) {
974         mConnection.releasePreparedStatement(statement);
975         mOpenDependents.remove(dependent);
976     }
977 
978     /**
979      * Close any open dependents.  This may be called multiple times without harm.  It never
980      * throws.
981      */
closeOpenDependents()982     void closeOpenDependents() {
983         while (mOpenDependents.size() > 0) {
984             final Closeable dependent = mOpenDependents.pollFirst();
985             if (dependent != null)
986                 try {
987                     dependent.close();
988                 } catch (IOException e) {
989                     // Swallow the exception.
990                 }
991         }
992     }
993 
994     /**
995      * Return the row ID of the last row to be inserted on this connection.  Note that the last row
996      * might not have been inserted on this particular statement, but the return value is the last
997      * row inserted on the same connection as that used by this statement.  The function checks that
998      * it is currently in a transaction before executing.  Because of this check, it is not
999      * necessary to acquire and release the connection: the connection has already been acquired.
1000      * @hide
1001      */
getLastInsertRowId()1002     long getLastInsertRowId() {
1003         throwIfNoTransaction();
1004         return mConnection.getLastInsertRowId();
1005     }
1006 
1007     /**
1008      * Return the number of database rows that were changed by the most recent SQL statement on
1009      * this connection.
1010      * @hide
1011      */
getLastChangedRowCount()1012     long getLastChangedRowCount() {
1013         throwIfNoTransaction();
1014         return mConnection.getLastChangedRowCount();
1015     }
1016 
1017     /**
1018      * Return the total number of database rows that were changed on the current connection, since
1019      * it was created.
1020      * @hide
1021      */
getTotalChangedRowCount()1022     long getTotalChangedRowCount() {
1023         throwIfNoTransaction();
1024         return mConnection.getTotalChangedRowCount();
1025     }
1026 
1027     /**
1028      * Throw {@link IllegalStateException} if there is no current transaction.
1029      */
throwIfNoTransaction()1030     void throwIfNoTransaction() {
1031         if (mTransactionStack == null) {
1032             throw new IllegalStateException("Cannot perform this operation because "
1033                     + "there is no current transaction.");
1034         }
1035     }
1036 
throwIfTransactionMarkedSuccessful()1037     private void throwIfTransactionMarkedSuccessful() {
1038         if (mTransactionStack != null && mTransactionStack.mMarkedSuccessful) {
1039             throw new IllegalStateException("Cannot perform this operation because "
1040                     + "the transaction has already been marked successful.  The only "
1041                     + "thing you can do now is call endTransaction().");
1042         }
1043     }
1044 
throwIfNestedTransaction()1045     private void throwIfNestedTransaction() {
1046         if (hasNestedTransaction()) {
1047             throw new IllegalStateException("Cannot perform this operation because "
1048                     + "a nested transaction is in progress.");
1049         }
1050     }
1051 
obtainTransaction(int mode, SQLiteTransactionListener listener)1052     private Transaction obtainTransaction(int mode, SQLiteTransactionListener listener) {
1053         Transaction transaction = mTransactionPool;
1054         if (transaction != null) {
1055             mTransactionPool = transaction.mParent;
1056             transaction.mParent = null;
1057             transaction.mMarkedSuccessful = false;
1058             transaction.mChildFailed = false;
1059         } else {
1060             transaction = new Transaction();
1061         }
1062         transaction.mMode = mode;
1063         transaction.mListener = listener;
1064         return transaction;
1065     }
1066 
recycleTransaction(Transaction transaction)1067     private void recycleTransaction(Transaction transaction) {
1068         transaction.mParent = mTransactionPool;
1069         transaction.mListener = null;
1070         mTransactionPool = transaction;
1071     }
1072 
1073     private static final class Transaction {
1074         public Transaction mParent;
1075         public int mMode;
1076         public SQLiteTransactionListener mListener;
1077         public boolean mMarkedSuccessful;
1078         public boolean mChildFailed;
1079     }
1080 }
1081