Home | History | Annotate | Download | only in sqlite
      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.database.CursorWindow;
     20 import android.database.DatabaseUtils;
     21 import android.os.CancellationSignal;
     22 import android.os.OperationCanceledException;
     23 import 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  */
    163 public 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 (hasNestedTransaction()) {
    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