SQLTransaction.cpp [plain text]
#include "config.h"
#include "SQLTransaction.h"
#include "Database.h"
#include "DatabaseAuthorizer.h"
#include "DatabaseContext.h"
#include "ExceptionCode.h"
#include "Logging.h"
#include "SQLError.h"
#include "SQLStatementCallback.h"
#include "SQLStatementErrorCallback.h"
#include "SQLTransactionBackend.h"
#include "SQLTransactionCallback.h"
#include "SQLTransactionClient.h" // FIXME: Should be used in the backend only.
#include "SQLTransactionErrorCallback.h"
#include "VoidCallback.h"
#include <wtf/StdLibExtras.h>
#include <wtf/Vector.h>
namespace WebCore {
Ref<SQLTransaction> SQLTransaction::create(Ref<Database>&& database, RefPtr<SQLTransactionCallback>&& callback, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, bool readOnly)
{
return adoptRef(*new SQLTransaction(WTF::move(database), WTF::move(callback), WTF::move(successCallback), WTF::move(errorCallback), readOnly));
}
SQLTransaction::SQLTransaction(Ref<Database>&& database, RefPtr<SQLTransactionCallback>&& callback, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, bool readOnly)
: m_database(WTF::move(database))
, m_callbackWrapper(WTF::move(callback), m_database->scriptExecutionContext())
, m_successCallbackWrapper(WTF::move(successCallback), m_database->scriptExecutionContext())
, m_errorCallbackWrapper(WTF::move(errorCallback), m_database->scriptExecutionContext())
, m_executeSqlAllowed(false)
, m_readOnly(readOnly)
{
}
SQLTransaction::~SQLTransaction()
{
}
bool SQLTransaction::hasCallback() const
{
return m_callbackWrapper.hasCallback();
}
bool SQLTransaction::hasSuccessCallback() const
{
return m_successCallbackWrapper.hasCallback();
}
bool SQLTransaction::hasErrorCallback() const
{
return m_errorCallbackWrapper.hasCallback();
}
void SQLTransaction::setBackend(SQLTransactionBackend* backend)
{
ASSERT(!m_backend);
m_backend = backend;
}
SQLTransaction::StateFunction SQLTransaction::stateFunctionFor(SQLTransactionState state)
{
static const StateFunction stateFunctions[] = {
&SQLTransaction::unreachableState, &SQLTransaction::unreachableState, &SQLTransaction::unreachableState, &SQLTransaction::unreachableState, &SQLTransaction::sendToBackendState, &SQLTransaction::unreachableState, &SQLTransaction::sendToBackendState, &SQLTransaction::sendToBackendState, &SQLTransaction::deliverTransactionCallback, &SQLTransaction::deliverTransactionErrorCallback, &SQLTransaction::deliverStatementCallback, &SQLTransaction::deliverQuotaIncreaseCallback, &SQLTransaction::deliverSuccessCallback };
ASSERT(WTF_ARRAY_LENGTH(stateFunctions) == static_cast<int>(SQLTransactionState::NumberOfStates));
ASSERT(state < SQLTransactionState::NumberOfStates);
return stateFunctions[static_cast<int>(state)];
}
void SQLTransaction::requestTransitToState(SQLTransactionState nextState)
{
LOG(StorageAPI, "Scheduling %s for transaction %p\n", nameForSQLTransactionState(nextState), this);
m_requestedState = nextState;
m_database->scheduleTransactionCallback(this);
}
SQLTransactionState SQLTransaction::nextStateForTransactionError()
{
ASSERT(m_transactionError);
if (m_errorCallbackWrapper.hasCallback())
return SQLTransactionState::DeliverTransactionErrorCallback;
return SQLTransactionState::CleanupAfterTransactionErrorCallback;
}
SQLTransactionState SQLTransaction::deliverTransactionCallback()
{
bool shouldDeliverErrorCallback = false;
RefPtr<SQLTransactionCallback> callback = m_callbackWrapper.unwrap();
if (callback) {
m_executeSqlAllowed = true;
shouldDeliverErrorCallback = !callback->handleEvent(this);
m_executeSqlAllowed = false;
}
SQLTransactionState nextState = SQLTransactionState::RunStatements;
if (shouldDeliverErrorCallback) {
m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the SQLTransactionCallback was null or threw an exception");
nextState = SQLTransactionState::DeliverTransactionErrorCallback;
}
return nextState;
}
SQLTransactionState SQLTransaction::deliverTransactionErrorCallback()
{
RefPtr<SQLTransactionErrorCallback> errorCallback = m_errorCallbackWrapper.unwrap();
if (errorCallback) {
if (!m_transactionError)
m_transactionError = m_backend->transactionError();
ASSERT(m_transactionError);
errorCallback->handleEvent(m_transactionError.get());
m_transactionError = nullptr;
}
clearCallbackWrappers();
return SQLTransactionState::CleanupAfterTransactionErrorCallback;
}
SQLTransactionState SQLTransaction::deliverStatementCallback()
{
m_executeSqlAllowed = true;
SQLStatement* currentStatement = m_backend->currentStatement();
ASSERT(currentStatement);
bool result = currentStatement->performCallback(this);
m_executeSqlAllowed = false;
if (result) {
m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the statement callback raised an exception or statement error callback did not return false");
return nextStateForTransactionError();
}
return SQLTransactionState::RunStatements;
}
SQLTransactionState SQLTransaction::deliverQuotaIncreaseCallback()
{
ASSERT(m_backend->currentStatement());
bool shouldRetryCurrentStatement = m_database->transactionClient()->didExceedQuota(&database());
m_backend->setShouldRetryCurrentStatement(shouldRetryCurrentStatement);
return SQLTransactionState::RunStatements;
}
SQLTransactionState SQLTransaction::deliverSuccessCallback()
{
RefPtr<VoidCallback> successCallback = m_successCallbackWrapper.unwrap();
if (successCallback)
successCallback->handleEvent();
clearCallbackWrappers();
return SQLTransactionState::CleanupAndTerminate;
}
SQLTransactionState SQLTransaction::unreachableState()
{
ASSERT_NOT_REACHED();
return SQLTransactionState::End;
}
SQLTransactionState SQLTransaction::sendToBackendState()
{
ASSERT(m_nextState != SQLTransactionState::Idle);
m_backend->requestTransitToState(m_nextState);
return SQLTransactionState::Idle;
}
void SQLTransaction::performPendingCallback()
{
computeNextStateAndCleanupIfNeeded();
runStateMachine();
}
void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, RefPtr<SQLStatementCallback>&& callback, RefPtr<SQLStatementErrorCallback>&& callbackError, ExceptionCode& ec)
{
if (!m_executeSqlAllowed || !m_database->opened()) {
ec = INVALID_STATE_ERR;
return;
}
int permissions = DatabaseAuthorizer::ReadWriteMask;
if (!m_database->databaseContext()->allowDatabaseAccess())
permissions |= DatabaseAuthorizer::NoAccessMask;
else if (m_readOnly)
permissions |= DatabaseAuthorizer::ReadOnlyMask;
auto statement = std::make_unique<SQLStatement>(m_database, WTF::move(callback), WTF::move(callbackError));
m_backend->executeSQL(WTF::move(statement), sqlStatement, arguments, permissions);
}
bool SQLTransaction::computeNextStateAndCleanupIfNeeded()
{
if (m_database->opened() && !m_database->isInterrupted()) {
setStateToRequestedState();
ASSERT(m_nextState == SQLTransactionState::End
|| m_nextState == SQLTransactionState::DeliverTransactionCallback
|| m_nextState == SQLTransactionState::DeliverTransactionErrorCallback
|| m_nextState == SQLTransactionState::DeliverStatementCallback
|| m_nextState == SQLTransactionState::DeliverQuotaIncreaseCallback
|| m_nextState == SQLTransactionState::DeliverSuccessCallback);
LOG(StorageAPI, "Callback %s\n", nameForSQLTransactionState(m_nextState));
return false;
}
clearCallbackWrappers();
m_nextState = SQLTransactionState::CleanupAndTerminate;
return true;
}
void SQLTransaction::clearCallbackWrappers()
{
m_callbackWrapper.clear();
m_successCallbackWrapper.clear();
m_errorCallbackWrapper.clear();
}
}