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 modeString(int transactionMode)315 private String modeString(int transactionMode) { 316 switch (transactionMode) { 317 case TRANSACTION_MODE_IMMEDIATE: return "TRANSACTION-IMMEDIATE"; 318 case TRANSACTION_MODE_EXCLUSIVE: return "TRANSACTION-EXCLUSIVE"; 319 case TRANSACTION_MODE_DEFERRED: return "TRANSACTION-DEFERRED"; 320 default: return "TRANSACTION"; 321 } 322 } 323 beginTransactionUnchecked(int transactionMode, SQLiteTransactionListener transactionListener, int connectionFlags, CancellationSignal cancellationSignal)324 private void beginTransactionUnchecked(int transactionMode, 325 SQLiteTransactionListener transactionListener, int connectionFlags, 326 CancellationSignal cancellationSignal) { 327 if (cancellationSignal != null) { 328 cancellationSignal.throwIfCanceled(); 329 } 330 331 if (mTransactionStack == null) { 332 acquireConnection(null, connectionFlags, cancellationSignal); // might throw 333 mConnection.recordBeginTransaction(modeString(transactionMode)); 334 } 335 try { 336 // Set up the transaction such that we can back out safely 337 // in case we fail part way. 338 if (mTransactionStack == null) { 339 // Execute SQL might throw a runtime exception. 340 switch (transactionMode) { 341 case TRANSACTION_MODE_IMMEDIATE: 342 mConnection.execute("BEGIN IMMEDIATE;", null, 343 cancellationSignal); // might throw 344 break; 345 case TRANSACTION_MODE_EXCLUSIVE: 346 mConnection.execute("BEGIN EXCLUSIVE;", null, 347 cancellationSignal); // might throw 348 break; 349 case TRANSACTION_MODE_DEFERRED: 350 mConnection.execute("BEGIN DEFERRED;", null, 351 cancellationSignal); // might throw 352 break; 353 default: 354 // Per SQLite documentation, this executes in DEFERRED mode. 355 mConnection.execute("BEGIN;", null, cancellationSignal); // might throw 356 break; 357 } 358 } 359 360 // Listener might throw a runtime exception. 361 if (transactionListener != null) { 362 try { 363 transactionListener.onBegin(); // might throw 364 } catch (RuntimeException ex) { 365 if (mTransactionStack == null) { 366 mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw 367 } 368 throw ex; 369 } 370 } 371 372 // Bookkeeping can't throw, except an OOM, which is just too bad... 373 Transaction transaction = obtainTransaction(transactionMode, transactionListener); 374 transaction.mParent = mTransactionStack; 375 mTransactionStack = transaction; 376 } finally { 377 if (mTransactionStack == null) { 378 releaseConnection(); // might throw 379 } 380 } 381 } 382 383 /** 384 * Marks the current transaction as having completed successfully. 385 * <p> 386 * This method can be called at most once between {@link #beginTransaction} and 387 * {@link #endTransaction} to indicate that the changes made by the transaction should be 388 * committed. If this method is not called, the changes will be rolled back 389 * when the transaction is ended. 390 * </p> 391 * 392 * @throws IllegalStateException if there is no current transaction, or if 393 * {@link #setTransactionSuccessful} has already been called for the current transaction. 394 * 395 * @see #beginTransaction 396 * @see #endTransaction 397 */ setTransactionSuccessful()398 public void setTransactionSuccessful() { 399 throwIfNoTransaction(); 400 throwIfTransactionMarkedSuccessful(); 401 402 mTransactionStack.mMarkedSuccessful = true; 403 // Close open dependents, since the next thing that is supposed to happen is the transaction 404 // ends. 405 closeOpenDependents(); 406 } 407 408 /** 409 * Ends the current transaction and commits or rolls back changes. 410 * <p> 411 * If this is the outermost transaction (not nested within any other 412 * transaction), then the changes are committed if {@link #setTransactionSuccessful} 413 * was called or rolled back otherwise. 414 * </p><p> 415 * This method must be called exactly once for each call to {@link #beginTransaction}. 416 * </p> 417 * 418 * @param cancellationSignal A signal to cancel the operation in progress, or null if none. 419 * 420 * @throws IllegalStateException if there is no current transaction. 421 * @throws SQLiteException if an error occurs. 422 * @throws OperationCanceledException if the operation was canceled. 423 * 424 * @see #beginTransaction 425 * @see #setTransactionSuccessful 426 * @see #yieldTransaction 427 */ endTransaction(CancellationSignal cancellationSignal)428 public void endTransaction(CancellationSignal cancellationSignal) { 429 throwIfNoTransaction(); 430 assert mConnection != null; 431 432 endTransactionUnchecked(cancellationSignal, false); 433 } 434 endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding)435 private void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) { 436 if (cancellationSignal != null) { 437 cancellationSignal.throwIfCanceled(); 438 } 439 440 final Transaction top = mTransactionStack; 441 boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed; 442 443 RuntimeException listenerException = null; 444 final SQLiteTransactionListener listener = top.mListener; 445 if (listener != null) { 446 try { 447 if (successful) { 448 listener.onCommit(); // might throw 449 } else { 450 listener.onRollback(); // might throw 451 } 452 } catch (RuntimeException ex) { 453 listenerException = ex; 454 successful = false; 455 } 456 } 457 458 mTransactionStack = top.mParent; 459 recycleTransaction(top); 460 461 if (mTransactionStack != null) { 462 if (!successful) { 463 mTransactionStack.mChildFailed = true; 464 } 465 } else { 466 // Close all dependents before anything that might throw. The list should have been 467 // cleared when the transaction was marked successful or unsuccessful. The call here 468 // does nothing if the list is empty but is provided for insurance. 469 closeOpenDependents(); 470 471 try { 472 if (successful) { 473 mConnection.execute("COMMIT;", null, cancellationSignal); // might throw 474 } else { 475 mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw 476 } 477 } finally { 478 mConnection.recordEndTransaction(successful); 479 releaseConnection(); // might throw 480 } 481 } 482 483 if (listenerException != null) { 484 throw listenerException; 485 } 486 } 487 488 /** 489 * Temporarily ends a transaction to let other threads have use of 490 * the database. Begins a new transaction after a specified delay. 491 * <p> 492 * If there are other threads waiting to acquire connections, 493 * then the current transaction is committed and the database 494 * connection is released. After a short delay, a new transaction 495 * is started. 496 * </p><p> 497 * The transaction is assumed to be successful so far. Do not call 498 * {@link #setTransactionSuccessful()} before calling this method. 499 * This method will fail if the transaction has already been marked 500 * successful. 501 * </p><p> 502 * The changes that were committed by a yield cannot be rolled back later. 503 * </p><p> 504 * Before this method was called, there must already have been 505 * a transaction in progress. When this method returns, there will 506 * still be a transaction in progress, either the same one as before 507 * or a new one if the transaction was actually yielded. 508 * </p><p> 509 * This method should not be called when there is a nested transaction 510 * in progress because it is not possible to yield a nested transaction. 511 * If <code>throwIfNested</code> is true, then attempting to yield 512 * a nested transaction will throw {@link IllegalStateException}, otherwise 513 * the method will return <code>false</code> in that case. 514 * </p><p> 515 * If there is no nested transaction in progress but a previous nested 516 * transaction failed, then the transaction is not yielded (because it 517 * must be rolled back) and this method returns <code>false</code>. 518 * </p> 519 * 520 * @param sleepAfterYieldDelayMillis A delay time to wait after yielding 521 * the database connection to allow other threads some time to run. 522 * If the value is less than or equal to zero, there will be no additional 523 * delay beyond the time it will take to begin a new transaction. 524 * @param throwIfUnsafe If true, then instead of returning false when no 525 * transaction is in progress, a nested transaction is in progress, or when 526 * the transaction has already been marked successful, throws {@link IllegalStateException}. 527 * @param cancellationSignal A signal to cancel the operation in progress, or null if none. 528 * @return True if the transaction was actually yielded. 529 * 530 * @throws IllegalStateException if <code>throwIfNested</code> is true and 531 * there is no current transaction, there is a nested transaction in progress or 532 * if {@link #setTransactionSuccessful} has already been called for the current transaction. 533 * @throws SQLiteException if an error occurs. 534 * @throws OperationCanceledException if the operation was canceled. 535 * 536 * @see #beginTransaction 537 * @see #endTransaction 538 */ yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe, CancellationSignal cancellationSignal)539 public boolean yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe, 540 CancellationSignal cancellationSignal) { 541 if (throwIfUnsafe) { 542 throwIfNoTransaction(); 543 throwIfTransactionMarkedSuccessful(); 544 throwIfNestedTransaction(); 545 } else { 546 if (mTransactionStack == null || mTransactionStack.mMarkedSuccessful 547 || mTransactionStack.mParent != null) { 548 return false; 549 } 550 } 551 assert mConnection != null; 552 553 if (mTransactionStack.mChildFailed) { 554 return false; 555 } 556 557 return yieldTransactionUnchecked(sleepAfterYieldDelayMillis, 558 cancellationSignal); // might throw 559 } 560 yieldTransactionUnchecked(long sleepAfterYieldDelayMillis, CancellationSignal cancellationSignal)561 private boolean yieldTransactionUnchecked(long sleepAfterYieldDelayMillis, 562 CancellationSignal cancellationSignal) { 563 if (cancellationSignal != null) { 564 cancellationSignal.throwIfCanceled(); 565 } 566 567 if (!mConnectionPool.shouldYieldConnection(mConnection, mConnectionFlags)) { 568 return false; 569 } 570 571 final int transactionMode = mTransactionStack.mMode; 572 final SQLiteTransactionListener listener = mTransactionStack.mListener; 573 final int connectionFlags = mConnectionFlags; 574 endTransactionUnchecked(cancellationSignal, true); // might throw 575 576 if (sleepAfterYieldDelayMillis > 0) { 577 try { 578 Thread.sleep(sleepAfterYieldDelayMillis); 579 } catch (InterruptedException ex) { 580 // we have been interrupted, that's all we need to do 581 } 582 } 583 584 beginTransactionUnchecked(transactionMode, listener, connectionFlags, 585 cancellationSignal); // might throw 586 return true; 587 } 588 589 /** 590 * Prepares a statement for execution but does not bind its parameters or execute it. 591 * <p> 592 * This method can be used to check for syntax errors during compilation 593 * prior to execution of the statement. If the {@code outStatementInfo} argument 594 * is not null, the provided {@link SQLiteStatementInfo} object is populated 595 * with information about the statement. 596 * </p><p> 597 * A prepared statement makes no reference to the arguments that may eventually 598 * be bound to it, consequently it it possible to cache certain prepared statements 599 * such as SELECT or INSERT/UPDATE statements. If the statement is cacheable, 600 * then it will be stored in the cache for later and reused if possible. 601 * </p> 602 * 603 * @param sql The SQL statement to prepare. 604 * @param connectionFlags The connection flags to use if a connection must be 605 * acquired by this operation. Refer to {@link SQLiteConnectionPool}. 606 * @param cancellationSignal A signal to cancel the operation in progress, or null if none. 607 * @param outStatementInfo The {@link SQLiteStatementInfo} object to populate 608 * with information about the statement, or null if none. 609 * 610 * @throws SQLiteException if an error occurs, such as a syntax error. 611 * @throws OperationCanceledException if the operation was canceled. 612 */ prepare(String sql, int connectionFlags, CancellationSignal cancellationSignal, SQLiteStatementInfo outStatementInfo)613 public void prepare(String sql, int connectionFlags, CancellationSignal cancellationSignal, 614 SQLiteStatementInfo outStatementInfo) { 615 if (sql == null) { 616 throw new IllegalArgumentException("sql must not be null."); 617 } 618 619 if (cancellationSignal != null) { 620 cancellationSignal.throwIfCanceled(); 621 } 622 623 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw 624 try { 625 mConnection.prepare(sql, outStatementInfo); // might throw 626 } finally { 627 releaseConnection(); // might throw 628 } 629 } 630 631 /** 632 * Executes a statement that does not return a result. 633 * 634 * @param sql The SQL statement to execute. 635 * @param bindArgs The arguments to bind, or null if none. 636 * @param connectionFlags The connection flags to use if a connection must be 637 * acquired by this operation. Refer to {@link SQLiteConnectionPool}. 638 * @param cancellationSignal A signal to cancel the operation in progress, or null 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 */ execute(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)644 public void execute(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; 652 } 653 654 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw 655 try { 656 mConnection.execute(sql, bindArgs, cancellationSignal); // might throw 657 } finally { 658 releaseConnection(); // might throw 659 } 660 } 661 662 /** 663 * Executes a statement that returns a single <code>long</code> 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>long</code>, or zero 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 */ executeForLong(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)677 public long executeForLong(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 0; 685 } 686 687 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw 688 try { 689 return mConnection.executeForLong(sql, bindArgs, cancellationSignal); // might throw 690 } finally { 691 releaseConnection(); // might throw 692 } 693 } 694 695 /** 696 * Executes a statement that returns a single {@link String} result. 697 * 698 * @param sql The SQL statement to execute. 699 * @param bindArgs The arguments to bind, or null if none. 700 * @param connectionFlags The connection flags to use if a connection must be 701 * acquired by this operation. Refer to {@link SQLiteConnectionPool}. 702 * @param cancellationSignal A signal to cancel the operation in progress, or null if none. 703 * @return The value of the first column in the first row of the result set 704 * as a <code>String</code>, or null if none. 705 * 706 * @throws SQLiteException if an error occurs, such as a syntax error 707 * or invalid number of bind arguments. 708 * @throws OperationCanceledException if the operation was canceled. 709 */ executeForString(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)710 public String executeForString(String sql, Object[] bindArgs, int connectionFlags, 711 CancellationSignal cancellationSignal) { 712 if (sql == null) { 713 throw new IllegalArgumentException("sql must not be null."); 714 } 715 716 if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) { 717 return null; 718 } 719 720 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw 721 try { 722 return mConnection.executeForString(sql, bindArgs, cancellationSignal); // might throw 723 } finally { 724 releaseConnection(); // might throw 725 } 726 } 727 728 /** 729 * Executes a statement that returns a single BLOB result as a 730 * file descriptor to a shared memory region. 731 * 732 * @param sql The SQL statement to execute. 733 * @param bindArgs The arguments to bind, or null if none. 734 * @param connectionFlags The connection flags to use if a connection must be 735 * acquired by this operation. Refer to {@link SQLiteConnectionPool}. 736 * @param cancellationSignal A signal to cancel the operation in progress, or null if none. 737 * @return The file descriptor for a shared memory region that contains 738 * the value of the first column in the first row of the result set as a BLOB, 739 * or null if none. 740 * 741 * @throws SQLiteException if an error occurs, such as a syntax error 742 * or invalid number of bind arguments. 743 * @throws OperationCanceledException if the operation was canceled. 744 */ executeForBlobFileDescriptor(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)745 public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs, 746 int connectionFlags, CancellationSignal cancellationSignal) { 747 if (sql == null) { 748 throw new IllegalArgumentException("sql must not be null."); 749 } 750 751 if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) { 752 return null; 753 } 754 755 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw 756 try { 757 return mConnection.executeForBlobFileDescriptor(sql, bindArgs, 758 cancellationSignal); // might throw 759 } finally { 760 releaseConnection(); // might throw 761 } 762 } 763 764 /** 765 * Executes a statement that returns a count of the number of rows 766 * that were changed. Use for UPDATE or DELETE SQL statements. 767 * 768 * @param sql The SQL statement to execute. 769 * @param bindArgs The arguments to bind, or null if none. 770 * @param connectionFlags The connection flags to use if a connection must be 771 * acquired by this operation. Refer to {@link SQLiteConnectionPool}. 772 * @param cancellationSignal A signal to cancel the operation in progress, or null if none. 773 * @return The number of rows that were changed. 774 * 775 * @throws SQLiteException if an error occurs, such as a syntax error 776 * or invalid number of bind arguments. 777 * @throws OperationCanceledException if the operation was canceled. 778 */ executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)779 public int executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags, 780 CancellationSignal cancellationSignal) { 781 if (sql == null) { 782 throw new IllegalArgumentException("sql must not be null."); 783 } 784 785 if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) { 786 return 0; 787 } 788 789 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw 790 try { 791 return mConnection.executeForChangedRowCount(sql, bindArgs, 792 cancellationSignal); // might throw 793 } finally { 794 releaseConnection(); // might throw 795 } 796 } 797 798 /** 799 * Executes a statement that returns the row id of the last row inserted 800 * by the statement. Use for INSERT SQL statements. 801 * 802 * @param sql The SQL statement to execute. 803 * @param bindArgs The arguments to bind, or null if none. 804 * @param connectionFlags The connection flags to use if a connection must be 805 * acquired by this operation. Refer to {@link SQLiteConnectionPool}. 806 * @param cancellationSignal A signal to cancel the operation in progress, or null if none. 807 * @return The row id of the last row that was inserted, or 0 if none. 808 * 809 * @throws SQLiteException if an error occurs, such as a syntax error 810 * or invalid number of bind arguments. 811 * @throws OperationCanceledException if the operation was canceled. 812 */ executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)813 public long executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags, 814 CancellationSignal cancellationSignal) { 815 if (sql == null) { 816 throw new IllegalArgumentException("sql must not be null."); 817 } 818 819 if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) { 820 return 0; 821 } 822 823 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw 824 try { 825 return mConnection.executeForLastInsertedRowId(sql, bindArgs, 826 cancellationSignal); // might throw 827 } finally { 828 releaseConnection(); // might throw 829 } 830 } 831 832 /** 833 * Executes a statement and populates the specified {@link CursorWindow} 834 * with a range of results. Returns the number of rows that were counted 835 * during query execution. 836 * 837 * @param sql The SQL statement to execute. 838 * @param bindArgs The arguments to bind, or null if none. 839 * @param window The cursor window to clear and fill. 840 * @param startPos The start position for filling the window. 841 * @param requiredPos The position of a row that MUST be in the window. 842 * If it won't fit, then the query should discard part of what it filled 843 * so that it does. Must be greater than or equal to <code>startPos</code>. 844 * @param countAllRows True to count all rows that the query would return 845 * regagless of whether they fit in the window. 846 * @param connectionFlags The connection flags to use if a connection must be 847 * acquired by this operation. Refer to {@link SQLiteConnectionPool}. 848 * @param cancellationSignal A signal to cancel the operation in progress, or null if none. 849 * @return The number of rows that were counted during query execution. Might 850 * not be all rows in the result set unless <code>countAllRows</code> is true. 851 * 852 * @throws SQLiteException if an error occurs, such as a syntax error 853 * or invalid number of bind arguments. 854 * @throws OperationCanceledException if the operation was canceled. 855 */ executeForCursorWindow(String sql, Object[] bindArgs, CursorWindow window, int startPos, int requiredPos, boolean countAllRows, int connectionFlags, CancellationSignal cancellationSignal)856 public int executeForCursorWindow(String sql, Object[] bindArgs, 857 CursorWindow window, int startPos, int requiredPos, boolean countAllRows, 858 int connectionFlags, CancellationSignal cancellationSignal) { 859 if (sql == null) { 860 throw new IllegalArgumentException("sql must not be null."); 861 } 862 if (window == null) { 863 throw new IllegalArgumentException("window must not be null."); 864 } 865 866 if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) { 867 window.clear(); 868 return 0; 869 } 870 871 acquireConnection(sql, connectionFlags, cancellationSignal); // might throw 872 try { 873 return mConnection.executeForCursorWindow(sql, bindArgs, 874 window, startPos, requiredPos, countAllRows, 875 cancellationSignal); // might throw 876 } finally { 877 releaseConnection(); // might throw 878 } 879 } 880 881 /** 882 * Performs special reinterpretation of certain SQL statements such as "BEGIN", 883 * "COMMIT" and "ROLLBACK" to ensure that transaction state invariants are 884 * maintained. 885 * 886 * This function is mainly used to support legacy apps that perform their 887 * own transactions by executing raw SQL rather than calling {@link #beginTransaction} 888 * and the like. 889 * 890 * @param sql The SQL statement to execute. 891 * @param bindArgs The arguments to bind, or null if none. 892 * @param connectionFlags The connection flags to use if a connection must be 893 * acquired by this operation. Refer to {@link SQLiteConnectionPool}. 894 * @param cancellationSignal A signal to cancel the operation in progress, or null if none. 895 * @return True if the statement was of a special form that was handled here, 896 * false otherwise. 897 * 898 * @throws SQLiteException if an error occurs, such as a syntax error 899 * or invalid number of bind arguments. 900 * @throws OperationCanceledException if the operation was canceled. 901 */ executeSpecial(String sql, Object[] bindArgs, int connectionFlags, CancellationSignal cancellationSignal)902 private boolean executeSpecial(String sql, Object[] bindArgs, int connectionFlags, 903 CancellationSignal cancellationSignal) { 904 if (cancellationSignal != null) { 905 cancellationSignal.throwIfCanceled(); 906 } 907 908 final int type = DatabaseUtils.getSqlStatementType(sql); 909 switch (type) { 910 case DatabaseUtils.STATEMENT_BEGIN: 911 beginTransaction(TRANSACTION_MODE_EXCLUSIVE, null, connectionFlags, 912 cancellationSignal); 913 return true; 914 915 case DatabaseUtils.STATEMENT_COMMIT: 916 setTransactionSuccessful(); 917 endTransaction(cancellationSignal); 918 return true; 919 920 case DatabaseUtils.STATEMENT_ABORT: 921 endTransaction(cancellationSignal); 922 return true; 923 } 924 return false; 925 } 926 acquireConnection(String sql, int connectionFlags, CancellationSignal cancellationSignal)927 private void acquireConnection(String sql, int connectionFlags, 928 CancellationSignal cancellationSignal) { 929 if (mConnection == null) { 930 assert mConnectionUseCount == 0; 931 mConnection = mConnectionPool.acquireConnection(sql, connectionFlags, 932 cancellationSignal); // might throw 933 mConnectionFlags = connectionFlags; 934 } 935 mConnectionUseCount += 1; 936 } 937 releaseConnection()938 private void releaseConnection() { 939 assert mConnection != null; 940 assert mConnectionUseCount > 0; 941 if (--mConnectionUseCount == 0) { 942 try { 943 mConnectionPool.releaseConnection(mConnection); // might throw 944 } finally { 945 mConnection = null; 946 } 947 } 948 } 949 950 /** 951 * Acquire a prepared statement for external use. A current transaction is required and that 952 * transaction may not have been marked successful. The dependent is registered its close() 953 * method is called when the transaction is closed. 954 */ 955 @NonNull acquirePersistentStatement(@onNull String query, @NonNull Closeable dependent)956 SQLiteConnection.PreparedStatement acquirePersistentStatement(@NonNull String query, 957 @NonNull Closeable dependent) { 958 throwIfNoTransaction(); 959 throwIfTransactionMarkedSuccessful(); 960 mOpenDependents.addFirst(dependent); 961 try { 962 return mConnection.acquirePersistentStatement(query); 963 } catch (Throwable e) { 964 mOpenDependents.remove(dependent); 965 throw e; 966 } 967 } 968 969 /** 970 * Release a prepared statement. The dependent should be in list of open dependents. 971 */ releasePersistentStatement(@onNull SQLiteConnection.PreparedStatement statement, @NonNull Closeable dependent)972 void releasePersistentStatement(@NonNull SQLiteConnection.PreparedStatement statement, 973 @NonNull Closeable dependent) { 974 mConnection.releasePreparedStatement(statement); 975 mOpenDependents.remove(dependent); 976 } 977 978 /** 979 * Close any open dependents. This may be called multiple times without harm. It never 980 * throws. 981 */ closeOpenDependents()982 void closeOpenDependents() { 983 while (mOpenDependents.size() > 0) { 984 final Closeable dependent = mOpenDependents.pollFirst(); 985 if (dependent != null) 986 try { 987 dependent.close(); 988 } catch (IOException e) { 989 // Swallow the exception. 990 } 991 } 992 } 993 994 /** 995 * Return the row ID of the last row to be inserted on this connection. Note that the last row 996 * might not have been inserted on this particular statement, but the return value is the last 997 * row inserted on the same connection as that used by this statement. The function checks that 998 * it is currently in a transaction before executing. Because of this check, it is not 999 * necessary to acquire and release the connection: the connection has already been acquired. 1000 * @hide 1001 */ getLastInsertRowId()1002 long getLastInsertRowId() { 1003 throwIfNoTransaction(); 1004 return mConnection.getLastInsertRowId(); 1005 } 1006 1007 /** 1008 * Return the number of database rows that were changed by the most recent SQL statement on 1009 * this connection. 1010 * @hide 1011 */ getLastChangedRowCount()1012 long getLastChangedRowCount() { 1013 throwIfNoTransaction(); 1014 return mConnection.getLastChangedRowCount(); 1015 } 1016 1017 /** 1018 * Return the total number of database rows that were changed on the current connection, since 1019 * it was created. 1020 * @hide 1021 */ getTotalChangedRowCount()1022 long getTotalChangedRowCount() { 1023 throwIfNoTransaction(); 1024 return mConnection.getTotalChangedRowCount(); 1025 } 1026 1027 /** 1028 * Throw {@link IllegalStateException} if there is no current transaction. 1029 */ throwIfNoTransaction()1030 void throwIfNoTransaction() { 1031 if (mTransactionStack == null) { 1032 throw new IllegalStateException("Cannot perform this operation because " 1033 + "there is no current transaction."); 1034 } 1035 } 1036 throwIfTransactionMarkedSuccessful()1037 private void throwIfTransactionMarkedSuccessful() { 1038 if (mTransactionStack != null && mTransactionStack.mMarkedSuccessful) { 1039 throw new IllegalStateException("Cannot perform this operation because " 1040 + "the transaction has already been marked successful. The only " 1041 + "thing you can do now is call endTransaction()."); 1042 } 1043 } 1044 throwIfNestedTransaction()1045 private void throwIfNestedTransaction() { 1046 if (hasNestedTransaction()) { 1047 throw new IllegalStateException("Cannot perform this operation because " 1048 + "a nested transaction is in progress."); 1049 } 1050 } 1051 obtainTransaction(int mode, SQLiteTransactionListener listener)1052 private Transaction obtainTransaction(int mode, SQLiteTransactionListener listener) { 1053 Transaction transaction = mTransactionPool; 1054 if (transaction != null) { 1055 mTransactionPool = transaction.mParent; 1056 transaction.mParent = null; 1057 transaction.mMarkedSuccessful = false; 1058 transaction.mChildFailed = false; 1059 } else { 1060 transaction = new Transaction(); 1061 } 1062 transaction.mMode = mode; 1063 transaction.mListener = listener; 1064 return transaction; 1065 } 1066 recycleTransaction(Transaction transaction)1067 private void recycleTransaction(Transaction transaction) { 1068 transaction.mParent = mTransactionPool; 1069 transaction.mListener = null; 1070 mTransactionPool = transaction; 1071 } 1072 1073 private static final class Transaction { 1074 public Transaction mParent; 1075 public int mMode; 1076 public SQLiteTransactionListener mListener; 1077 public boolean mMarkedSuccessful; 1078 public boolean mChildFailed; 1079 } 1080 } 1081