summaryrefslogtreecommitdiffstats
path: root/WebCore/storage/SQLTransaction.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/storage/SQLTransaction.cpp')
-rw-r--r--WebCore/storage/SQLTransaction.cpp550
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