| 1 | /* |
| 2 | * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in the |
| 12 | * documentation and/or other materials provided with the distribution. |
| 13 | * 3. Neither the name of Apple Inc. ("Apple") nor the names of |
| 14 | * its contributors may be used to endorse or promote products derived |
| 15 | * from this software without specific prior written permission. |
| 16 | * |
| 17 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| 18 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 20 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | */ |
| 28 | |
| 29 | #include "config.h" |
| 30 | #include "SQLTransaction.h" |
| 31 | |
| 32 | #include "Database.h" |
| 33 | #include "DatabaseAuthorizer.h" |
| 34 | #include "DatabaseContext.h" |
| 35 | #include "DatabaseThread.h" |
| 36 | #include "DatabaseTracker.h" |
| 37 | #include "Logging.h" |
| 38 | #include "OriginLock.h" |
| 39 | #include "SQLError.h" |
| 40 | #include "SQLStatement.h" |
| 41 | #include "SQLStatementCallback.h" |
| 42 | #include "SQLStatementErrorCallback.h" |
| 43 | #include "SQLTransactionBackend.h" |
| 44 | #include "SQLTransactionCallback.h" |
| 45 | #include "SQLTransactionCoordinator.h" |
| 46 | #include "SQLTransactionErrorCallback.h" |
| 47 | #include "SQLiteTransaction.h" |
| 48 | #include "VoidCallback.h" |
| 49 | #include <wtf/Optional.h> |
| 50 | #include <wtf/StdLibExtras.h> |
| 51 | #include <wtf/Vector.h> |
| 52 | |
| 53 | namespace WebCore { |
| 54 | |
| 55 | Ref<SQLTransaction> SQLTransaction::create(Ref<Database>&& database, RefPtr<SQLTransactionCallback>&& callback, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<SQLTransactionWrapper>&& wrapper, bool readOnly) |
| 56 | { |
| 57 | return adoptRef(*new SQLTransaction(WTFMove(database), WTFMove(callback), WTFMove(successCallback), WTFMove(errorCallback), WTFMove(wrapper), readOnly)); |
| 58 | } |
| 59 | |
| 60 | SQLTransaction::SQLTransaction(Ref<Database>&& database, RefPtr<SQLTransactionCallback>&& callback, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<SQLTransactionWrapper>&& wrapper, bool readOnly) |
| 61 | : m_database(WTFMove(database)) |
| 62 | , m_callbackWrapper(WTFMove(callback), &m_database->scriptExecutionContext()) |
| 63 | , m_successCallbackWrapper(WTFMove(successCallback), &m_database->scriptExecutionContext()) |
| 64 | , m_errorCallbackWrapper(WTFMove(errorCallback), &m_database->scriptExecutionContext()) |
| 65 | , m_wrapper(WTFMove(wrapper)) |
| 66 | , m_nextStep(&SQLTransaction::acquireLock) |
| 67 | , m_readOnly(readOnly) |
| 68 | , m_backend(*this) |
| 69 | { |
| 70 | } |
| 71 | |
| 72 | SQLTransaction::~SQLTransaction() = default; |
| 73 | |
| 74 | ExceptionOr<void> SQLTransaction::executeSql(const String& sqlStatement, Optional<Vector<SQLValue>>&& arguments, RefPtr<SQLStatementCallback>&& callback, RefPtr<SQLStatementErrorCallback>&& callbackError) |
| 75 | { |
| 76 | if (!m_executeSqlAllowed || !m_database->opened()) |
| 77 | return Exception { InvalidStateError }; |
| 78 | |
| 79 | int permissions = DatabaseAuthorizer::ReadWriteMask; |
| 80 | if (!m_database->databaseContext().allowDatabaseAccess()) |
| 81 | permissions |= DatabaseAuthorizer::NoAccessMask; |
| 82 | else if (m_readOnly) |
| 83 | permissions |= DatabaseAuthorizer::ReadOnlyMask; |
| 84 | |
| 85 | auto statement = std::make_unique<SQLStatement>(m_database, sqlStatement, arguments.valueOr(Vector<SQLValue> { }), WTFMove(callback), WTFMove(callbackError), permissions); |
| 86 | |
| 87 | if (m_database->deleted()) |
| 88 | statement->setDatabaseDeletedError(); |
| 89 | |
| 90 | enqueueStatement(WTFMove(statement)); |
| 91 | |
| 92 | return { }; |
| 93 | } |
| 94 | |
| 95 | void SQLTransaction::lockAcquired() |
| 96 | { |
| 97 | m_lockAcquired = true; |
| 98 | |
| 99 | m_backend.m_requestedState = SQLTransactionState::OpenTransactionAndPreflight; |
| 100 | m_database->scheduleTransactionStep(*this); |
| 101 | } |
| 102 | |
| 103 | void SQLTransaction::performNextStep() |
| 104 | { |
| 105 | m_backend.computeNextStateAndCleanupIfNeeded(); |
| 106 | m_backend.runStateMachine(); |
| 107 | } |
| 108 | |
| 109 | void SQLTransaction::performPendingCallback() |
| 110 | { |
| 111 | LOG(StorageAPI, "Callback %s\n" , debugStepName(m_nextStep)); |
| 112 | |
| 113 | ASSERT(m_nextStep == &SQLTransaction::deliverTransactionCallback |
| 114 | || m_nextStep == &SQLTransaction::deliverTransactionErrorCallback |
| 115 | || m_nextStep == &SQLTransaction::deliverStatementCallback |
| 116 | || m_nextStep == &SQLTransaction::deliverQuotaIncreaseCallback |
| 117 | || m_nextStep == &SQLTransaction::deliverSuccessCallback); |
| 118 | |
| 119 | checkAndHandleClosedDatabase(); |
| 120 | |
| 121 | if (m_nextStep) |
| 122 | (this->*m_nextStep)(); |
| 123 | } |
| 124 | |
| 125 | void SQLTransaction::notifyDatabaseThreadIsShuttingDown() |
| 126 | { |
| 127 | m_backend.notifyDatabaseThreadIsShuttingDown(); |
| 128 | } |
| 129 | |
| 130 | void SQLTransaction::enqueueStatement(std::unique_ptr<SQLStatement> statement) |
| 131 | { |
| 132 | LockHolder locker(m_statementMutex); |
| 133 | m_statementQueue.append(WTFMove(statement)); |
| 134 | } |
| 135 | |
| 136 | SQLTransaction::StateFunction SQLTransaction::stateFunctionFor(SQLTransactionState state) |
| 137 | { |
| 138 | static const StateFunction stateFunctions[] = { |
| 139 | &SQLTransaction::unreachableState, // 0. illegal |
| 140 | &SQLTransaction::unreachableState, // 1. idle |
| 141 | &SQLTransaction::unreachableState, // 2. acquireLock |
| 142 | &SQLTransaction::unreachableState, // 3. openTransactionAndPreflight |
| 143 | &SQLTransaction::unreachableState, // 4. runStatements |
| 144 | &SQLTransaction::unreachableState, // 5. postflightAndCommit |
| 145 | &SQLTransaction::unreachableState, // 6. cleanupAndTerminate |
| 146 | &SQLTransaction::unreachableState, // 7. cleanupAfterTransactionErrorCallback |
| 147 | &SQLTransaction::deliverTransactionCallback, // 8. |
| 148 | &SQLTransaction::deliverTransactionErrorCallback, // 9. |
| 149 | &SQLTransaction::deliverStatementCallback, // 10. |
| 150 | &SQLTransaction::deliverQuotaIncreaseCallback, // 11. |
| 151 | &SQLTransaction::deliverSuccessCallback // 12. |
| 152 | }; |
| 153 | |
| 154 | ASSERT(WTF_ARRAY_LENGTH(stateFunctions) == static_cast<int>(SQLTransactionState::NumberOfStates)); |
| 155 | ASSERT(state < SQLTransactionState::NumberOfStates); |
| 156 | |
| 157 | return stateFunctions[static_cast<int>(state)]; |
| 158 | } |
| 159 | |
| 160 | // requestTransitToState() can be called from the backend. Hence, it should |
| 161 | // NOT be modifying SQLTransactionBackend in general. The only safe field to |
| 162 | // modify is m_requestedState which is meant for this purpose. |
| 163 | void SQLTransaction::requestTransitToState(SQLTransactionState nextState) |
| 164 | { |
| 165 | LOG(StorageAPI, "Scheduling %s for transaction %p\n" , nameForSQLTransactionState(nextState), this); |
| 166 | m_requestedState = nextState; |
| 167 | m_database->scheduleTransactionCallback(this); |
| 168 | } |
| 169 | |
| 170 | void SQLTransaction::checkAndHandleClosedDatabase() |
| 171 | { |
| 172 | if (m_database->opened()) |
| 173 | return; |
| 174 | |
| 175 | // If the database was stopped, don't do anything and cancel queued work |
| 176 | LOG(StorageAPI, "Database was stopped or interrupted - cancelling work for this transaction" ); |
| 177 | |
| 178 | LockHolder locker(m_statementMutex); |
| 179 | m_statementQueue.clear(); |
| 180 | m_nextStep = nullptr; |
| 181 | |
| 182 | // Release the unneeded callbacks, to break reference cycles. |
| 183 | m_callbackWrapper.clear(); |
| 184 | m_successCallbackWrapper.clear(); |
| 185 | m_errorCallbackWrapper.clear(); |
| 186 | |
| 187 | // The next steps should be executed only if we're on the DB thread. |
| 188 | if (m_database->databaseThread().getThread() != &Thread::current()) |
| 189 | return; |
| 190 | |
| 191 | // The current SQLite transaction should be stopped, as well |
| 192 | if (m_sqliteTransaction) { |
| 193 | m_sqliteTransaction->stop(); |
| 194 | m_sqliteTransaction = nullptr; |
| 195 | } |
| 196 | |
| 197 | if (m_lockAcquired) |
| 198 | m_database->transactionCoordinator()->releaseLock(*this); |
| 199 | } |
| 200 | |
| 201 | void SQLTransaction::scheduleCallback(void (SQLTransaction::*step)()) |
| 202 | { |
| 203 | m_nextStep = step; |
| 204 | |
| 205 | LOG(StorageAPI, "Scheduling %s for transaction %p\n" , debugStepName(step), this); |
| 206 | m_database->scheduleTransactionCallback(this); |
| 207 | } |
| 208 | |
| 209 | void SQLTransaction::acquireLock() |
| 210 | { |
| 211 | m_database->transactionCoordinator()->acquireLock(*this); |
| 212 | } |
| 213 | |
| 214 | void SQLTransaction::openTransactionAndPreflight() |
| 215 | { |
| 216 | ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
| 217 | ASSERT(m_lockAcquired); |
| 218 | |
| 219 | LOG(StorageAPI, "Opening and preflighting transaction %p" , this); |
| 220 | |
| 221 | // If the database was deleted, jump to the error callback |
| 222 | if (m_database->deleted()) { |
| 223 | m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to open a transaction, because the user deleted the database" ); |
| 224 | |
| 225 | handleTransactionError(); |
| 226 | return; |
| 227 | } |
| 228 | |
| 229 | // Set the maximum usage for this transaction if this transactions is not read-only |
| 230 | if (!m_readOnly) { |
| 231 | acquireOriginLock(); |
| 232 | m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); |
| 233 | } |
| 234 | |
| 235 | ASSERT(!m_sqliteTransaction); |
| 236 | m_sqliteTransaction = std::make_unique<SQLiteTransaction>(m_database->sqliteDatabase(), m_readOnly); |
| 237 | |
| 238 | m_database->resetDeletes(); |
| 239 | m_database->disableAuthorizer(); |
| 240 | m_sqliteTransaction->begin(); |
| 241 | m_database->enableAuthorizer(); |
| 242 | |
| 243 | // Spec 4.3.2.1+2: Open a transaction to the database, jumping to the error callback if that fails |
| 244 | if (!m_sqliteTransaction->inProgress()) { |
| 245 | ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
| 246 | m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to begin transaction" , m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); |
| 247 | m_sqliteTransaction = nullptr; |
| 248 | |
| 249 | handleTransactionError(); |
| 250 | return; |
| 251 | } |
| 252 | |
| 253 | // Note: We intentionally retrieve the actual version even with an empty expected version. |
| 254 | // In multi-process browsers, we take this opportinutiy to update the cached value for |
| 255 | // the actual version. In single-process browsers, this is just a map lookup. |
| 256 | String actualVersion; |
| 257 | if (!m_database->getActualVersionForTransaction(actualVersion)) { |
| 258 | m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to read version" , m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); |
| 259 | m_database->disableAuthorizer(); |
| 260 | m_sqliteTransaction = nullptr; |
| 261 | m_database->enableAuthorizer(); |
| 262 | |
| 263 | handleTransactionError(); |
| 264 | return; |
| 265 | } |
| 266 | |
| 267 | m_hasVersionMismatch = !m_database->expectedVersion().isEmpty() && (m_database->expectedVersion() != actualVersion); |
| 268 | |
| 269 | // Spec 4.3.2.3: Perform preflight steps, jumping to the error callback if they fail |
| 270 | if (m_wrapper && !m_wrapper->performPreflight(*this)) { |
| 271 | m_database->disableAuthorizer(); |
| 272 | m_sqliteTransaction = nullptr; |
| 273 | m_database->enableAuthorizer(); |
| 274 | m_transactionError = m_wrapper->sqlError(); |
| 275 | if (!m_transactionError) |
| 276 | m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction preflight" ); |
| 277 | |
| 278 | handleTransactionError(); |
| 279 | return; |
| 280 | } |
| 281 | |
| 282 | // Spec 4.3.2.4: Invoke the transaction callback with the new SQLTransaction object |
| 283 | if (m_callbackWrapper.hasCallback()) { |
| 284 | scheduleCallback(&SQLTransaction::deliverTransactionCallback); |
| 285 | return; |
| 286 | } |
| 287 | |
| 288 | // If we have no callback to make, skip pass to the state after: |
| 289 | runStatements(); |
| 290 | } |
| 291 | |
| 292 | void SQLTransaction::runStatements() |
| 293 | { |
| 294 | ASSERT(m_lockAcquired); |
| 295 | |
| 296 | // If there is a series of statements queued up that are all successful and have no associated |
| 297 | // SQLStatementCallback objects, then we can burn through the queue |
| 298 | do { |
| 299 | if (m_shouldRetryCurrentStatement && !m_sqliteTransaction->wasRolledBackBySqlite()) { |
| 300 | m_shouldRetryCurrentStatement = false; |
| 301 | // FIXME - Another place that needs fixing up after <rdar://problem/5628468> is addressed. |
| 302 | // See ::openTransactionAndPreflight() for discussion |
| 303 | |
| 304 | // Reset the maximum size here, as it was increased to allow us to retry this statement. |
| 305 | // m_shouldRetryCurrentStatement is set to true only when a statement exceeds |
| 306 | // the quota, which can happen only in a read-write transaction. Therefore, there |
| 307 | // is no need to check here if the transaction is read-write. |
| 308 | m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); |
| 309 | } else { |
| 310 | // If the current statement has already been run, failed due to quota constraints, and we're not retrying it, |
| 311 | // that means it ended in an error. Handle it now |
| 312 | if (m_currentStatement && m_currentStatement->lastExecutionFailedDueToQuota()) { |
| 313 | handleCurrentStatementError(); |
| 314 | break; |
| 315 | } |
| 316 | |
| 317 | // Otherwise, advance to the next statement |
| 318 | getNextStatement(); |
| 319 | } |
| 320 | } while (runCurrentStatement()); |
| 321 | |
| 322 | // If runCurrentStatement() returned false, that means either there was no current statement to run, |
| 323 | // or the current statement requires a callback to complete. In the later case, it also scheduled |
| 324 | // the callback or performed any other additional work so we can return. |
| 325 | if (!m_currentStatement) |
| 326 | postflightAndCommit(); |
| 327 | } |
| 328 | |
| 329 | void SQLTransaction::cleanupAndTerminate() |
| 330 | { |
| 331 | ASSERT(m_lockAcquired); |
| 332 | |
| 333 | // Spec 4.3.2.9: End transaction steps. There is no next step. |
| 334 | LOG(StorageAPI, "Transaction %p is complete\n" , this); |
| 335 | ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
| 336 | |
| 337 | // Phase 5 cleanup. See comment on the SQLTransaction life-cycle above. |
| 338 | m_backend.doCleanup(); |
| 339 | m_database->inProgressTransactionCompleted(); |
| 340 | } |
| 341 | |
| 342 | void SQLTransaction::cleanupAfterTransactionErrorCallback() |
| 343 | { |
| 344 | ASSERT(m_lockAcquired); |
| 345 | |
| 346 | LOG(StorageAPI, "Transaction %p is complete with an error\n" , this); |
| 347 | m_database->disableAuthorizer(); |
| 348 | if (m_sqliteTransaction) { |
| 349 | // Spec 4.3.2.10: Rollback the transaction. |
| 350 | m_sqliteTransaction->rollback(); |
| 351 | |
| 352 | ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
| 353 | m_sqliteTransaction = nullptr; |
| 354 | } |
| 355 | m_database->enableAuthorizer(); |
| 356 | |
| 357 | releaseOriginLockIfNeeded(); |
| 358 | |
| 359 | ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
| 360 | |
| 361 | cleanupAndTerminate(); |
| 362 | } |
| 363 | |
| 364 | void SQLTransaction::deliverTransactionCallback() |
| 365 | { |
| 366 | bool shouldDeliverErrorCallback = false; |
| 367 | |
| 368 | // Spec 4.3.2 4: Invoke the transaction callback with the new SQLTransaction object |
| 369 | RefPtr<SQLTransactionCallback> callback = m_callbackWrapper.unwrap(); |
| 370 | if (callback) { |
| 371 | m_executeSqlAllowed = true; |
| 372 | |
| 373 | auto result = callback->handleEvent(*this); |
| 374 | shouldDeliverErrorCallback = result.type() == CallbackResultType::ExceptionThrown; |
| 375 | |
| 376 | m_executeSqlAllowed = false; |
| 377 | } |
| 378 | |
| 379 | // Spec 4.3.2 5: If the transaction callback was null or raised an exception, jump to the error callback |
| 380 | if (shouldDeliverErrorCallback) { |
| 381 | m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the SQLTransactionCallback was null or threw an exception" ); |
| 382 | return deliverTransactionErrorCallback(); |
| 383 | } |
| 384 | |
| 385 | m_backend.requestTransitToState(SQLTransactionState::RunStatements); |
| 386 | } |
| 387 | |
| 388 | void SQLTransaction::deliverTransactionErrorCallback() |
| 389 | { |
| 390 | ASSERT(m_transactionError); |
| 391 | |
| 392 | // Spec 4.3.2.10: If exists, invoke error callback with the last |
| 393 | // error to have occurred in this transaction. |
| 394 | RefPtr<SQLTransactionErrorCallback> errorCallback = m_errorCallbackWrapper.unwrap(); |
| 395 | if (errorCallback) |
| 396 | errorCallback->handleEvent(*m_transactionError); |
| 397 | |
| 398 | clearCallbackWrappers(); |
| 399 | |
| 400 | // Spec 4.3.2.10: Rollback the transaction. |
| 401 | m_backend.requestTransitToState(SQLTransactionState::CleanupAfterTransactionErrorCallback); |
| 402 | } |
| 403 | |
| 404 | void SQLTransaction::deliverStatementCallback() |
| 405 | { |
| 406 | ASSERT(m_currentStatement); |
| 407 | |
| 408 | // Spec 4.3.2.6.6 and 4.3.2.6.3: If the statement callback went wrong, jump to the transaction error callback |
| 409 | // Otherwise, continue to loop through the statement queue |
| 410 | m_executeSqlAllowed = true; |
| 411 | bool result = m_currentStatement->performCallback(*this); |
| 412 | m_executeSqlAllowed = false; |
| 413 | |
| 414 | if (result) { |
| 415 | m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the statement callback raised an exception or statement error callback did not return false" ); |
| 416 | |
| 417 | if (m_errorCallbackWrapper.hasCallback()) |
| 418 | return deliverTransactionErrorCallback(); |
| 419 | |
| 420 | // No error callback, so fast-forward to: |
| 421 | // Transaction Step 11 - Rollback the transaction. |
| 422 | m_backend.requestTransitToState(SQLTransactionState::CleanupAfterTransactionErrorCallback); |
| 423 | return; |
| 424 | } |
| 425 | |
| 426 | m_backend.requestTransitToState(SQLTransactionState::RunStatements); |
| 427 | } |
| 428 | |
| 429 | void SQLTransaction::deliverQuotaIncreaseCallback() |
| 430 | { |
| 431 | ASSERT(m_currentStatement); |
| 432 | ASSERT(!m_shouldRetryCurrentStatement); |
| 433 | |
| 434 | m_shouldRetryCurrentStatement = m_database->didExceedQuota(); |
| 435 | |
| 436 | m_backend.requestTransitToState(SQLTransactionState::RunStatements); |
| 437 | } |
| 438 | |
| 439 | void SQLTransaction::deliverSuccessCallback() |
| 440 | { |
| 441 | // Spec 4.3.2.8: Deliver success callback. |
| 442 | RefPtr<VoidCallback> successCallback = m_successCallbackWrapper.unwrap(); |
| 443 | if (successCallback) |
| 444 | successCallback->handleEvent(); |
| 445 | |
| 446 | clearCallbackWrappers(); |
| 447 | |
| 448 | // Schedule a "post-success callback" step to return control to the database thread in case there |
| 449 | // are further transactions queued up for this Database |
| 450 | m_backend.requestTransitToState(SQLTransactionState::CleanupAndTerminate); |
| 451 | } |
| 452 | |
| 453 | // This state function is used as a stub function to plug unimplemented states |
| 454 | // in the state dispatch table. They are unimplemented because they should |
| 455 | // never be reached in the course of correct execution. |
| 456 | void SQLTransaction::unreachableState() |
| 457 | { |
| 458 | ASSERT_NOT_REACHED(); |
| 459 | } |
| 460 | |
| 461 | void SQLTransaction::computeNextStateAndCleanupIfNeeded() |
| 462 | { |
| 463 | // Only honor the requested state transition if we're not supposed to be |
| 464 | // cleaning up and shutting down: |
| 465 | if (m_database->opened()) { |
| 466 | setStateToRequestedState(); |
| 467 | ASSERT(m_nextState == SQLTransactionState::End |
| 468 | || m_nextState == SQLTransactionState::DeliverTransactionCallback |
| 469 | || m_nextState == SQLTransactionState::DeliverTransactionErrorCallback |
| 470 | || m_nextState == SQLTransactionState::DeliverStatementCallback |
| 471 | || m_nextState == SQLTransactionState::DeliverQuotaIncreaseCallback |
| 472 | || m_nextState == SQLTransactionState::DeliverSuccessCallback); |
| 473 | |
| 474 | LOG(StorageAPI, "Callback %s\n" , nameForSQLTransactionState(m_nextState)); |
| 475 | return; |
| 476 | } |
| 477 | |
| 478 | clearCallbackWrappers(); |
| 479 | m_backend.requestTransitToState(SQLTransactionState::CleanupAndTerminate); |
| 480 | } |
| 481 | |
| 482 | void SQLTransaction::clearCallbackWrappers() |
| 483 | { |
| 484 | // Release the unneeded callbacks, to break reference cycles. |
| 485 | m_callbackWrapper.clear(); |
| 486 | m_successCallbackWrapper.clear(); |
| 487 | m_errorCallbackWrapper.clear(); |
| 488 | } |
| 489 | |
| 490 | void SQLTransaction::getNextStatement() |
| 491 | { |
| 492 | m_currentStatement = nullptr; |
| 493 | |
| 494 | LockHolder locker(m_statementMutex); |
| 495 | if (!m_statementQueue.isEmpty()) |
| 496 | m_currentStatement = m_statementQueue.takeFirst(); |
| 497 | } |
| 498 | |
| 499 | bool SQLTransaction::runCurrentStatement() |
| 500 | { |
| 501 | if (!m_currentStatement) { |
| 502 | // No more statements to run. So move on to the next state. |
| 503 | return false; |
| 504 | } |
| 505 | |
| 506 | m_database->resetAuthorizer(); |
| 507 | |
| 508 | if (m_hasVersionMismatch) |
| 509 | m_currentStatement->setVersionMismatchedError(); |
| 510 | |
| 511 | if (m_currentStatement->execute(m_database)) { |
| 512 | if (m_database->lastActionChangedDatabase()) { |
| 513 | // Flag this transaction as having changed the database for later delegate notification |
| 514 | m_modifiedDatabase = true; |
| 515 | } |
| 516 | |
| 517 | if (m_currentStatement->hasStatementCallback()) { |
| 518 | scheduleCallback(&SQLTransaction::deliverStatementCallback); |
| 519 | return false; |
| 520 | } |
| 521 | |
| 522 | // If we get here, then the statement doesn't have a callback to invoke. |
| 523 | // We can move on to the next statement. Hence, stay in this state. |
| 524 | return true; |
| 525 | } |
| 526 | |
| 527 | if (m_currentStatement->lastExecutionFailedDueToQuota()) { |
| 528 | scheduleCallback(&SQLTransaction::deliverQuotaIncreaseCallback); |
| 529 | return false; |
| 530 | } |
| 531 | |
| 532 | handleCurrentStatementError(); |
| 533 | return false; |
| 534 | } |
| 535 | |
| 536 | void SQLTransaction::handleCurrentStatementError() |
| 537 | { |
| 538 | // Spec 4.3.2.6.6: error - Call the statement's error callback, but if there was no error callback, |
| 539 | // or the transaction was rolled back, jump to the transaction error callback |
| 540 | if (m_currentStatement->hasStatementErrorCallback() && !m_sqliteTransaction->wasRolledBackBySqlite()) { |
| 541 | scheduleCallback(&SQLTransaction::deliverStatementCallback); |
| 542 | return; |
| 543 | } |
| 544 | |
| 545 | m_transactionError = m_currentStatement->sqlError(); |
| 546 | if (!m_transactionError) |
| 547 | m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "the statement failed to execute" ); |
| 548 | |
| 549 | handleTransactionError(); |
| 550 | } |
| 551 | |
| 552 | void SQLTransaction::handleTransactionError() |
| 553 | { |
| 554 | ASSERT(m_transactionError); |
| 555 | if (m_errorCallbackWrapper.hasCallback()) { |
| 556 | scheduleCallback(&SQLTransaction::deliverTransactionErrorCallback); |
| 557 | return; |
| 558 | } |
| 559 | |
| 560 | // No error callback, so fast-forward to the next state and rollback the |
| 561 | // transaction. |
| 562 | m_backend.cleanupAfterTransactionErrorCallback(); |
| 563 | } |
| 564 | |
| 565 | void SQLTransaction::postflightAndCommit() |
| 566 | { |
| 567 | ASSERT(m_lockAcquired); |
| 568 | |
| 569 | // Spec 4.3.2.7: Perform postflight steps, jumping to the error callback if they fail. |
| 570 | if (m_wrapper && !m_wrapper->performPostflight(*this)) { |
| 571 | m_transactionError = m_wrapper->sqlError(); |
| 572 | if (!m_transactionError) |
| 573 | m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction postflight" ); |
| 574 | |
| 575 | handleTransactionError(); |
| 576 | return; |
| 577 | } |
| 578 | |
| 579 | // Spec 4.3.2.7: Commit the transaction, jumping to the error callback if that fails. |
| 580 | ASSERT(m_sqliteTransaction); |
| 581 | |
| 582 | m_database->disableAuthorizer(); |
| 583 | m_sqliteTransaction->commit(); |
| 584 | m_database->enableAuthorizer(); |
| 585 | |
| 586 | releaseOriginLockIfNeeded(); |
| 587 | |
| 588 | // If the commit failed, the transaction will still be marked as "in progress" |
| 589 | if (m_sqliteTransaction->inProgress()) { |
| 590 | if (m_wrapper) |
| 591 | m_wrapper->handleCommitFailedAfterPostflight(*this); |
| 592 | m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to commit transaction" , m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); |
| 593 | |
| 594 | handleTransactionError(); |
| 595 | return; |
| 596 | } |
| 597 | |
| 598 | // Vacuum the database if anything was deleted. |
| 599 | if (m_database->hadDeletes()) |
| 600 | m_database->incrementalVacuumIfNeeded(); |
| 601 | |
| 602 | // The commit was successful. If the transaction modified this database, notify the delegates. |
| 603 | if (m_modifiedDatabase) |
| 604 | m_database->didCommitWriteTransaction(); |
| 605 | |
| 606 | // Spec 4.3.2.8: Deliver success callback, if there is one. |
| 607 | scheduleCallback(&SQLTransaction::deliverSuccessCallback); |
| 608 | } |
| 609 | |
| 610 | void SQLTransaction::acquireOriginLock() |
| 611 | { |
| 612 | ASSERT(!m_originLock); |
| 613 | m_originLock = DatabaseTracker::singleton().originLockFor(m_database->securityOrigin()); |
| 614 | m_originLock->lock(); |
| 615 | } |
| 616 | |
| 617 | void SQLTransaction::releaseOriginLockIfNeeded() |
| 618 | { |
| 619 | if (m_originLock) { |
| 620 | m_originLock->unlock(); |
| 621 | m_originLock = nullptr; |
| 622 | } |
| 623 | } |
| 624 | |
| 625 | #if !LOG_DISABLED |
| 626 | const char* SQLTransaction::debugStepName(void (SQLTransaction::*step)()) |
| 627 | { |
| 628 | if (step == &SQLTransaction::acquireLock) |
| 629 | return "acquireLock" ; |
| 630 | if (step == &SQLTransaction::openTransactionAndPreflight) |
| 631 | return "openTransactionAndPreflight" ; |
| 632 | if (step == &SQLTransaction::runStatements) |
| 633 | return "runStatements" ; |
| 634 | if (step == &SQLTransaction::postflightAndCommit) |
| 635 | return "postflightAndCommit" ; |
| 636 | if (step == &SQLTransaction::cleanupAfterTransactionErrorCallback) |
| 637 | return "cleanupAfterTransactionErrorCallback" ; |
| 638 | if (step == &SQLTransaction::deliverTransactionCallback) |
| 639 | return "deliverTransactionCallback" ; |
| 640 | if (step == &SQLTransaction::deliverTransactionErrorCallback) |
| 641 | return "deliverTransactionErrorCallback" ; |
| 642 | if (step == &SQLTransaction::deliverStatementCallback) |
| 643 | return "deliverStatementCallback" ; |
| 644 | if (step == &SQLTransaction::deliverQuotaIncreaseCallback) |
| 645 | return "deliverQuotaIncreaseCallback" ; |
| 646 | if (step == &SQLTransaction::deliverSuccessCallback) |
| 647 | return "deliverSuccessCallback" ; |
| 648 | |
| 649 | ASSERT_NOT_REACHED(); |
| 650 | return "UNKNOWN" ; |
| 651 | } |
| 652 | #endif |
| 653 | |
| 654 | } // namespace WebCore |
| 655 | |