1/* 2 * Copyright (C) 2007, 2008 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 Computer, 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#if ENABLE(DATABASE) 33 34#include "Database.h" 35#include "DatabaseAuthorizer.h" 36#include "DatabaseThread.h" 37#include "Logging.h" 38#include "PlatformString.h" 39#include "ScriptExecutionContext.h" 40#include "SQLError.h" 41#include "SQLiteTransaction.h" 42#include "SQLStatement.h" 43#include "SQLStatementCallback.h" 44#include "SQLStatementErrorCallback.h" 45#include "SQLTransactionCallback.h" 46#include "SQLTransactionClient.h" 47#include "SQLTransactionCoordinator.h" 48#include "SQLTransactionErrorCallback.h" 49#include "SQLValue.h" 50#include "VoidCallback.h" 51#include <wtf/OwnPtr.h> 52#include <wtf/PassRefPtr.h> 53#include <wtf/RefPtr.h> 54 55// There's no way of knowing exactly how much more space will be required when a statement hits the quota limit. 56// For now, we'll arbitrarily choose currentQuota + 1mb. 57// In the future we decide to track if a size increase wasn't enough, and ask for larger-and-larger increases until its enough. 58static const int DefaultQuotaSizeIncrease = 1048576; 59 60namespace WebCore { 61 62PassRefPtr<SQLTransaction> SQLTransaction::create(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, 63 PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionWrapper> wrapper, bool readOnly) 64{ 65 return adoptRef(new SQLTransaction(db, callback, errorCallback, successCallback, wrapper, readOnly)); 66} 67 68SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, 69 PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionWrapper> wrapper, bool readOnly) 70 : m_nextStep(&SQLTransaction::acquireLock) 71 , m_executeSqlAllowed(false) 72 , m_database(db) 73 , m_wrapper(wrapper) 74 , m_callbackWrapper(callback, db->scriptExecutionContext()) 75 , m_successCallbackWrapper(successCallback, db->scriptExecutionContext()) 76 , m_errorCallbackWrapper(errorCallback, db->scriptExecutionContext()) 77 , m_shouldRetryCurrentStatement(false) 78 , m_modifiedDatabase(false) 79 , m_lockAcquired(false) 80 , m_readOnly(readOnly) 81{ 82 ASSERT(m_database); 83} 84 85SQLTransaction::~SQLTransaction() 86{ 87 ASSERT(!m_sqliteTransaction); 88} 89 90void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e) 91{ 92 if (!m_executeSqlAllowed || !m_database->opened()) { 93 e = INVALID_STATE_ERR; 94 return; 95 } 96 97 int permissions = DatabaseAuthorizer::ReadWriteMask; 98 if (!m_database->scriptExecutionContext()->allowDatabaseAccess()) 99 permissions |= DatabaseAuthorizer::NoAccessMask; 100 else if (m_readOnly) 101 permissions |= DatabaseAuthorizer::ReadOnlyMask; 102 103 RefPtr<SQLStatement> statement = SQLStatement::create(m_database.get(), sqlStatement, arguments, callback, callbackError, permissions); 104 105 if (m_database->deleted()) 106 statement->setDatabaseDeletedError(); 107 108 if (!m_database->versionMatchesExpected()) 109 statement->setVersionMismatchedError(); 110 111 enqueueStatement(statement); 112} 113 114void SQLTransaction::enqueueStatement(PassRefPtr<SQLStatement> statement) 115{ 116 MutexLocker locker(m_statementMutex); 117 m_statementQueue.append(statement); 118} 119 120#if !LOG_DISABLED 121const char* SQLTransaction::debugStepName(SQLTransaction::TransactionStepMethod step) 122{ 123 if (step == &SQLTransaction::acquireLock) 124 return "acquireLock"; 125 else if (step == &SQLTransaction::openTransactionAndPreflight) 126 return "openTransactionAndPreflight"; 127 else if (step == &SQLTransaction::runStatements) 128 return "runStatements"; 129 else if (step == &SQLTransaction::postflightAndCommit) 130 return "postflightAndCommit"; 131 else if (step == &SQLTransaction::cleanupAfterTransactionErrorCallback) 132 return "cleanupAfterTransactionErrorCallback"; 133 else if (step == &SQLTransaction::deliverTransactionCallback) 134 return "deliverTransactionCallback"; 135 else if (step == &SQLTransaction::deliverTransactionErrorCallback) 136 return "deliverTransactionErrorCallback"; 137 else if (step == &SQLTransaction::deliverStatementCallback) 138 return "deliverStatementCallback"; 139 else if (step == &SQLTransaction::deliverQuotaIncreaseCallback) 140 return "deliverQuotaIncreaseCallback"; 141 else if (step == &SQLTransaction::deliverSuccessCallback) 142 return "deliverSuccessCallback"; 143 else if (step == &SQLTransaction::cleanupAfterSuccessCallback) 144 return "cleanupAfterSuccessCallback"; 145 else 146 return "UNKNOWN"; 147} 148#endif 149 150void SQLTransaction::checkAndHandleClosedOrInterruptedDatabase() 151{ 152 if (m_database->opened() && !m_database->isInterrupted()) 153 return; 154 155 // If the database was stopped, don't do anything and cancel queued work 156 LOG(StorageAPI, "Database was stopped or interrupted - cancelling work for this transaction"); 157 MutexLocker locker(m_statementMutex); 158 m_statementQueue.clear(); 159 m_nextStep = 0; 160 161 // Release the unneeded callbacks, to break reference cycles. 162 m_callbackWrapper.clear(); 163 m_successCallbackWrapper.clear(); 164 m_errorCallbackWrapper.clear(); 165 166 // The next steps should be executed only if we're on the DB thread. 167 if (currentThread() != database()->scriptExecutionContext()->databaseThread()->getThreadID()) 168 return; 169 170 // The current SQLite transaction should be stopped, as well 171 if (m_sqliteTransaction) { 172 m_sqliteTransaction->stop(); 173 m_sqliteTransaction.clear(); 174 } 175 176 if (m_lockAcquired) 177 m_database->transactionCoordinator()->releaseLock(this); 178} 179 180 181bool SQLTransaction::performNextStep() 182{ 183 LOG(StorageAPI, "Step %s\n", debugStepName(m_nextStep)); 184 185 ASSERT(m_nextStep == &SQLTransaction::acquireLock || 186 m_nextStep == &SQLTransaction::openTransactionAndPreflight || 187 m_nextStep == &SQLTransaction::runStatements || 188 m_nextStep == &SQLTransaction::postflightAndCommit || 189 m_nextStep == &SQLTransaction::cleanupAfterSuccessCallback || 190 m_nextStep == &SQLTransaction::cleanupAfterTransactionErrorCallback); 191 192 checkAndHandleClosedOrInterruptedDatabase(); 193 194 if (m_nextStep) 195 (this->*m_nextStep)(); 196 197 // If there is no nextStep after performing the above step, the transaction is complete 198 return !m_nextStep; 199} 200 201void SQLTransaction::performPendingCallback() 202{ 203 LOG(StorageAPI, "Callback %s\n", debugStepName(m_nextStep)); 204 205 ASSERT(m_nextStep == &SQLTransaction::deliverTransactionCallback || 206 m_nextStep == &SQLTransaction::deliverTransactionErrorCallback || 207 m_nextStep == &SQLTransaction::deliverStatementCallback || 208 m_nextStep == &SQLTransaction::deliverQuotaIncreaseCallback || 209 m_nextStep == &SQLTransaction::deliverSuccessCallback); 210 211 checkAndHandleClosedOrInterruptedDatabase(); 212 213 if (m_nextStep) 214 (this->*m_nextStep)(); 215} 216 217void SQLTransaction::notifyDatabaseThreadIsShuttingDown() 218{ 219 ASSERT(currentThread() == database()->scriptExecutionContext()->databaseThread()->getThreadID()); 220 221 // If the transaction is in progress, we should roll it back here, since this is our last 222 // oportunity to do something related to this transaction on the DB thread. 223 // Clearing m_sqliteTransaction invokes SQLiteTransaction's destructor which does just that. 224 m_sqliteTransaction.clear(); 225} 226 227void SQLTransaction::acquireLock() 228{ 229 m_database->transactionCoordinator()->acquireLock(this); 230} 231 232void SQLTransaction::lockAcquired() 233{ 234 m_lockAcquired = true; 235 m_nextStep = &SQLTransaction::openTransactionAndPreflight; 236 LOG(StorageAPI, "Scheduling openTransactionAndPreflight immediately for transaction %p\n", this); 237 m_database->scheduleTransactionStep(this, true); 238} 239 240void SQLTransaction::openTransactionAndPreflight() 241{ 242 ASSERT(!m_database->sqliteDatabase().transactionInProgress()); 243 ASSERT(m_lockAcquired); 244 245 LOG(StorageAPI, "Opening and preflighting transaction %p", this); 246 247 // If the database was deleted, jump to the error callback 248 if (m_database->deleted()) { 249 m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to open a transaction, because the user deleted the database"); 250 handleTransactionError(false); 251 return; 252 } 253 254 // Set the maximum usage for this transaction if this transactions is not read-only 255 if (!m_readOnly) 256 m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); 257 258 ASSERT(!m_sqliteTransaction); 259 m_sqliteTransaction = adoptPtr(new SQLiteTransaction(m_database->sqliteDatabase(), m_readOnly)); 260 261 m_database->resetDeletes(); 262 m_database->disableAuthorizer(); 263 m_sqliteTransaction->begin(); 264 m_database->enableAuthorizer(); 265 266 // Transaction Steps 1+2 - Open a transaction to the database, jumping to the error callback if that fails 267 if (!m_sqliteTransaction->inProgress()) { 268 ASSERT(!m_database->sqliteDatabase().transactionInProgress()); 269 m_sqliteTransaction.clear(); 270 m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to open a transaction to the database"); 271 handleTransactionError(false); 272 return; 273 } 274 275 // Transaction Steps 3 - Peform preflight steps, jumping to the error callback if they fail 276 if (m_wrapper && !m_wrapper->performPreflight(this)) { 277 m_sqliteTransaction.clear(); 278 m_transactionError = m_wrapper->sqlError(); 279 if (!m_transactionError) 280 m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occured setting up transaction"); 281 282 handleTransactionError(false); 283 return; 284 } 285 286 // Transaction Step 4 - Invoke the transaction callback with the new SQLTransaction object 287 m_nextStep = &SQLTransaction::deliverTransactionCallback; 288 LOG(StorageAPI, "Scheduling deliverTransactionCallback for transaction %p\n", this); 289 m_database->scheduleTransactionCallback(this); 290} 291 292void SQLTransaction::deliverTransactionCallback() 293{ 294 bool shouldDeliverErrorCallback = false; 295 296 RefPtr<SQLTransactionCallback> callback = m_callbackWrapper.unwrap(); 297 if (callback) { 298 m_executeSqlAllowed = true; 299 shouldDeliverErrorCallback = !callback->handleEvent(this); 300 m_executeSqlAllowed = false; 301 } 302 303 // Transaction Step 5 - If the transaction callback was null or raised an exception, jump to the error callback 304 if (shouldDeliverErrorCallback) { 305 m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the SQLTransactionCallback was null or threw an exception"); 306 deliverTransactionErrorCallback(); 307 } else 308 scheduleToRunStatements(); 309} 310 311void SQLTransaction::scheduleToRunStatements() 312{ 313 m_nextStep = &SQLTransaction::runStatements; 314 LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this); 315 m_database->scheduleTransactionStep(this); 316} 317 318void SQLTransaction::runStatements() 319{ 320 ASSERT(m_lockAcquired); 321 322 // If there is a series of statements queued up that are all successful and have no associated 323 // SQLStatementCallback objects, then we can burn through the queue 324 do { 325 if (m_shouldRetryCurrentStatement && !m_sqliteTransaction->wasRolledBackBySqlite()) { 326 m_shouldRetryCurrentStatement = false; 327 // FIXME - Another place that needs fixing up after <rdar://problem/5628468> is addressed. 328 // See ::openTransactionAndPreflight() for discussion 329 330 // Reset the maximum size here, as it was increased to allow us to retry this statement. 331 // m_shouldRetryCurrentStatement is set to true only when a statement exceeds 332 // the quota, which can happen only in a read-write transaction. Therefore, there 333 // is no need to check here if the transaction is read-write. 334 m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); 335 } else { 336 // If the current statement has already been run, failed due to quota constraints, and we're not retrying it, 337 // that means it ended in an error. Handle it now 338 if (m_currentStatement && m_currentStatement->lastExecutionFailedDueToQuota()) { 339 handleCurrentStatementError(); 340 break; 341 } 342 343 // Otherwise, advance to the next statement 344 getNextStatement(); 345 } 346 } while (runCurrentStatement()); 347 348 // If runCurrentStatement() returned false, that means either there was no current statement to run, 349 // or the current statement requires a callback to complete. In the later case, it also scheduled 350 // the callback or performed any other additional work so we can return 351 if (!m_currentStatement) 352 postflightAndCommit(); 353} 354 355void SQLTransaction::getNextStatement() 356{ 357 m_currentStatement = 0; 358 359 MutexLocker locker(m_statementMutex); 360 if (!m_statementQueue.isEmpty()) { 361 m_currentStatement = m_statementQueue.takeFirst(); 362 } 363} 364 365bool SQLTransaction::runCurrentStatement() 366{ 367 if (!m_currentStatement) 368 return false; 369 370 m_database->resetAuthorizer(); 371 372 if (m_currentStatement->execute(m_database.get())) { 373 if (m_database->lastActionChangedDatabase()) { 374 // Flag this transaction as having changed the database for later delegate notification 375 m_modifiedDatabase = true; 376 // Also dirty the size of this database file for calculating quota usage 377 m_database->transactionClient()->didExecuteStatement(database()); 378 } 379 380 if (m_currentStatement->hasStatementCallback()) { 381 m_nextStep = &SQLTransaction::deliverStatementCallback; 382 LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this); 383 m_database->scheduleTransactionCallback(this); 384 return false; 385 } 386 return true; 387 } 388 389 if (m_currentStatement->lastExecutionFailedDueToQuota()) { 390 m_nextStep = &SQLTransaction::deliverQuotaIncreaseCallback; 391 LOG(StorageAPI, "Scheduling deliverQuotaIncreaseCallback for transaction %p\n", this); 392 m_database->scheduleTransactionCallback(this); 393 return false; 394 } 395 396 handleCurrentStatementError(); 397 398 return false; 399} 400 401void SQLTransaction::handleCurrentStatementError() 402{ 403 // Transaction Steps 6.error - Call the statement's error callback, but if there was no error callback, 404 // or the transaction was rolled back, jump to the transaction error callback 405 if (m_currentStatement->hasStatementErrorCallback() && !m_sqliteTransaction->wasRolledBackBySqlite()) { 406 m_nextStep = &SQLTransaction::deliverStatementCallback; 407 LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this); 408 m_database->scheduleTransactionCallback(this); 409 } else { 410 m_transactionError = m_currentStatement->sqlError(); 411 if (!m_transactionError) 412 m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "the statement failed to execute"); 413 handleTransactionError(false); 414 } 415} 416 417void SQLTransaction::deliverStatementCallback() 418{ 419 ASSERT(m_currentStatement); 420 421 // Transaction Step 6.6 and 6.3(error) - If the statement callback went wrong, jump to the transaction error callback 422 // Otherwise, continue to loop through the statement queue 423 m_executeSqlAllowed = true; 424 bool result = m_currentStatement->performCallback(this); 425 m_executeSqlAllowed = false; 426 427 if (result) { 428 m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the statement callback raised an exception or statement error callback did not return false"); 429 handleTransactionError(true); 430 } else 431 scheduleToRunStatements(); 432} 433 434void SQLTransaction::deliverQuotaIncreaseCallback() 435{ 436 ASSERT(m_currentStatement); 437 ASSERT(!m_shouldRetryCurrentStatement); 438 439 m_shouldRetryCurrentStatement = m_database->transactionClient()->didExceedQuota(database()); 440 441 m_nextStep = &SQLTransaction::runStatements; 442 LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this); 443 m_database->scheduleTransactionStep(this); 444} 445 446void SQLTransaction::postflightAndCommit() 447{ 448 ASSERT(m_lockAcquired); 449 450 // Transaction Step 7 - Peform postflight steps, jumping to the error callback if they fail 451 if (m_wrapper && !m_wrapper->performPostflight(this)) { 452 m_transactionError = m_wrapper->sqlError(); 453 if (!m_transactionError) 454 m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occured setting up transaction"); 455 handleTransactionError(false); 456 return; 457 } 458 459 // Transacton Step 8+9 - Commit the transaction, jumping to the error callback if that fails 460 ASSERT(m_sqliteTransaction); 461 462 m_database->disableAuthorizer(); 463 m_sqliteTransaction->commit(); 464 m_database->enableAuthorizer(); 465 466 // If the commit failed, the transaction will still be marked as "in progress" 467 if (m_sqliteTransaction->inProgress()) { 468 m_successCallbackWrapper.clear(); 469 m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "failed to commit the transaction"); 470 handleTransactionError(false); 471 return; 472 } 473 474 // Vacuum the database if anything was deleted. 475 if (m_database->hadDeletes()) 476 m_database->incrementalVacuumIfNeeded(); 477 478 // The commit was successful. If the transaction modified this database, notify the delegates. 479 if (m_modifiedDatabase) 480 m_database->transactionClient()->didCommitWriteTransaction(database()); 481 482 // Now release our unneeded callbacks, to break reference cycles. 483 m_errorCallbackWrapper.clear(); 484 485 // Transaction Step 10 - Deliver success callback, if there is one 486 if (m_successCallbackWrapper.hasCallback()) { 487 m_nextStep = &SQLTransaction::deliverSuccessCallback; 488 LOG(StorageAPI, "Scheduling deliverSuccessCallback for transaction %p\n", this); 489 m_database->scheduleTransactionCallback(this); 490 } else 491 cleanupAfterSuccessCallback(); 492} 493 494void SQLTransaction::deliverSuccessCallback() 495{ 496 // Transaction Step 10 - Deliver success callback 497 RefPtr<VoidCallback> successCallback = m_successCallbackWrapper.unwrap(); 498 if (successCallback) 499 successCallback->handleEvent(); 500 501 // Schedule a "post-success callback" step to return control to the database thread in case there 502 // are further transactions queued up for this Database 503 m_nextStep = &SQLTransaction::cleanupAfterSuccessCallback; 504 LOG(StorageAPI, "Scheduling cleanupAfterSuccessCallback for transaction %p\n", this); 505 m_database->scheduleTransactionStep(this); 506} 507 508void SQLTransaction::cleanupAfterSuccessCallback() 509{ 510 ASSERT(m_lockAcquired); 511 512 // Transaction Step 11 - End transaction steps 513 // There is no next step 514 LOG(StorageAPI, "Transaction %p is complete\n", this); 515 ASSERT(!m_database->sqliteDatabase().transactionInProgress()); 516 m_sqliteTransaction.clear(); 517 m_nextStep = 0; 518 519 // Release the lock on this database 520 m_database->transactionCoordinator()->releaseLock(this); 521} 522 523void SQLTransaction::handleTransactionError(bool inCallback) 524{ 525 if (m_errorCallbackWrapper.hasCallback()) { 526 if (inCallback) 527 deliverTransactionErrorCallback(); 528 else { 529 m_nextStep = &SQLTransaction::deliverTransactionErrorCallback; 530 LOG(StorageAPI, "Scheduling deliverTransactionErrorCallback for transaction %p\n", this); 531 m_database->scheduleTransactionCallback(this); 532 } 533 return; 534 } 535 536 // No error callback, so fast-forward to: 537 // Transaction Step 12 - Rollback the transaction. 538 if (inCallback) { 539 m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback; 540 LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this); 541 m_database->scheduleTransactionStep(this); 542 } else { 543 cleanupAfterTransactionErrorCallback(); 544 } 545} 546 547void SQLTransaction::deliverTransactionErrorCallback() 548{ 549 ASSERT(m_transactionError); 550 551 // Transaction Step 12 - If exists, invoke error callback with the last 552 // error to have occurred in this transaction. 553 RefPtr<SQLTransactionErrorCallback> errorCallback = m_errorCallbackWrapper.unwrap(); 554 if (errorCallback) 555 errorCallback->handleEvent(m_transactionError.get()); 556 557 m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback; 558 LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this); 559 m_database->scheduleTransactionStep(this); 560} 561 562void SQLTransaction::cleanupAfterTransactionErrorCallback() 563{ 564 ASSERT(m_lockAcquired); 565 566 m_database->disableAuthorizer(); 567 if (m_sqliteTransaction) { 568 // Transaction Step 12 - Rollback the transaction. 569 m_sqliteTransaction->rollback(); 570 571 ASSERT(!m_database->sqliteDatabase().transactionInProgress()); 572 m_sqliteTransaction.clear(); 573 } 574 m_database->enableAuthorizer(); 575 576 // Transaction Step 12 - Any still-pending statements in the transaction are discarded. 577 { 578 MutexLocker locker(m_statementMutex); 579 m_statementQueue.clear(); 580 } 581 582 // Transaction is complete! There is no next step 583 LOG(StorageAPI, "Transaction %p is complete with an error\n", this); 584 ASSERT(!m_database->sqliteDatabase().transactionInProgress()); 585 m_nextStep = 0; 586 587 // Now release the lock on this database 588 m_database->transactionCoordinator()->releaseLock(this); 589} 590 591} // namespace WebCore 592 593#endif // ENABLE(DATABASE) 594