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