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