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