diff options
Diffstat (limited to 'WebCore/storage/SQLTransaction.cpp')
-rw-r--r-- | WebCore/storage/SQLTransaction.cpp | 550 |
1 files changed, 0 insertions, 550 deletions
diff --git a/WebCore/storage/SQLTransaction.cpp b/WebCore/storage/SQLTransaction.cpp deleted file mode 100644 index 5820d7f..0000000 --- a/WebCore/storage/SQLTransaction.cpp +++ /dev/null @@ -1,550 +0,0 @@ -/* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "SQLTransaction.h" - -#include "ChromeClient.h" -#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" -#include "SQLError.h" -#include "SQLiteTransaction.h" -#include "SQLResultSet.h" -#include "SQLStatement.h" -#include "SQLStatementCallback.h" -#include "SQLStatementErrorCallback.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. -// 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) -{ - return adoptRef(new SQLTransaction(db, callback, errorCallback, successCallback, wrapper)); -} - -SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback, - PassRefPtr<SQLTransactionWrapper> wrapper) - : m_nextStep(&SQLTransaction::openTransactionAndPreflight) - , m_executeSqlAllowed(false) - , m_database(db) - , m_wrapper(wrapper) - , m_callback(callback) - , m_successCallback(successCallback) - , m_errorCallback(errorCallback) - , m_shouldRetryCurrentStatement(false) - , m_shouldCommitAfterErrorCallback(true) - , m_modifiedDatabase(false) -{ - ASSERT(m_database); -} - -SQLTransaction::~SQLTransaction() -{ -} - -void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e) -{ - if (!m_executeSqlAllowed || m_database->stopped()) { - e = INVALID_STATE_ERR; - return; - } - - RefPtr<SQLStatement> statement = SQLStatement::create(sqlStatement.copy(), arguments, callback, callbackError); - - if (m_database->deleted()) - statement->setDatabaseDeletedError(); - - if (!m_database->versionMatchesExpected()) - statement->setVersionMismatchedError(); - - enqueueStatement(statement); -} - -void SQLTransaction::enqueueStatement(PassRefPtr<SQLStatement> statement) -{ - MutexLocker locker(m_statementMutex); - m_statementQueue.append(statement); -} - -#ifndef NDEBUG -const char* SQLTransaction::debugStepName(SQLTransaction::TransactionStepMethod step) -{ - if (step == &SQLTransaction::openTransactionAndPreflight) - return "openTransactionAndPreflight"; - else if (step == &SQLTransaction::runStatements) - return "runStatements"; - else if (step == &SQLTransaction::postflightAndCommit) - return "postflightAndCommit"; - else if (step == &SQLTransaction::cleanupAfterTransactionErrorCallback) - return "cleanupAfterTransactionErrorCallback"; - else if (step == &SQLTransaction::deliverTransactionCallback) - return "deliverTransactionCallback"; - else if (step == &SQLTransaction::deliverTransactionErrorCallback) - return "deliverTransactionErrorCallback"; - else if (step == &SQLTransaction::deliverStatementCallback) - return "deliverStatementCallback"; - else if (step == &SQLTransaction::deliverQuotaIncreaseCallback) - return "deliverQuotaIncreaseCallback"; - else if (step == &SQLTransaction::deliverSuccessCallback) - return "deliverSuccessCallback"; - else if (step == &SQLTransaction::cleanupAfterSuccessCallback) - return "cleanupAfterSuccessCallback"; - else - return "UNKNOWN"; -} -#endif - -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(); - } -} - - -bool SQLTransaction::performNextStep() -{ - LOG(StorageAPI, "Step %s\n", debugStepName(m_nextStep)); - - ASSERT(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)(); - - // If there is no nextStep after performing the above step, the transaction is complete - return !m_nextStep; -} - -void SQLTransaction::performPendingCallback() -{ - LOG(StorageAPI, "Callback %s\n", debugStepName(m_nextStep)); - - ASSERT(m_nextStep == &SQLTransaction::deliverTransactionCallback || - m_nextStep == &SQLTransaction::deliverTransactionErrorCallback || - m_nextStep == &SQLTransaction::deliverStatementCallback || - m_nextStep == &SQLTransaction::deliverQuotaIncreaseCallback || - m_nextStep == &SQLTransaction::deliverSuccessCallback); - - checkAndHandleClosedDatabase(); - - if (m_nextStep) - (this->*m_nextStep)(); -} - -void SQLTransaction::openTransactionAndPreflight() -{ - ASSERT(!m_database->m_sqliteDatabase.transactionInProgress()); - - LOG(StorageAPI, "Opening and preflighting transaction %p", this); - - // If the database was deleted, jump to the error callback - if (m_database->deleted()) { - m_transactionError = SQLError::create(0, "unable to open a transaction, because the user deleted the database"); - handleTransactionError(false); - return; - } - - // Set the maximum usage for this transaction - m_database->m_sqliteDatabase.setMaximumSize(m_database->maximumSize()); - - ASSERT(!m_sqliteTransaction); - m_sqliteTransaction.set(new SQLiteTransaction(m_database->m_sqliteDatabase)); - - m_database->m_databaseAuthorizer->disable(); - m_sqliteTransaction->begin(); - 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()); - m_sqliteTransaction.clear(); - m_transactionError = SQLError::create(0, "unable to open a transaction to the database"); - 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()); - m_sqliteTransaction.clear(); - m_transactionError = m_wrapper->sqlError(); - if (!m_transactionError) - m_transactionError = SQLError::create(0, "unknown error occured setting up transaction"); - - 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); - m_database->scheduleTransactionCallback(this); -} - -void SQLTransaction::deliverTransactionCallback() -{ - bool shouldDeliverErrorCallback = false; - - if (m_callback) { - m_executeSqlAllowed = true; - m_callback->handleEvent(this, shouldDeliverErrorCallback); - m_executeSqlAllowed = false; - } else - shouldDeliverErrorCallback = true; - - // Transaction Step 5 - If the transaction callback was null or raised an exception, jump to the error callback - if (shouldDeliverErrorCallback) { - m_transactionError = SQLError::create(0, "the SQLTransactionCallback was null or threw an exception"); - deliverTransactionErrorCallback(); - } else - scheduleToRunStatements(); -} - -void SQLTransaction::scheduleToRunStatements() -{ - m_nextStep = &SQLTransaction::runStatements; - LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this); - m_database->scheduleTransactionStep(this); -} - -void SQLTransaction::runStatements() -{ - // 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 { - if (m_shouldRetryCurrentStatement) { - 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 - 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, - // that means it ended in an error. Handle it now - if (m_currentStatement && m_currentStatement->lastExecutionFailedDueToQuota()) { - 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 - // the callback or performed any other additional work so we can return - if (!m_currentStatement) - postflightAndCommit(); -} - -void SQLTransaction::getNextStatement() -{ - m_currentStatement = 0; - - MutexLocker locker(m_statementMutex); - if (!m_statementQueue.isEmpty()) { - m_currentStatement = m_statementQueue.first(); - m_statementQueue.removeFirst(); - } -} - -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()); - } - - if (m_currentStatement->hasStatementCallback()) { - m_nextStep = &SQLTransaction::deliverStatementCallback; - LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this); - m_database->scheduleTransactionCallback(this); - return false; - } - 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; -} - -void SQLTransaction::handleCurrentStatementError() -{ - // Transaction Steps 6.error - Call the statement's error callback, but if there was no error callback, - // jump to the transaction error callback - if (m_currentStatement->hasStatementErrorCallback()) { - m_nextStep = &SQLTransaction::deliverStatementCallback; - LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this); - m_database->scheduleTransactionCallback(this); - } else { - m_transactionError = m_currentStatement->sqlError(); - if (!m_transactionError) - m_transactionError = SQLError::create(1, "the statement failed to execute"); - handleTransactionError(false); - } -} - -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; - bool result = m_currentStatement->performCallback(this); - m_executeSqlAllowed = false; - - if (result) { - m_transactionError = SQLError::create(0, "the statement callback raised an exception or statement error callback did not return false"); - handleTransactionError(true); - } else - scheduleToRunStatements(); -} - -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_nextStep = &SQLTransaction::runStatements; - LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this); - m_database->scheduleTransactionStep(this); -} - -void SQLTransaction::postflightAndCommit() -{ - // 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(); - if (!m_transactionError) - m_transactionError = SQLError::create(0, "unknown error occured setting up transaction"); - 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(); - - // If the commit failed, the transaction will still be marked as "in progress" - if (m_sqliteTransaction->inProgress()) { - m_shouldCommitAfterErrorCallback = false; - m_transactionError = SQLError::create(0, "failed to commit the transaction"); - 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); - - // 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 - cleanupAfterSuccessCallback(); -} - -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; - - // Schedule a "post-success callback" step to return control to the database thread in case there - // are further transactions queued up for this Database - m_nextStep = &SQLTransaction::cleanupAfterSuccessCallback; - LOG(StorageAPI, "Scheduling cleanupAfterSuccessCallback for transaction %p\n", this); - m_database->scheduleTransactionStep(this); -} - -void SQLTransaction::cleanupAfterSuccessCallback() -{ - // 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; -} - -void SQLTransaction::handleTransactionError(bool inCallback) -{ - if (m_errorCallback) { - if (inCallback) - deliverTransactionErrorCallback(); - else { - m_nextStep = &SQLTransaction::deliverTransactionErrorCallback; - LOG(StorageAPI, "Scheduling deliverTransactionErrorCallback for transaction %p\n", this); - m_database->scheduleTransactionCallback(this); - } - return; - } - - // Transaction Step 12 - If the callback couldn't be called, then rollback the transaction. - m_shouldCommitAfterErrorCallback = false; - if (inCallback) { - m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback; - LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this); - m_database->scheduleTransactionStep(this); - } else { - cleanupAfterTransactionErrorCallback(); - } -} - -void SQLTransaction::deliverTransactionErrorCallback() -{ - ASSERT(m_transactionError); - - // Transaction Step 12 - If the callback didn't return false, then rollback the transaction. - // This includes the callback not existing, returning true, or throwing an exception - if (!m_errorCallback || m_errorCallback->handleEvent(m_transactionError.get())) - m_shouldCommitAfterErrorCallback = false; - - m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback; - LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this); - m_database->scheduleTransactionStep(this); -} - -void SQLTransaction::cleanupAfterTransactionErrorCallback() -{ - m_database->m_databaseAuthorizer->disable(); - if (m_sqliteTransaction) { - // Transaction Step 12 -If the error callback returned false, and the last error wasn't itself a - // failure when committing the transaction, then try to commit the transaction - if (m_shouldCommitAfterErrorCallback) - m_sqliteTransaction->commit(); - - if (m_sqliteTransaction->inProgress()) { - // Transaction Step 12 - If that fails, or if the callback couldn't be called - // or if it didn't return false, then rollback the transaction. - m_sqliteTransaction->rollback(); - } else if (m_modifiedDatabase) { - // But if the commit was successful, notify the delegates if the transaction modified this database - DatabaseTracker::tracker().scheduleNotifyDatabaseChanged(m_database->m_securityOrigin.get(), m_database->m_name); - } - - 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()); - m_nextStep = 0; - - // Now release our callbacks, to break reference cycles. - m_callback = 0; - m_errorCallback = 0; -} - -} // namespace WebCore |