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