1/* 2 * Copyright (C) 2007, 2008, 2013 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 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(SQL_DATABASE) 33 34#include "AbstractSQLTransactionBackend.h" 35#include "Database.h" 36#include "DatabaseAuthorizer.h" 37#include "DatabaseContext.h" 38#include "ExceptionCode.h" 39#include "Logging.h" 40#include "SQLError.h" 41#include "SQLStatementCallback.h" 42#include "SQLStatementErrorCallback.h" 43#include "SQLTransactionCallback.h" 44#include "SQLTransactionClient.h" // FIXME: Should be used in the backend only. 45#include "SQLTransactionErrorCallback.h" 46#include "VoidCallback.h" 47#include <wtf/StdLibExtras.h> 48#include <wtf/Vector.h> 49 50namespace WebCore { 51 52PassRefPtr<SQLTransaction> SQLTransaction::create(Database* db, PassRefPtr<SQLTransactionCallback> callback, 53 PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, 54 bool readOnly) 55{ 56 return adoptRef(new SQLTransaction(db, callback, successCallback, errorCallback, readOnly)); 57} 58 59SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> callback, 60 PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, 61 bool readOnly) 62 : m_database(db) 63 , m_callbackWrapper(callback, db->scriptExecutionContext()) 64 , m_successCallbackWrapper(successCallback, db->scriptExecutionContext()) 65 , m_errorCallbackWrapper(errorCallback, db->scriptExecutionContext()) 66 , m_executeSqlAllowed(false) 67 , m_readOnly(readOnly) 68{ 69 ASSERT(m_database); 70} 71 72bool SQLTransaction::hasCallback() const 73{ 74 return m_callbackWrapper.hasCallback(); 75} 76 77bool SQLTransaction::hasSuccessCallback() const 78{ 79 return m_successCallbackWrapper.hasCallback(); 80} 81 82bool SQLTransaction::hasErrorCallback() const 83{ 84 return m_errorCallbackWrapper.hasCallback(); 85} 86 87void SQLTransaction::setBackend(AbstractSQLTransactionBackend* backend) 88{ 89 ASSERT(!m_backend); 90 m_backend = backend; 91} 92 93SQLTransaction::StateFunction SQLTransaction::stateFunctionFor(SQLTransactionState state) 94{ 95 static const StateFunction stateFunctions[] = { 96 &SQLTransaction::unreachableState, // 0. illegal 97 &SQLTransaction::unreachableState, // 1. idle 98 &SQLTransaction::unreachableState, // 2. acquireLock 99 &SQLTransaction::unreachableState, // 3. openTransactionAndPreflight 100 &SQLTransaction::sendToBackendState, // 4. runStatements 101 &SQLTransaction::unreachableState, // 5. postflightAndCommit 102 &SQLTransaction::sendToBackendState, // 6. cleanupAndTerminate 103 &SQLTransaction::sendToBackendState, // 7. cleanupAfterTransactionErrorCallback 104 &SQLTransaction::deliverTransactionCallback, // 8. 105 &SQLTransaction::deliverTransactionErrorCallback, // 9. 106 &SQLTransaction::deliverStatementCallback, // 10. 107 &SQLTransaction::deliverQuotaIncreaseCallback, // 11. 108 &SQLTransaction::deliverSuccessCallback // 12. 109 }; 110 111 ASSERT(WTF_ARRAY_LENGTH(stateFunctions) == static_cast<int>(SQLTransactionState::NumberOfStates)); 112 ASSERT(state < SQLTransactionState::NumberOfStates); 113 114 return stateFunctions[static_cast<int>(state)]; 115} 116 117// requestTransitToState() can be called from the backend. Hence, it should 118// NOT be modifying SQLTransactionBackend in general. The only safe field to 119// modify is m_requestedState which is meant for this purpose. 120void SQLTransaction::requestTransitToState(SQLTransactionState nextState) 121{ 122 LOG(StorageAPI, "Scheduling %s for transaction %p\n", nameForSQLTransactionState(nextState), this); 123 m_requestedState = nextState; 124 m_database->scheduleTransactionCallback(this); 125} 126 127SQLTransactionState SQLTransaction::nextStateForTransactionError() 128{ 129 ASSERT(m_transactionError); 130 if (m_errorCallbackWrapper.hasCallback()) 131 return SQLTransactionState::DeliverTransactionErrorCallback; 132 133 // No error callback, so fast-forward to: 134 // Transaction Step 11 - Rollback the transaction. 135 return SQLTransactionState::CleanupAfterTransactionErrorCallback; 136} 137 138SQLTransactionState SQLTransaction::deliverTransactionCallback() 139{ 140 bool shouldDeliverErrorCallback = false; 141 142 // Spec 4.3.2 4: Invoke the transaction callback with the new SQLTransaction object 143 RefPtr<SQLTransactionCallback> callback = m_callbackWrapper.unwrap(); 144 if (callback) { 145 m_executeSqlAllowed = true; 146 shouldDeliverErrorCallback = !callback->handleEvent(this); 147 m_executeSqlAllowed = false; 148 } 149 150 // Spec 4.3.2 5: If the transaction callback was null or raised an exception, jump to the error callback 151 SQLTransactionState nextState = SQLTransactionState::RunStatements; 152 if (shouldDeliverErrorCallback) { 153 m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the SQLTransactionCallback was null or threw an exception"); 154 nextState = SQLTransactionState::DeliverTransactionErrorCallback; 155 } 156 return nextState; 157} 158 159SQLTransactionState SQLTransaction::deliverTransactionErrorCallback() 160{ 161 // Spec 4.3.2.10: If exists, invoke error callback with the last 162 // error to have occurred in this transaction. 163 RefPtr<SQLTransactionErrorCallback> errorCallback = m_errorCallbackWrapper.unwrap(); 164 if (errorCallback) { 165 // If we get here with an empty m_transactionError, then the backend 166 // must be waiting in the idle state waiting for this state to finish. 167 // Hence, it's thread safe to fetch the backend transactionError without 168 // a lock. 169 if (!m_transactionError) 170 m_transactionError = m_backend->transactionError(); 171 172 ASSERT(m_transactionError); 173 errorCallback->handleEvent(m_transactionError.get()); 174 175 m_transactionError = 0; 176 } 177 178 clearCallbackWrappers(); 179 180 // Spec 4.3.2.10: Rollback the transaction. 181 return SQLTransactionState::CleanupAfterTransactionErrorCallback; 182} 183 184SQLTransactionState SQLTransaction::deliverStatementCallback() 185{ 186 // Spec 4.3.2.6.6 and 4.3.2.6.3: If the statement callback went wrong, jump to the transaction error callback 187 // Otherwise, continue to loop through the statement queue 188 m_executeSqlAllowed = true; 189 190 AbstractSQLStatement* currentAbstractStatement = m_backend->currentStatement(); 191 SQLStatement* currentStatement = static_cast<SQLStatement*>(currentAbstractStatement); 192 ASSERT(currentStatement); 193 194 bool result = currentStatement->performCallback(this); 195 196 m_executeSqlAllowed = false; 197 198 if (result) { 199 m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the statement callback raised an exception or statement error callback did not return false"); 200 return nextStateForTransactionError(); 201 } 202 return SQLTransactionState::RunStatements; 203} 204 205SQLTransactionState SQLTransaction::deliverQuotaIncreaseCallback() 206{ 207 ASSERT(m_backend->currentStatement()); 208 209 bool shouldRetryCurrentStatement = m_database->transactionClient()->didExceedQuota(database()); 210 m_backend->setShouldRetryCurrentStatement(shouldRetryCurrentStatement); 211 212 return SQLTransactionState::RunStatements; 213} 214 215SQLTransactionState SQLTransaction::deliverSuccessCallback() 216{ 217 // Spec 4.3.2.8: Deliver success callback. 218 RefPtr<VoidCallback> successCallback = m_successCallbackWrapper.unwrap(); 219 if (successCallback) 220 successCallback->handleEvent(); 221 222 clearCallbackWrappers(); 223 224 // Schedule a "post-success callback" step to return control to the database thread in case there 225 // are further transactions queued up for this Database 226 return SQLTransactionState::CleanupAndTerminate; 227} 228 229// This state function is used as a stub function to plug unimplemented states 230// in the state dispatch table. They are unimplemented because they should 231// never be reached in the course of correct execution. 232SQLTransactionState SQLTransaction::unreachableState() 233{ 234 ASSERT_NOT_REACHED(); 235 return SQLTransactionState::End; 236} 237 238SQLTransactionState SQLTransaction::sendToBackendState() 239{ 240 ASSERT(m_nextState != SQLTransactionState::Idle); 241 m_backend->requestTransitToState(m_nextState); 242 return SQLTransactionState::Idle; 243} 244 245void SQLTransaction::performPendingCallback() 246{ 247 computeNextStateAndCleanupIfNeeded(); 248 runStateMachine(); 249} 250 251void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e) 252{ 253 if (!m_executeSqlAllowed || !m_database->opened()) { 254 e = INVALID_STATE_ERR; 255 return; 256 } 257 258 int permissions = DatabaseAuthorizer::ReadWriteMask; 259 if (!m_database->databaseContext()->allowDatabaseAccess()) 260 permissions |= DatabaseAuthorizer::NoAccessMask; 261 else if (m_readOnly) 262 permissions |= DatabaseAuthorizer::ReadOnlyMask; 263 264 auto statement = std::make_unique<SQLStatement>(m_database.get(), callback, callbackError); 265 m_backend->executeSQL(WTF::move(statement), sqlStatement, arguments, permissions); 266} 267 268bool SQLTransaction::computeNextStateAndCleanupIfNeeded() 269{ 270 // Only honor the requested state transition if we're not supposed to be 271 // cleaning up and shutting down: 272 if (m_database->opened() && !m_database->isInterrupted()) { 273 setStateToRequestedState(); 274 ASSERT(m_nextState == SQLTransactionState::End 275 || m_nextState == SQLTransactionState::DeliverTransactionCallback 276 || m_nextState == SQLTransactionState::DeliverTransactionErrorCallback 277 || m_nextState == SQLTransactionState::DeliverStatementCallback 278 || m_nextState == SQLTransactionState::DeliverQuotaIncreaseCallback 279 || m_nextState == SQLTransactionState::DeliverSuccessCallback); 280 281 LOG(StorageAPI, "Callback %s\n", nameForSQLTransactionState(m_nextState)); 282 return false; 283 } 284 285 clearCallbackWrappers(); 286 m_nextState = SQLTransactionState::CleanupAndTerminate; 287 288 return true; 289} 290 291void SQLTransaction::clearCallbackWrappers() 292{ 293 // Release the unneeded callbacks, to break reference cycles. 294 m_callbackWrapper.clear(); 295 m_successCallbackWrapper.clear(); 296 m_errorCallbackWrapper.clear(); 297} 298 299} // namespace WebCore 300 301#endif // ENABLE(SQL_DATABASE) 302