diff options
Diffstat (limited to 'WebCore/storage/SQLTransaction.cpp')
-rw-r--r-- | WebCore/storage/SQLTransaction.cpp | 178 |
1 files changed, 103 insertions, 75 deletions
diff --git a/WebCore/storage/SQLTransaction.cpp b/WebCore/storage/SQLTransaction.cpp index 3331e6e..149b384 100644 --- a/WebCore/storage/SQLTransaction.cpp +++ b/WebCore/storage/SQLTransaction.cpp @@ -35,11 +35,9 @@ #include "Database.h" #include "DatabaseAuthorizer.h" #include "DatabaseDetails.h" -#include "DatabaseTracker.h" #include "Document.h" #include "ExceptionCode.h" #include "Logging.h" -#include "OriginQuotaManager.h" #include "Page.h" #include "PlatformString.h" #include "SecurityOrigin.h" @@ -50,24 +48,26 @@ #include "SQLStatement.h" #include "SQLStatementCallback.h" #include "SQLStatementErrorCallback.h" +#include "SQLTransactionClient.h" +#include "SQLTransactionCoordinator.h" #include "SQLValue.h" -// There's no way of knowing exactly how much more space will be required when a statement hits the quota limit. +// There's no way of knowing exactly how much more space will be required when a statement hits the quota limit. // For now, we'll arbitrarily choose currentQuota + 1mb. // In the future we decide to track if a size increase wasn't enough, and ask for larger-and-larger increases until its enough. static const int DefaultQuotaSizeIncrease = 1048576; namespace WebCore { -PassRefPtr<SQLTransaction> SQLTransaction::create(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, - PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionWrapper> wrapper) +PassRefPtr<SQLTransaction> SQLTransaction::create(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, + PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionWrapper> wrapper, bool readOnly) { - return adoptRef(new SQLTransaction(db, callback, errorCallback, successCallback, wrapper)); + return adoptRef(new SQLTransaction(db, callback, errorCallback, successCallback, wrapper, readOnly)); } -SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback, - PassRefPtr<SQLTransactionWrapper> wrapper) - : m_nextStep(&SQLTransaction::openTransactionAndPreflight) +SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback, + PassRefPtr<SQLTransactionWrapper> wrapper, bool readOnly) + : m_nextStep(&SQLTransaction::acquireLock) , m_executeSqlAllowed(false) , m_database(db) , m_wrapper(wrapper) @@ -76,6 +76,8 @@ SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> , m_errorCallback(errorCallback) , m_shouldRetryCurrentStatement(false) , m_modifiedDatabase(false) + , m_lockAcquired(false) + , m_readOnly(readOnly) { ASSERT(m_database); } @@ -91,11 +93,13 @@ void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValu return; } - bool readOnlyMode = false; - Page* page = m_database->document()->page(); - if (!page || page->settings()->privateBrowsingEnabled()) - readOnlyMode = true; - + bool readOnlyMode = m_readOnly; + if (!readOnlyMode) { + Page* page = m_database->document()->page(); + if (!page || page->settings()->privateBrowsingEnabled()) + readOnlyMode = true; + } + RefPtr<SQLStatement> statement = SQLStatement::create(sqlStatement, arguments, callback, callbackError, readOnlyMode); if (m_database->deleted()) @@ -116,7 +120,9 @@ void SQLTransaction::enqueueStatement(PassRefPtr<SQLStatement> statement) #ifndef NDEBUG const char* SQLTransaction::debugStepName(SQLTransaction::TransactionStepMethod step) { - if (step == &SQLTransaction::openTransactionAndPreflight) + if (step == &SQLTransaction::acquireLock) + return "acquireLock"; + else if (step == &SQLTransaction::openTransactionAndPreflight) return "openTransactionAndPreflight"; else if (step == &SQLTransaction::runStatements) return "runStatements"; @@ -145,18 +151,21 @@ void SQLTransaction::checkAndHandleClosedDatabase() { if (!m_database->stopped()) return; - + // If the database was stopped, don't do anything and cancel queued work LOG(StorageAPI, "Database was stopped - cancelling work for this transaction"); MutexLocker locker(m_statementMutex); m_statementQueue.clear(); m_nextStep = 0; - + // The current SQLite transaction should be stopped, as well if (m_sqliteTransaction) { m_sqliteTransaction->stop(); m_sqliteTransaction.clear(); } + + if (m_lockAcquired) + m_database->transactionCoordinator()->releaseLock(this); } @@ -164,14 +173,15 @@ bool SQLTransaction::performNextStep() { LOG(StorageAPI, "Step %s\n", debugStepName(m_nextStep)); - ASSERT(m_nextStep == &SQLTransaction::openTransactionAndPreflight || + ASSERT(m_nextStep == &SQLTransaction::acquireLock || + m_nextStep == &SQLTransaction::openTransactionAndPreflight || m_nextStep == &SQLTransaction::runStatements || m_nextStep == &SQLTransaction::postflightAndCommit || m_nextStep == &SQLTransaction::cleanupAfterSuccessCallback || m_nextStep == &SQLTransaction::cleanupAfterTransactionErrorCallback); - + checkAndHandleClosedDatabase(); - + if (m_nextStep) (this->*m_nextStep)(); @@ -190,14 +200,28 @@ void SQLTransaction::performPendingCallback() m_nextStep == &SQLTransaction::deliverSuccessCallback); checkAndHandleClosedDatabase(); - + if (m_nextStep) (this->*m_nextStep)(); } +void SQLTransaction::acquireLock() +{ + m_database->transactionCoordinator()->acquireLock(this); +} + +void SQLTransaction::lockAcquired() +{ + m_lockAcquired = true; + m_nextStep = &SQLTransaction::openTransactionAndPreflight; + LOG(StorageAPI, "Scheduling openTransactionAndPreflight immediately for transaction %p\n", this); + m_database->scheduleTransactionStep(this, true); +} + void SQLTransaction::openTransactionAndPreflight() { ASSERT(!m_database->m_sqliteDatabase.transactionInProgress()); + ASSERT(m_lockAcquired); LOG(StorageAPI, "Opening and preflighting transaction %p", this); @@ -208,16 +232,17 @@ void SQLTransaction::openTransactionAndPreflight() return; } - // Set the maximum usage for this transaction - m_database->m_sqliteDatabase.setMaximumSize(m_database->maximumSize()); - + // Set the maximum usage for this transaction if this transactions is not read-only + if (!m_readOnly) + m_database->m_sqliteDatabase.setMaximumSize(m_database->maximumSize()); + ASSERT(!m_sqliteTransaction); - m_sqliteTransaction.set(new SQLiteTransaction(m_database->m_sqliteDatabase)); - + m_sqliteTransaction.set(new SQLiteTransaction(m_database->m_sqliteDatabase, m_readOnly)); + m_database->m_databaseAuthorizer->disable(); m_sqliteTransaction->begin(); - m_database->m_databaseAuthorizer->enable(); - + m_database->m_databaseAuthorizer->enable(); + // Transaction Steps 1+2 - Open a transaction to the database, jumping to the error callback if that fails if (!m_sqliteTransaction->inProgress()) { ASSERT(!m_database->m_sqliteDatabase.transactionInProgress()); @@ -226,7 +251,7 @@ void SQLTransaction::openTransactionAndPreflight() handleTransactionError(false); return; } - + // Transaction Steps 3 - Peform preflight steps, jumping to the error callback if they fail if (m_wrapper && !m_wrapper->performPreflight(this)) { ASSERT(!m_database->m_sqliteDatabase.transactionInProgress()); @@ -238,7 +263,7 @@ void SQLTransaction::openTransactionAndPreflight() handleTransactionError(false); return; } - + // Transaction Step 4 - Invoke the transaction callback with the new SQLTransaction object m_nextStep = &SQLTransaction::deliverTransactionCallback; LOG(StorageAPI, "Scheduling deliverTransactionCallback for transaction %p\n", this); @@ -273,6 +298,8 @@ void SQLTransaction::scheduleToRunStatements() void SQLTransaction::runStatements() { + ASSERT(m_lockAcquired); + // If there is a series of statements queued up that are all successful and have no associated // SQLStatementCallback objects, then we can burn through the queue do { @@ -280,8 +307,11 @@ void SQLTransaction::runStatements() m_shouldRetryCurrentStatement = false; // FIXME - Another place that needs fixing up after <rdar://problem/5628468> is addressed. // See ::openTransactionAndPreflight() for discussion - - // Reset the maximum size here, as it was increased to allow us to retry this statement + + // Reset the maximum size here, as it was increased to allow us to retry this statement. + // m_shouldRetryCurrentStatement is set to true only when a statement exceeds + // the quota, which can happen only in a read-write transaction. Therefore, there + // is no need to check here if the transaction is read-write. m_database->m_sqliteDatabase.setMaximumSize(m_database->maximumSize()); } else { // If the current statement has already been run, failed due to quota constraints, and we're not retrying it, @@ -290,14 +320,14 @@ void SQLTransaction::runStatements() handleCurrentStatementError(); break; } - + // Otherwise, advance to the next statement getNextStatement(); } } while (runCurrentStatement()); - + // If runCurrentStatement() returned false, that means either there was no current statement to run, - // or the current statement requires a callback to complete. In the later case, it also scheduled + // or the current statement requires a callback to complete. In the later case, it also scheduled // the callback or performed any other additional work so we can return if (!m_currentStatement) postflightAndCommit(); @@ -306,7 +336,7 @@ void SQLTransaction::runStatements() void SQLTransaction::getNextStatement() { m_currentStatement = 0; - + MutexLocker locker(m_statementMutex); if (!m_statementQueue.isEmpty()) { m_currentStatement = m_statementQueue.first(); @@ -318,20 +348,17 @@ bool SQLTransaction::runCurrentStatement() { if (!m_currentStatement) return false; - + m_database->m_databaseAuthorizer->reset(); - + if (m_currentStatement->execute(m_database.get())) { if (m_database->m_databaseAuthorizer->lastActionChangedDatabase()) { // Flag this transaction as having changed the database for later delegate notification m_modifiedDatabase = true; // Also dirty the size of this database file for calculating quota usage - OriginQuotaManager& manager(DatabaseTracker::tracker().originQuotaManager()); - Locker<OriginQuotaManager> locker(manager); - - manager.markDatabase(m_database.get()); + m_database->transactionClient()->didExecuteStatement(this); } - + if (m_currentStatement->hasStatementCallback()) { m_nextStep = &SQLTransaction::deliverStatementCallback; LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this); @@ -340,16 +367,16 @@ bool SQLTransaction::runCurrentStatement() } return true; } - + if (m_currentStatement->lastExecutionFailedDueToQuota()) { m_nextStep = &SQLTransaction::deliverQuotaIncreaseCallback; LOG(StorageAPI, "Scheduling deliverQuotaIncreaseCallback for transaction %p\n", this); m_database->scheduleTransactionCallback(this); return false; } - + handleCurrentStatementError(); - + return false; } @@ -372,7 +399,7 @@ void SQLTransaction::handleCurrentStatementError() void SQLTransaction::deliverStatementCallback() { ASSERT(m_currentStatement); - + // Transaction Step 6.6 and 6.3(error) - If the statement callback went wrong, jump to the transaction error callback // Otherwise, continue to loop through the statement queue m_executeSqlAllowed = true; @@ -390,27 +417,18 @@ void SQLTransaction::deliverQuotaIncreaseCallback() { ASSERT(m_currentStatement); ASSERT(!m_shouldRetryCurrentStatement); - - Page* page = m_database->document()->page(); - ASSERT(page); - - RefPtr<SecurityOrigin> origin = m_database->securityOriginCopy(); - - unsigned long long currentQuota = DatabaseTracker::tracker().quotaForOrigin(origin.get()); - page->chrome()->client()->exceededDatabaseQuota(m_database->document()->frame(), m_database->stringIdentifier()); - unsigned long long newQuota = DatabaseTracker::tracker().quotaForOrigin(origin.get()); - - // If the new quota ended up being larger than the old quota, we will retry the statement. - if (newQuota > currentQuota) - m_shouldRetryCurrentStatement = true; - + + m_shouldRetryCurrentStatement = m_database->transactionClient()->didExceedQuota(this); + m_nextStep = &SQLTransaction::runStatements; LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this); m_database->scheduleTransactionStep(this); } void SQLTransaction::postflightAndCommit() -{ +{ + ASSERT(m_lockAcquired); + // Transaction Step 7 - Peform postflight steps, jumping to the error callback if they fail if (m_wrapper && !m_wrapper->performPostflight(this)) { m_transactionError = m_wrapper->sqlError(); @@ -419,10 +437,10 @@ void SQLTransaction::postflightAndCommit() handleTransactionError(false); return; } - + // Transacton Step 8+9 - Commit the transaction, jumping to the error callback if that fails ASSERT(m_sqliteTransaction); - + m_database->m_databaseAuthorizer->disable(); m_sqliteTransaction->commit(); m_database->m_databaseAuthorizer->enable(); @@ -433,21 +451,21 @@ void SQLTransaction::postflightAndCommit() handleTransactionError(false); return; } - + // The commit was successful, notify the delegates if the transaction modified this database if (m_modifiedDatabase) - DatabaseTracker::tracker().scheduleNotifyDatabaseChanged(m_database->m_securityOrigin.get(), m_database->m_name); - + m_database->transactionClient()->didCommitTransaction(this); + // Now release our unneeded callbacks, to break reference cycles. m_callback = 0; m_errorCallback = 0; - + // Transaction Step 10 - Deliver success callback, if there is one if (m_successCallback) { m_nextStep = &SQLTransaction::deliverSuccessCallback; LOG(StorageAPI, "Scheduling deliverSuccessCallback for transaction %p\n", this); m_database->scheduleTransactionCallback(this); - } else + } else cleanupAfterSuccessCallback(); } @@ -456,7 +474,7 @@ void SQLTransaction::deliverSuccessCallback() // Transaction Step 10 - Deliver success callback ASSERT(m_successCallback); m_successCallback->handleEvent(); - + // Release the last callback to break reference cycle m_successCallback = 0; @@ -469,11 +487,16 @@ void SQLTransaction::deliverSuccessCallback() void SQLTransaction::cleanupAfterSuccessCallback() { + ASSERT(m_lockAcquired); + // Transaction Step 11 - End transaction steps // There is no next step LOG(StorageAPI, "Transaction %p is complete\n", this); ASSERT(!m_database->m_sqliteDatabase.transactionInProgress()); m_nextStep = 0; + + // Release the lock on this database + m_database->transactionCoordinator()->releaseLock(this); } void SQLTransaction::handleTransactionError(bool inCallback) @@ -488,7 +511,7 @@ void SQLTransaction::handleTransactionError(bool inCallback) } return; } - + // No error callback, so fast-forward to: // Transaction Step 12 - Rollback the transaction. if (inCallback) { @@ -503,7 +526,7 @@ void SQLTransaction::handleTransactionError(bool inCallback) void SQLTransaction::deliverTransactionErrorCallback() { ASSERT(m_transactionError); - + // Transaction Step 12 - If exists, invoke error callback with the last // error to have occurred in this transaction. if (m_errorCallback) @@ -516,22 +539,24 @@ void SQLTransaction::deliverTransactionErrorCallback() void SQLTransaction::cleanupAfterTransactionErrorCallback() { + ASSERT(m_lockAcquired); + m_database->m_databaseAuthorizer->disable(); if (m_sqliteTransaction) { // Transaction Step 12 - Rollback the transaction. m_sqliteTransaction->rollback(); - + ASSERT(!m_database->m_sqliteDatabase.transactionInProgress()); m_sqliteTransaction.clear(); } m_database->m_databaseAuthorizer->enable(); - + // Transaction Step 12 - Any still-pending statements in the transaction are discarded. { MutexLocker locker(m_statementMutex); m_statementQueue.clear(); } - + // Transaction is complete! There is no next step LOG(StorageAPI, "Transaction %p is complete with an error\n", this); ASSERT(!m_database->m_sqliteDatabase.transactionInProgress()); @@ -540,6 +565,9 @@ void SQLTransaction::cleanupAfterTransactionErrorCallback() // Now release our callbacks, to break reference cycles. m_callback = 0; m_errorCallback = 0; + + // Now release the lock on this database + m_database->transactionCoordinator()->releaseLock(this); } } // namespace WebCore |