gnunet-android

GNUnet for Android
Log | Files | Refs | README

SQLiteSession.java (41215B)


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