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