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 
beginTransactionUnchecked(int transactionMode, SQLiteTransactionListener transactionListener, int connectionFlags, CancellationSignal cancellationSignal)315     private void beginTransactionUnchecked(int transactionMode,
316             SQLiteTransactionListener transactionListener, int connectionFlags,
317             CancellationSignal cancellationSignal) {
318         if (cancellationSignal != null) {
319             cancellationSignal.throwIfCanceled();
320         }
321 
322         if (mTransactionStack == null) {
323             acquireConnection(null, connectionFlags, cancellationSignal); // might throw
324         }
325         try {
326             // Set up the transaction such that we can back out safely
327             // in case we fail part way.
328             if (mTransactionStack == null) {
329                 // Execute SQL might throw a runtime exception.
330                 switch (transactionMode) {
331                     case TRANSACTION_MODE_IMMEDIATE:
332                         mConnection.execute("BEGIN IMMEDIATE;", null,
333                                 cancellationSignal); // might throw
334                         break;
335                     case TRANSACTION_MODE_EXCLUSIVE:
336                         mConnection.execute("BEGIN EXCLUSIVE;", null,
337                                 cancellationSignal); // might throw
338                         break;
339                     case TRANSACTION_MODE_DEFERRED:
340                         mConnection.execute("BEGIN DEFERRED;", null,
341                                 cancellationSignal); // might throw
342                         break;
343                     default:
344                         // Per SQLite documentation, this executes in DEFERRED mode.
345                         mConnection.execute("BEGIN;", null, cancellationSignal); // might throw
346                         break;
347                 }
348             }
349 
350             // Listener might throw a runtime exception.
351             if (transactionListener != null) {
352                 try {
353                     transactionListener.onBegin(); // might throw
354                 } catch (RuntimeException ex) {
355                     if (mTransactionStack == null) {
356                         mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
357                     }
358                     throw ex;
359                 }
360             }
361 
362             // Bookkeeping can't throw, except an OOM, which is just too bad...
363             Transaction transaction = obtainTransaction(transactionMode, transactionListener);
364             transaction.mParent = mTransactionStack;
365             mTransactionStack = transaction;
366         } finally {
367             if (mTransactionStack == null) {
368                 releaseConnection(); // might throw
369             }
370         }
371     }
372 
373     /**
374      * Marks the current transaction as having completed successfully.
375      * <p>
376      * This method can be called at most once between {@link #beginTransaction} and
377      * {@link #endTransaction} to indicate that the changes made by the transaction should be
378      * committed.  If this method is not called, the changes will be rolled back
379      * when the transaction is ended.
380      * </p>
381      *
382      * @throws IllegalStateException if there is no current transaction, or if
383      * {@link #setTransactionSuccessful} has already been called for the current transaction.
384      *
385      * @see #beginTransaction
386      * @see #endTransaction
387      */
setTransactionSuccessful()388     public void setTransactionSuccessful() {
389         throwIfNoTransaction();
390         throwIfTransactionMarkedSuccessful();
391 
392         mTransactionStack.mMarkedSuccessful = true;
393         // Close open dependents, since the next thing that is supposed to happen is the transaction
394         // ends.
395         closeOpenDependents();
396     }
397 
398     /**
399      * Ends the current transaction and commits or rolls back changes.
400      * <p>
401      * If this is the outermost transaction (not nested within any other
402      * transaction), then the changes are committed if {@link #setTransactionSuccessful}
403      * was called or rolled back otherwise.
404      * </p><p>
405      * This method must be called exactly once for each call to {@link #beginTransaction}.
406      * </p>
407      *
408      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
409      *
410      * @throws IllegalStateException if there is no current transaction.
411      * @throws SQLiteException if an error occurs.
412      * @throws OperationCanceledException if the operation was canceled.
413      *
414      * @see #beginTransaction
415      * @see #setTransactionSuccessful
416      * @see #yieldTransaction
417      */
endTransaction(CancellationSignal cancellationSignal)418     public void endTransaction(CancellationSignal cancellationSignal) {
419         throwIfNoTransaction();
420         assert mConnection != null;
421 
422         endTransactionUnchecked(cancellationSignal, false);
423     }
424 
endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding)425     private void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) {
426         if (cancellationSignal != null) {
427             cancellationSignal.throwIfCanceled();
428         }
429 
430         final Transaction top = mTransactionStack;
431         boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed;
432 
433         RuntimeException listenerException = null;
434         final SQLiteTransactionListener listener = top.mListener;
435         if (listener != null) {
436             try {
437                 if (successful) {
438                     listener.onCommit(); // might throw
439                 } else {
440                     listener.onRollback(); // might throw
441                 }
442             } catch (RuntimeException ex) {
443                 listenerException = ex;
444                 successful = false;
445             }
446         }
447 
448         mTransactionStack = top.mParent;
449         recycleTransaction(top);
450 
451         if (mTransactionStack != null) {
452             if (!successful) {
453                 mTransactionStack.mChildFailed = true;
454             }
455         } else {
456             // Close all dependents before anything that might throw.  The list should have been
457             // cleared when the transaction was marked successful or unsuccessful.  The call here
458             // does nothing if the list is empty but is provided for insurance.
459             closeOpenDependents();
460 
461             try {
462                 if (successful) {
463                     mConnection.execute("COMMIT;", null, cancellationSignal); // might throw
464                 } else {
465                     mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
466                 }
467             } finally {
468                 releaseConnection(); // might throw
469             }
470         }
471 
472         if (listenerException != null) {
473             throw listenerException;
474         }
475     }
476 
477     /**
478      * Temporarily ends a transaction to let other threads have use of
479      * the database.  Begins a new transaction after a specified delay.
480      * <p>
481      * If there are other threads waiting to acquire connections,
482      * then the current transaction is committed and the database
483      * connection is released.  After a short delay, a new transaction
484      * is started.
485      * </p><p>
486      * The transaction is assumed to be successful so far.  Do not call
487      * {@link #setTransactionSuccessful()} before calling this method.
488      * This method will fail if the transaction has already been marked
489      * successful.
490      * </p><p>
491      * The changes that were committed by a yield cannot be rolled back later.
492      * </p><p>
493      * Before this method was called, there must already have been
494      * a transaction in progress.  When this method returns, there will
495      * still be a transaction in progress, either the same one as before
496      * or a new one if the transaction was actually yielded.
497      * </p><p>
498      * This method should not be called when there is a nested transaction
499      * in progress because it is not possible to yield a nested transaction.
500      * If <code>throwIfNested</code> is true, then attempting to yield
501      * a nested transaction will throw {@link IllegalStateException}, otherwise
502      * the method will return <code>false</code> in that case.
503      * </p><p>
504      * If there is no nested transaction in progress but a previous nested
505      * transaction failed, then the transaction is not yielded (because it
506      * must be rolled back) and this method returns <code>false</code>.
507      * </p>
508      *
509      * @param sleepAfterYieldDelayMillis A delay time to wait after yielding
510      * the database connection to allow other threads some time to run.
511      * If the value is less than or equal to zero, there will be no additional
512      * delay beyond the time it will take to begin a new transaction.
513      * @param throwIfUnsafe If true, then instead of returning false when no
514      * transaction is in progress, a nested transaction is in progress, or when
515      * the transaction has already been marked successful, throws {@link IllegalStateException}.
516      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
517      * @return True if the transaction was actually yielded.
518      *
519      * @throws IllegalStateException if <code>throwIfNested</code> is true and
520      * there is no current transaction, there is a nested transaction in progress or
521      * if {@link #setTransactionSuccessful} has already been called for the current transaction.
522      * @throws SQLiteException if an error occurs.
523      * @throws OperationCanceledException if the operation was canceled.
524      *
525      * @see #beginTransaction
526      * @see #endTransaction
527      */
yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe, CancellationSignal cancellationSignal)528     public boolean yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe,
529             CancellationSignal cancellationSignal) {
530         if (throwIfUnsafe) {
531             throwIfNoTransaction();
532             throwIfTransactionMarkedSuccessful();
533             throwIfNestedTransaction();
534         } else {
535             if (mTransactionStack == null || mTransactionStack.mMarkedSuccessful
536                     || mTransactionStack.mParent != null) {
537                 return false;
538             }
539         }
540         assert mConnection != null;
541 
542         if (mTransactionStack.mChildFailed) {
543             return false;
544         }
545 
546         return yieldTransactionUnchecked(sleepAfterYieldDelayMillis,
547                 cancellationSignal); // might throw
548     }
549 
yieldTransactionUnchecked(long sleepAfterYieldDelayMillis, CancellationSignal cancellationSignal)550     private boolean yieldTransactionUnchecked(long sleepAfterYieldDelayMillis,
551             CancellationSignal cancellationSignal) {
552         if (cancellationSignal != null) {
553             cancellationSignal.throwIfCanceled();
554         }
555 
556         if (!mConnectionPool.shouldYieldConnection(mConnection, mConnectionFlags)) {
557             return false;
558         }
559 
560         final int transactionMode = mTransactionStack.mMode;
561         final SQLiteTransactionListener listener = mTransactionStack.mListener;
562         final int connectionFlags = mConnectionFlags;
563         endTransactionUnchecked(cancellationSignal, true); // might throw
564 
565         if (sleepAfterYieldDelayMillis > 0) {
566             try {
567                 Thread.sleep(sleepAfterYieldDelayMillis);
568             } catch (InterruptedException ex) {
569                 // we have been interrupted, that's all we need to do
570             }
571         }
572 
573         beginTransactionUnchecked(transactionMode, listener, connectionFlags,
574                 cancellationSignal); // might throw
575         return true;
576     }
577 
578     /**
579      * Prepares a statement for execution but does not bind its parameters or execute it.
580      * <p>
581      * This method can be used to check for syntax errors during compilation
582      * prior to execution of the statement.  If the {@code outStatementInfo} argument
583      * is not null, the provided {@link SQLiteStatementInfo} object is populated
584      * with information about the statement.
585      * </p><p>
586      * A prepared statement makes no reference to the arguments that may eventually
587      * be bound to it, consequently it it possible to cache certain prepared statements
588      * such as SELECT or INSERT/UPDATE statements.  If the statement is cacheable,
589      * then it will be stored in the cache for later and reused if possible.
590      * </p>
591      *
592      * @param sql The SQL statement to prepare.
593      * @param connectionFlags The connection flags to use if a connection must be
594      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
595      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
596      * @param outStatementInfo The {@link SQLiteStatementInfo} object to populate
597      * with information about the statement, or null if none.
598      *
599      * @throws SQLiteException if an error occurs, such as a syntax error.
600      * @throws OperationCanceledException if the operation was canceled.
601      */
prepare(String sql, int connectionFlags, CancellationSignal cancellationSignal, SQLiteStatementInfo outStatementInfo)602     public void prepare(String sql, int connectionFlags, CancellationSignal cancellationSignal,
603             SQLiteStatementInfo outStatementInfo) {
604         if (sql == null) {
605             throw new IllegalArgumentException("sql must not be null.");
606         }
607 
608         if (cancellationSignal != null) {
609             cancellationSignal.throwIfCanceled();
610         }
611 
612         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
613         try {
614             mConnection.prepare(sql, outStatementInfo); // might throw
615         } finally {
616             releaseConnection(); // might throw
617         }
618     }
619 
620     /**
621      * Executes a statement that does not return a result.
622      *
623      * @param sql The SQL statement to execute.
624      * @param bindArgs The arguments to bind, or null if none.
625      * @param connectionFlags The connection flags to use if a connection must be
626      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
627      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
628      *
629      * @throws SQLiteException if an error occurs, such as a syntax error
630      * or invalid number of bind arguments.
631      * @throws OperationCanceledException if the operation was canceled.
632      */
execute(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)633     public void execute(String sql, Object[] bindArgs, int connectionFlags,
634             CancellationSignal cancellationSignal) {
635         if (sql == null) {
636             throw new IllegalArgumentException("sql must not be null.");
637         }
638 
639         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
640             return;
641         }
642 
643         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
644         try {
645             mConnection.execute(sql, bindArgs, cancellationSignal); // might throw
646         } finally {
647             releaseConnection(); // might throw
648         }
649     }
650 
651     /**
652      * Executes a statement that returns a single <code>long</code> result.
653      *
654      * @param sql The SQL statement to execute.
655      * @param bindArgs The arguments to bind, or null if none.
656      * @param connectionFlags The connection flags to use if a connection must be
657      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
658      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
659      * @return The value of the first column in the first row of the result set
660      * as a <code>long</code>, or zero if none.
661      *
662      * @throws SQLiteException if an error occurs, such as a syntax error
663      * or invalid number of bind arguments.
664      * @throws OperationCanceledException if the operation was canceled.
665      */
executeForLong(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)666     public long executeForLong(String sql, Object[] bindArgs, int connectionFlags,
667             CancellationSignal cancellationSignal) {
668         if (sql == null) {
669             throw new IllegalArgumentException("sql must not be null.");
670         }
671 
672         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
673             return 0;
674         }
675 
676         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
677         try {
678             return mConnection.executeForLong(sql, bindArgs, cancellationSignal); // might throw
679         } finally {
680             releaseConnection(); // might throw
681         }
682     }
683 
684     /**
685      * Executes a statement that returns a single {@link String} result.
686      *
687      * @param sql The SQL statement to execute.
688      * @param bindArgs The arguments to bind, or null if none.
689      * @param connectionFlags The connection flags to use if a connection must be
690      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
691      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
692      * @return The value of the first column in the first row of the result set
693      * as a <code>String</code>, or null if none.
694      *
695      * @throws SQLiteException if an error occurs, such as a syntax error
696      * or invalid number of bind arguments.
697      * @throws OperationCanceledException if the operation was canceled.
698      */
executeForString(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)699     public String executeForString(String sql, Object[] bindArgs, int connectionFlags,
700             CancellationSignal cancellationSignal) {
701         if (sql == null) {
702             throw new IllegalArgumentException("sql must not be null.");
703         }
704 
705         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
706             return null;
707         }
708 
709         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
710         try {
711             return mConnection.executeForString(sql, bindArgs, cancellationSignal); // might throw
712         } finally {
713             releaseConnection(); // might throw
714         }
715     }
716 
717     /**
718      * Executes a statement that returns a single BLOB result as a
719      * file descriptor to a shared memory region.
720      *
721      * @param sql The SQL statement to execute.
722      * @param bindArgs The arguments to bind, or null if none.
723      * @param connectionFlags The connection flags to use if a connection must be
724      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
725      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
726      * @return The file descriptor for a shared memory region that contains
727      * the value of the first column in the first row of the result set as a BLOB,
728      * or null if none.
729      *
730      * @throws SQLiteException if an error occurs, such as a syntax error
731      * or invalid number of bind arguments.
732      * @throws OperationCanceledException if the operation was canceled.
733      */
executeForBlobFileDescriptor(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)734     public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs,
735             int connectionFlags, CancellationSignal cancellationSignal) {
736         if (sql == null) {
737             throw new IllegalArgumentException("sql must not be null.");
738         }
739 
740         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
741             return null;
742         }
743 
744         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
745         try {
746             return mConnection.executeForBlobFileDescriptor(sql, bindArgs,
747                     cancellationSignal); // might throw
748         } finally {
749             releaseConnection(); // might throw
750         }
751     }
752 
753     /**
754      * Executes a statement that returns a count of the number of rows
755      * that were changed.  Use for UPDATE or DELETE SQL statements.
756      *
757      * @param sql The SQL statement to execute.
758      * @param bindArgs The arguments to bind, or null if none.
759      * @param connectionFlags The connection flags to use if a connection must be
760      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
761      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
762      * @return The number of rows that were changed.
763      *
764      * @throws SQLiteException if an error occurs, such as a syntax error
765      * or invalid number of bind arguments.
766      * @throws OperationCanceledException if the operation was canceled.
767      */
executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)768     public int executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags,
769             CancellationSignal cancellationSignal) {
770         if (sql == null) {
771             throw new IllegalArgumentException("sql must not be null.");
772         }
773 
774         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
775             return 0;
776         }
777 
778         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
779         try {
780             return mConnection.executeForChangedRowCount(sql, bindArgs,
781                     cancellationSignal); // might throw
782         } finally {
783             releaseConnection(); // might throw
784         }
785     }
786 
787     /**
788      * Executes a statement that returns the row id of the last row inserted
789      * by the statement.  Use for INSERT SQL statements.
790      *
791      * @param sql The SQL statement to execute.
792      * @param bindArgs The arguments to bind, or null if none.
793      * @param connectionFlags The connection flags to use if a connection must be
794      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
795      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
796      * @return The row id of the last row that was inserted, or 0 if none.
797      *
798      * @throws SQLiteException if an error occurs, such as a syntax error
799      * or invalid number of bind arguments.
800      * @throws OperationCanceledException if the operation was canceled.
801      */
executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)802     public long executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags,
803             CancellationSignal cancellationSignal) {
804         if (sql == null) {
805             throw new IllegalArgumentException("sql must not be null.");
806         }
807 
808         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
809             return 0;
810         }
811 
812         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
813         try {
814             return mConnection.executeForLastInsertedRowId(sql, bindArgs,
815                     cancellationSignal); // might throw
816         } finally {
817             releaseConnection(); // might throw
818         }
819     }
820 
821     /**
822      * Executes a statement and populates the specified {@link CursorWindow}
823      * with a range of results.  Returns the number of rows that were counted
824      * during query execution.
825      *
826      * @param sql The SQL statement to execute.
827      * @param bindArgs The arguments to bind, or null if none.
828      * @param window The cursor window to clear and fill.
829      * @param startPos The start position for filling the window.
830      * @param requiredPos The position of a row that MUST be in the window.
831      * If it won't fit, then the query should discard part of what it filled
832      * so that it does.  Must be greater than or equal to <code>startPos</code>.
833      * @param countAllRows True to count all rows that the query would return
834      * regagless of whether they fit in the window.
835      * @param connectionFlags The connection flags to use if a connection must be
836      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
837      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
838      * @return The number of rows that were counted during query execution.  Might
839      * not be all rows in the result set unless <code>countAllRows</code> is true.
840      *
841      * @throws SQLiteException if an error occurs, such as a syntax error
842      * or invalid number of bind arguments.
843      * @throws OperationCanceledException if the operation was canceled.
844      */
executeForCursorWindow(String sql, Object[] bindArgs, CursorWindow window, int startPos, int requiredPos, boolean countAllRows, int connectionFlags, CancellationSignal cancellationSignal)845     public int executeForCursorWindow(String sql, Object[] bindArgs,
846             CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
847             int connectionFlags, CancellationSignal cancellationSignal) {
848         if (sql == null) {
849             throw new IllegalArgumentException("sql must not be null.");
850         }
851         if (window == null) {
852             throw new IllegalArgumentException("window must not be null.");
853         }
854 
855         if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
856             window.clear();
857             return 0;
858         }
859 
860         acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
861         try {
862             return mConnection.executeForCursorWindow(sql, bindArgs,
863                     window, startPos, requiredPos, countAllRows,
864                     cancellationSignal); // might throw
865         } finally {
866             releaseConnection(); // might throw
867         }
868     }
869 
870     /**
871      * Performs special reinterpretation of certain SQL statements such as "BEGIN",
872      * "COMMIT" and "ROLLBACK" to ensure that transaction state invariants are
873      * maintained.
874      *
875      * This function is mainly used to support legacy apps that perform their
876      * own transactions by executing raw SQL rather than calling {@link #beginTransaction}
877      * and the like.
878      *
879      * @param sql The SQL statement to execute.
880      * @param bindArgs The arguments to bind, or null if none.
881      * @param connectionFlags The connection flags to use if a connection must be
882      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
883      * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
884      * @return True if the statement was of a special form that was handled here,
885      * false otherwise.
886      *
887      * @throws SQLiteException if an error occurs, such as a syntax error
888      * or invalid number of bind arguments.
889      * @throws OperationCanceledException if the operation was canceled.
890      */
executeSpecial(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)891     private boolean executeSpecial(String sql, Object[] bindArgs, int connectionFlags,
892             CancellationSignal cancellationSignal) {
893         if (cancellationSignal != null) {
894             cancellationSignal.throwIfCanceled();
895         }
896 
897         final int type = DatabaseUtils.getSqlStatementType(sql);
898         switch (type) {
899             case DatabaseUtils.STATEMENT_BEGIN:
900                 beginTransaction(TRANSACTION_MODE_EXCLUSIVE, null, connectionFlags,
901                         cancellationSignal);
902                 return true;
903 
904             case DatabaseUtils.STATEMENT_COMMIT:
905                 setTransactionSuccessful();
906                 endTransaction(cancellationSignal);
907                 return true;
908 
909             case DatabaseUtils.STATEMENT_ABORT:
910                 endTransaction(cancellationSignal);
911                 return true;
912         }
913         return false;
914     }
915 
acquireConnection(String sql, int connectionFlags, CancellationSignal cancellationSignal)916     private void acquireConnection(String sql, int connectionFlags,
917             CancellationSignal cancellationSignal) {
918         if (mConnection == null) {
919             assert mConnectionUseCount == 0;
920             mConnection = mConnectionPool.acquireConnection(sql, connectionFlags,
921                     cancellationSignal); // might throw
922             mConnectionFlags = connectionFlags;
923         }
924         mConnectionUseCount += 1;
925     }
926 
releaseConnection()927     private void releaseConnection() {
928         assert mConnection != null;
929         assert mConnectionUseCount > 0;
930         if (--mConnectionUseCount == 0) {
931             try {
932                 mConnectionPool.releaseConnection(mConnection); // might throw
933             } finally {
934                 mConnection = null;
935             }
936         }
937     }
938 
939     /**
940      * Acquire a prepared statement for external use. A current transaction is required and that
941      * transaction may not have been marked successful. The dependent is registered its close()
942      * method is called when the transaction is closed.
943      */
944     @NonNull
acquirePersistentStatement(@onNull String query, @NonNull Closeable dependent)945     SQLiteConnection.PreparedStatement acquirePersistentStatement(@NonNull String query,
946             @NonNull Closeable dependent) {
947         throwIfNoTransaction();
948         throwIfTransactionMarkedSuccessful();
949         mOpenDependents.addFirst(dependent);
950         try {
951             return mConnection.acquirePersistentStatement(query);
952         } catch (Throwable e) {
953             mOpenDependents.remove(dependent);
954             throw e;
955         }
956     }
957 
958     /**
959      * Release a prepared statement.  The dependent should be in list of open dependents.
960      */
releasePersistentStatement(@onNull SQLiteConnection.PreparedStatement statement, @NonNull Closeable dependent)961     void releasePersistentStatement(@NonNull SQLiteConnection.PreparedStatement statement,
962             @NonNull Closeable dependent) {
963         mConnection.releasePreparedStatement(statement);
964         mOpenDependents.remove(dependent);
965     }
966 
967     /**
968      * Close any open dependents.  This may be called multiple times without harm.  It never
969      * throws.
970      */
closeOpenDependents()971     void closeOpenDependents() {
972         while (mOpenDependents.size() > 0) {
973             final Closeable dependent = mOpenDependents.pollFirst();
974             if (dependent != null)
975                 try {
976                     dependent.close();
977                 } catch (IOException e) {
978                     // Swallow the exception.
979                 }
980         }
981     }
982 
983     /**
984      * Return the row ID of the last row to be inserted on this connection.  Note that the last row
985      * might not have been inserted on this particular statement, but the return value is the last
986      * row inserted on the same connection as that used by this statement.  The function checks that
987      * it is currently in a transaction before executing.  Because of this check, it is not
988      * necessary to acquire and release the connection: the connection has already been acquired.
989      * @hide
990      */
getLastInsertRowId()991     long getLastInsertRowId() {
992         throwIfNoTransaction();
993         return mConnection.getLastInsertRowId();
994     }
995 
996     /**
997      * Return the number of database rows that were changed by the most recent SQL statement on
998      * this connection.
999      * @hide
1000      */
getLastChangedRowCount()1001     long getLastChangedRowCount() {
1002         throwIfNoTransaction();
1003         return mConnection.getLastChangedRowCount();
1004     }
1005 
1006     /**
1007      * Return the total number of database rows that were changed on the current connection, since
1008      * it was created.
1009      * @hide
1010      */
getTotalChangedRowCount()1011     long getTotalChangedRowCount() {
1012         throwIfNoTransaction();
1013         return mConnection.getTotalChangedRowCount();
1014     }
1015 
1016     /**
1017      * Throw {@link IllegalStateException} if there is no current transaction.
1018      */
throwIfNoTransaction()1019     void throwIfNoTransaction() {
1020         if (mTransactionStack == null) {
1021             throw new IllegalStateException("Cannot perform this operation because "
1022                     + "there is no current transaction.");
1023         }
1024     }
1025 
throwIfTransactionMarkedSuccessful()1026     private void throwIfTransactionMarkedSuccessful() {
1027         if (mTransactionStack != null && mTransactionStack.mMarkedSuccessful) {
1028             throw new IllegalStateException("Cannot perform this operation because "
1029                     + "the transaction has already been marked successful.  The only "
1030                     + "thing you can do now is call endTransaction().");
1031         }
1032     }
1033 
throwIfNestedTransaction()1034     private void throwIfNestedTransaction() {
1035         if (hasNestedTransaction()) {
1036             throw new IllegalStateException("Cannot perform this operation because "
1037                     + "a nested transaction is in progress.");
1038         }
1039     }
1040 
obtainTransaction(int mode, SQLiteTransactionListener listener)1041     private Transaction obtainTransaction(int mode, SQLiteTransactionListener listener) {
1042         Transaction transaction = mTransactionPool;
1043         if (transaction != null) {
1044             mTransactionPool = transaction.mParent;
1045             transaction.mParent = null;
1046             transaction.mMarkedSuccessful = false;
1047             transaction.mChildFailed = false;
1048         } else {
1049             transaction = new Transaction();
1050         }
1051         transaction.mMode = mode;
1052         transaction.mListener = listener;
1053         return transaction;
1054     }
1055 
recycleTransaction(Transaction transaction)1056     private void recycleTransaction(Transaction transaction) {
1057         transaction.mParent = mTransactionPool;
1058         transaction.mListener = null;
1059         mTransactionPool = transaction;
1060     }
1061 
1062     private static final class Transaction {
1063         public Transaction mParent;
1064         public int mMode;
1065         public SQLiteTransactionListener mListener;
1066         public boolean mMarkedSuccessful;
1067         public boolean mChildFailed;
1068     }
1069 }
1070