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 */ SQLiteSession(SQLiteConnectionPool connectionPool)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 */ hasTransaction()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 */ hasNestedTransaction()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 */ hasConnection()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 */ beginTransaction(int transactionMode, SQLiteTransactionListener transactionListener, int connectionFlags, CancellationSignal cancellationSignal)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 beginTransactionUnchecked(int transactionMode, SQLiteTransactionListener transactionListener, int connectionFlags, CancellationSignal cancellationSignal)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 */ setTransactionSuccessful()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 */ endTransaction(CancellationSignal cancellationSignal)397 public void endTransaction(CancellationSignal cancellationSignal) { 398 throwIfNoTransaction(); 399 assert mConnection != null; 400 401 endTransactionUnchecked(cancellationSignal, false); 402 } 403 endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding)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 */ yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe, CancellationSignal cancellationSignal)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 yieldTransactionUnchecked(long sleepAfterYieldDelayMillis, CancellationSignal cancellationSignal)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 */ prepare(String sql, int connectionFlags, CancellationSignal cancellationSignal, SQLiteStatementInfo outStatementInfo)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 */ execute(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)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 */ executeForLong(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)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 */ executeForString(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)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 */ executeForBlobFileDescriptor(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)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 */ executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)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 */ executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)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 */ executeForCursorWindow(String sql, Object[] bindArgs, CursorWindow window, int startPos, int requiredPos, boolean countAllRows, int connectionFlags, CancellationSignal cancellationSignal)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 */ executeSpecial(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)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 acquireConnection(String sql, int connectionFlags, CancellationSignal cancellationSignal)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 releaseConnection()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 throwIfNoTransaction()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 throwIfTransactionMarkedSuccessful()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 throwIfNestedTransaction()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 obtainTransaction(int mode, SQLiteTransactionListener listener)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 recycleTransaction(Transaction transaction)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