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