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 | #include "Database.h" |
33 | #include "DatabaseAuthorizer.h" |
34 | #include "DatabaseContext.h" |
35 | #include "DatabaseThread.h" |
36 | #include "DatabaseTracker.h" |
37 | #include "Logging.h" |
38 | #include "OriginLock.h" |
39 | #include "SQLError.h" |
40 | #include "SQLStatement.h" |
41 | #include "SQLStatementCallback.h" |
42 | #include "SQLStatementErrorCallback.h" |
43 | #include "SQLTransactionBackend.h" |
44 | #include "SQLTransactionCallback.h" |
45 | #include "SQLTransactionCoordinator.h" |
46 | #include "SQLTransactionErrorCallback.h" |
47 | #include "SQLiteTransaction.h" |
48 | #include "VoidCallback.h" |
49 | #include <wtf/Optional.h> |
50 | #include <wtf/StdLibExtras.h> |
51 | #include <wtf/Vector.h> |
52 | |
53 | namespace WebCore { |
54 | |
55 | Ref<SQLTransaction> SQLTransaction::create(Ref<Database>&& database, RefPtr<SQLTransactionCallback>&& callback, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<SQLTransactionWrapper>&& wrapper, bool readOnly) |
56 | { |
57 | return adoptRef(*new SQLTransaction(WTFMove(database), WTFMove(callback), WTFMove(successCallback), WTFMove(errorCallback), WTFMove(wrapper), readOnly)); |
58 | } |
59 | |
60 | SQLTransaction::SQLTransaction(Ref<Database>&& database, RefPtr<SQLTransactionCallback>&& callback, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<SQLTransactionWrapper>&& wrapper, bool readOnly) |
61 | : m_database(WTFMove(database)) |
62 | , m_callbackWrapper(WTFMove(callback), &m_database->scriptExecutionContext()) |
63 | , m_successCallbackWrapper(WTFMove(successCallback), &m_database->scriptExecutionContext()) |
64 | , m_errorCallbackWrapper(WTFMove(errorCallback), &m_database->scriptExecutionContext()) |
65 | , m_wrapper(WTFMove(wrapper)) |
66 | , m_nextStep(&SQLTransaction::acquireLock) |
67 | , m_readOnly(readOnly) |
68 | , m_backend(*this) |
69 | { |
70 | } |
71 | |
72 | SQLTransaction::~SQLTransaction() = default; |
73 | |
74 | ExceptionOr<void> SQLTransaction::executeSql(const String& sqlStatement, Optional<Vector<SQLValue>>&& arguments, RefPtr<SQLStatementCallback>&& callback, RefPtr<SQLStatementErrorCallback>&& callbackError) |
75 | { |
76 | if (!m_executeSqlAllowed || !m_database->opened()) |
77 | return Exception { InvalidStateError }; |
78 | |
79 | int permissions = DatabaseAuthorizer::ReadWriteMask; |
80 | if (!m_database->databaseContext().allowDatabaseAccess()) |
81 | permissions |= DatabaseAuthorizer::NoAccessMask; |
82 | else if (m_readOnly) |
83 | permissions |= DatabaseAuthorizer::ReadOnlyMask; |
84 | |
85 | auto statement = std::make_unique<SQLStatement>(m_database, sqlStatement, arguments.valueOr(Vector<SQLValue> { }), WTFMove(callback), WTFMove(callbackError), permissions); |
86 | |
87 | if (m_database->deleted()) |
88 | statement->setDatabaseDeletedError(); |
89 | |
90 | enqueueStatement(WTFMove(statement)); |
91 | |
92 | return { }; |
93 | } |
94 | |
95 | void SQLTransaction::lockAcquired() |
96 | { |
97 | m_lockAcquired = true; |
98 | |
99 | m_backend.m_requestedState = SQLTransactionState::OpenTransactionAndPreflight; |
100 | m_database->scheduleTransactionStep(*this); |
101 | } |
102 | |
103 | void SQLTransaction::performNextStep() |
104 | { |
105 | m_backend.computeNextStateAndCleanupIfNeeded(); |
106 | m_backend.runStateMachine(); |
107 | } |
108 | |
109 | void SQLTransaction::performPendingCallback() |
110 | { |
111 | LOG(StorageAPI, "Callback %s\n" , debugStepName(m_nextStep)); |
112 | |
113 | ASSERT(m_nextStep == &SQLTransaction::deliverTransactionCallback |
114 | || m_nextStep == &SQLTransaction::deliverTransactionErrorCallback |
115 | || m_nextStep == &SQLTransaction::deliverStatementCallback |
116 | || m_nextStep == &SQLTransaction::deliverQuotaIncreaseCallback |
117 | || m_nextStep == &SQLTransaction::deliverSuccessCallback); |
118 | |
119 | checkAndHandleClosedDatabase(); |
120 | |
121 | if (m_nextStep) |
122 | (this->*m_nextStep)(); |
123 | } |
124 | |
125 | void SQLTransaction::notifyDatabaseThreadIsShuttingDown() |
126 | { |
127 | m_backend.notifyDatabaseThreadIsShuttingDown(); |
128 | } |
129 | |
130 | void SQLTransaction::enqueueStatement(std::unique_ptr<SQLStatement> statement) |
131 | { |
132 | LockHolder locker(m_statementMutex); |
133 | m_statementQueue.append(WTFMove(statement)); |
134 | } |
135 | |
136 | SQLTransaction::StateFunction SQLTransaction::stateFunctionFor(SQLTransactionState state) |
137 | { |
138 | static const StateFunction stateFunctions[] = { |
139 | &SQLTransaction::unreachableState, // 0. illegal |
140 | &SQLTransaction::unreachableState, // 1. idle |
141 | &SQLTransaction::unreachableState, // 2. acquireLock |
142 | &SQLTransaction::unreachableState, // 3. openTransactionAndPreflight |
143 | &SQLTransaction::unreachableState, // 4. runStatements |
144 | &SQLTransaction::unreachableState, // 5. postflightAndCommit |
145 | &SQLTransaction::unreachableState, // 6. cleanupAndTerminate |
146 | &SQLTransaction::unreachableState, // 7. cleanupAfterTransactionErrorCallback |
147 | &SQLTransaction::deliverTransactionCallback, // 8. |
148 | &SQLTransaction::deliverTransactionErrorCallback, // 9. |
149 | &SQLTransaction::deliverStatementCallback, // 10. |
150 | &SQLTransaction::deliverQuotaIncreaseCallback, // 11. |
151 | &SQLTransaction::deliverSuccessCallback // 12. |
152 | }; |
153 | |
154 | ASSERT(WTF_ARRAY_LENGTH(stateFunctions) == static_cast<int>(SQLTransactionState::NumberOfStates)); |
155 | ASSERT(state < SQLTransactionState::NumberOfStates); |
156 | |
157 | return stateFunctions[static_cast<int>(state)]; |
158 | } |
159 | |
160 | // requestTransitToState() can be called from the backend. Hence, it should |
161 | // NOT be modifying SQLTransactionBackend in general. The only safe field to |
162 | // modify is m_requestedState which is meant for this purpose. |
163 | void SQLTransaction::requestTransitToState(SQLTransactionState nextState) |
164 | { |
165 | LOG(StorageAPI, "Scheduling %s for transaction %p\n" , nameForSQLTransactionState(nextState), this); |
166 | m_requestedState = nextState; |
167 | m_database->scheduleTransactionCallback(this); |
168 | } |
169 | |
170 | void SQLTransaction::checkAndHandleClosedDatabase() |
171 | { |
172 | if (m_database->opened()) |
173 | return; |
174 | |
175 | // If the database was stopped, don't do anything and cancel queued work |
176 | LOG(StorageAPI, "Database was stopped or interrupted - cancelling work for this transaction" ); |
177 | |
178 | LockHolder locker(m_statementMutex); |
179 | m_statementQueue.clear(); |
180 | m_nextStep = nullptr; |
181 | |
182 | // Release the unneeded callbacks, to break reference cycles. |
183 | m_callbackWrapper.clear(); |
184 | m_successCallbackWrapper.clear(); |
185 | m_errorCallbackWrapper.clear(); |
186 | |
187 | // The next steps should be executed only if we're on the DB thread. |
188 | if (m_database->databaseThread().getThread() != &Thread::current()) |
189 | return; |
190 | |
191 | // The current SQLite transaction should be stopped, as well |
192 | if (m_sqliteTransaction) { |
193 | m_sqliteTransaction->stop(); |
194 | m_sqliteTransaction = nullptr; |
195 | } |
196 | |
197 | if (m_lockAcquired) |
198 | m_database->transactionCoordinator()->releaseLock(*this); |
199 | } |
200 | |
201 | void SQLTransaction::scheduleCallback(void (SQLTransaction::*step)()) |
202 | { |
203 | m_nextStep = step; |
204 | |
205 | LOG(StorageAPI, "Scheduling %s for transaction %p\n" , debugStepName(step), this); |
206 | m_database->scheduleTransactionCallback(this); |
207 | } |
208 | |
209 | void SQLTransaction::acquireLock() |
210 | { |
211 | m_database->transactionCoordinator()->acquireLock(*this); |
212 | } |
213 | |
214 | void SQLTransaction::openTransactionAndPreflight() |
215 | { |
216 | ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
217 | ASSERT(m_lockAcquired); |
218 | |
219 | LOG(StorageAPI, "Opening and preflighting transaction %p" , this); |
220 | |
221 | // If the database was deleted, jump to the error callback |
222 | if (m_database->deleted()) { |
223 | m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to open a transaction, because the user deleted the database" ); |
224 | |
225 | handleTransactionError(); |
226 | return; |
227 | } |
228 | |
229 | // Set the maximum usage for this transaction if this transactions is not read-only |
230 | if (!m_readOnly) { |
231 | acquireOriginLock(); |
232 | m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); |
233 | } |
234 | |
235 | ASSERT(!m_sqliteTransaction); |
236 | m_sqliteTransaction = std::make_unique<SQLiteTransaction>(m_database->sqliteDatabase(), m_readOnly); |
237 | |
238 | m_database->resetDeletes(); |
239 | m_database->disableAuthorizer(); |
240 | m_sqliteTransaction->begin(); |
241 | m_database->enableAuthorizer(); |
242 | |
243 | // Spec 4.3.2.1+2: Open a transaction to the database, jumping to the error callback if that fails |
244 | if (!m_sqliteTransaction->inProgress()) { |
245 | ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
246 | m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to begin transaction" , m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); |
247 | m_sqliteTransaction = nullptr; |
248 | |
249 | handleTransactionError(); |
250 | return; |
251 | } |
252 | |
253 | // Note: We intentionally retrieve the actual version even with an empty expected version. |
254 | // In multi-process browsers, we take this opportinutiy to update the cached value for |
255 | // the actual version. In single-process browsers, this is just a map lookup. |
256 | String actualVersion; |
257 | if (!m_database->getActualVersionForTransaction(actualVersion)) { |
258 | m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to read version" , m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); |
259 | m_database->disableAuthorizer(); |
260 | m_sqliteTransaction = nullptr; |
261 | m_database->enableAuthorizer(); |
262 | |
263 | handleTransactionError(); |
264 | return; |
265 | } |
266 | |
267 | m_hasVersionMismatch = !m_database->expectedVersion().isEmpty() && (m_database->expectedVersion() != actualVersion); |
268 | |
269 | // Spec 4.3.2.3: Perform preflight steps, jumping to the error callback if they fail |
270 | if (m_wrapper && !m_wrapper->performPreflight(*this)) { |
271 | m_database->disableAuthorizer(); |
272 | m_sqliteTransaction = nullptr; |
273 | m_database->enableAuthorizer(); |
274 | m_transactionError = m_wrapper->sqlError(); |
275 | if (!m_transactionError) |
276 | m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction preflight" ); |
277 | |
278 | handleTransactionError(); |
279 | return; |
280 | } |
281 | |
282 | // Spec 4.3.2.4: Invoke the transaction callback with the new SQLTransaction object |
283 | if (m_callbackWrapper.hasCallback()) { |
284 | scheduleCallback(&SQLTransaction::deliverTransactionCallback); |
285 | return; |
286 | } |
287 | |
288 | // If we have no callback to make, skip pass to the state after: |
289 | runStatements(); |
290 | } |
291 | |
292 | void SQLTransaction::runStatements() |
293 | { |
294 | ASSERT(m_lockAcquired); |
295 | |
296 | // If there is a series of statements queued up that are all successful and have no associated |
297 | // SQLStatementCallback objects, then we can burn through the queue |
298 | do { |
299 | if (m_shouldRetryCurrentStatement && !m_sqliteTransaction->wasRolledBackBySqlite()) { |
300 | m_shouldRetryCurrentStatement = false; |
301 | // FIXME - Another place that needs fixing up after <rdar://problem/5628468> is addressed. |
302 | // See ::openTransactionAndPreflight() for discussion |
303 | |
304 | // Reset the maximum size here, as it was increased to allow us to retry this statement. |
305 | // m_shouldRetryCurrentStatement is set to true only when a statement exceeds |
306 | // the quota, which can happen only in a read-write transaction. Therefore, there |
307 | // is no need to check here if the transaction is read-write. |
308 | m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); |
309 | } else { |
310 | // If the current statement has already been run, failed due to quota constraints, and we're not retrying it, |
311 | // that means it ended in an error. Handle it now |
312 | if (m_currentStatement && m_currentStatement->lastExecutionFailedDueToQuota()) { |
313 | handleCurrentStatementError(); |
314 | break; |
315 | } |
316 | |
317 | // Otherwise, advance to the next statement |
318 | getNextStatement(); |
319 | } |
320 | } while (runCurrentStatement()); |
321 | |
322 | // If runCurrentStatement() returned false, that means either there was no current statement to run, |
323 | // or the current statement requires a callback to complete. In the later case, it also scheduled |
324 | // the callback or performed any other additional work so we can return. |
325 | if (!m_currentStatement) |
326 | postflightAndCommit(); |
327 | } |
328 | |
329 | void SQLTransaction::cleanupAndTerminate() |
330 | { |
331 | ASSERT(m_lockAcquired); |
332 | |
333 | // Spec 4.3.2.9: End transaction steps. There is no next step. |
334 | LOG(StorageAPI, "Transaction %p is complete\n" , this); |
335 | ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
336 | |
337 | // Phase 5 cleanup. See comment on the SQLTransaction life-cycle above. |
338 | m_backend.doCleanup(); |
339 | m_database->inProgressTransactionCompleted(); |
340 | } |
341 | |
342 | void SQLTransaction::cleanupAfterTransactionErrorCallback() |
343 | { |
344 | ASSERT(m_lockAcquired); |
345 | |
346 | LOG(StorageAPI, "Transaction %p is complete with an error\n" , this); |
347 | m_database->disableAuthorizer(); |
348 | if (m_sqliteTransaction) { |
349 | // Spec 4.3.2.10: Rollback the transaction. |
350 | m_sqliteTransaction->rollback(); |
351 | |
352 | ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
353 | m_sqliteTransaction = nullptr; |
354 | } |
355 | m_database->enableAuthorizer(); |
356 | |
357 | releaseOriginLockIfNeeded(); |
358 | |
359 | ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
360 | |
361 | cleanupAndTerminate(); |
362 | } |
363 | |
364 | void SQLTransaction::deliverTransactionCallback() |
365 | { |
366 | bool shouldDeliverErrorCallback = false; |
367 | |
368 | // Spec 4.3.2 4: Invoke the transaction callback with the new SQLTransaction object |
369 | RefPtr<SQLTransactionCallback> callback = m_callbackWrapper.unwrap(); |
370 | if (callback) { |
371 | m_executeSqlAllowed = true; |
372 | |
373 | auto result = callback->handleEvent(*this); |
374 | shouldDeliverErrorCallback = result.type() == CallbackResultType::ExceptionThrown; |
375 | |
376 | m_executeSqlAllowed = false; |
377 | } |
378 | |
379 | // Spec 4.3.2 5: If the transaction callback was null or raised an exception, jump to the error callback |
380 | if (shouldDeliverErrorCallback) { |
381 | m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the SQLTransactionCallback was null or threw an exception" ); |
382 | return deliverTransactionErrorCallback(); |
383 | } |
384 | |
385 | m_backend.requestTransitToState(SQLTransactionState::RunStatements); |
386 | } |
387 | |
388 | void SQLTransaction::deliverTransactionErrorCallback() |
389 | { |
390 | ASSERT(m_transactionError); |
391 | |
392 | // Spec 4.3.2.10: If exists, invoke error callback with the last |
393 | // error to have occurred in this transaction. |
394 | RefPtr<SQLTransactionErrorCallback> errorCallback = m_errorCallbackWrapper.unwrap(); |
395 | if (errorCallback) |
396 | errorCallback->handleEvent(*m_transactionError); |
397 | |
398 | clearCallbackWrappers(); |
399 | |
400 | // Spec 4.3.2.10: Rollback the transaction. |
401 | m_backend.requestTransitToState(SQLTransactionState::CleanupAfterTransactionErrorCallback); |
402 | } |
403 | |
404 | void SQLTransaction::deliverStatementCallback() |
405 | { |
406 | ASSERT(m_currentStatement); |
407 | |
408 | // Spec 4.3.2.6.6 and 4.3.2.6.3: If the statement callback went wrong, jump to the transaction error callback |
409 | // Otherwise, continue to loop through the statement queue |
410 | m_executeSqlAllowed = true; |
411 | bool result = m_currentStatement->performCallback(*this); |
412 | m_executeSqlAllowed = false; |
413 | |
414 | if (result) { |
415 | m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the statement callback raised an exception or statement error callback did not return false" ); |
416 | |
417 | if (m_errorCallbackWrapper.hasCallback()) |
418 | return deliverTransactionErrorCallback(); |
419 | |
420 | // No error callback, so fast-forward to: |
421 | // Transaction Step 11 - Rollback the transaction. |
422 | m_backend.requestTransitToState(SQLTransactionState::CleanupAfterTransactionErrorCallback); |
423 | return; |
424 | } |
425 | |
426 | m_backend.requestTransitToState(SQLTransactionState::RunStatements); |
427 | } |
428 | |
429 | void SQLTransaction::deliverQuotaIncreaseCallback() |
430 | { |
431 | ASSERT(m_currentStatement); |
432 | ASSERT(!m_shouldRetryCurrentStatement); |
433 | |
434 | m_shouldRetryCurrentStatement = m_database->didExceedQuota(); |
435 | |
436 | m_backend.requestTransitToState(SQLTransactionState::RunStatements); |
437 | } |
438 | |
439 | void SQLTransaction::deliverSuccessCallback() |
440 | { |
441 | // Spec 4.3.2.8: Deliver success callback. |
442 | RefPtr<VoidCallback> successCallback = m_successCallbackWrapper.unwrap(); |
443 | if (successCallback) |
444 | successCallback->handleEvent(); |
445 | |
446 | clearCallbackWrappers(); |
447 | |
448 | // Schedule a "post-success callback" step to return control to the database thread in case there |
449 | // are further transactions queued up for this Database |
450 | m_backend.requestTransitToState(SQLTransactionState::CleanupAndTerminate); |
451 | } |
452 | |
453 | // This state function is used as a stub function to plug unimplemented states |
454 | // in the state dispatch table. They are unimplemented because they should |
455 | // never be reached in the course of correct execution. |
456 | void SQLTransaction::unreachableState() |
457 | { |
458 | ASSERT_NOT_REACHED(); |
459 | } |
460 | |
461 | void SQLTransaction::computeNextStateAndCleanupIfNeeded() |
462 | { |
463 | // Only honor the requested state transition if we're not supposed to be |
464 | // cleaning up and shutting down: |
465 | if (m_database->opened()) { |
466 | setStateToRequestedState(); |
467 | ASSERT(m_nextState == SQLTransactionState::End |
468 | || m_nextState == SQLTransactionState::DeliverTransactionCallback |
469 | || m_nextState == SQLTransactionState::DeliverTransactionErrorCallback |
470 | || m_nextState == SQLTransactionState::DeliverStatementCallback |
471 | || m_nextState == SQLTransactionState::DeliverQuotaIncreaseCallback |
472 | || m_nextState == SQLTransactionState::DeliverSuccessCallback); |
473 | |
474 | LOG(StorageAPI, "Callback %s\n" , nameForSQLTransactionState(m_nextState)); |
475 | return; |
476 | } |
477 | |
478 | clearCallbackWrappers(); |
479 | m_backend.requestTransitToState(SQLTransactionState::CleanupAndTerminate); |
480 | } |
481 | |
482 | void SQLTransaction::clearCallbackWrappers() |
483 | { |
484 | // Release the unneeded callbacks, to break reference cycles. |
485 | m_callbackWrapper.clear(); |
486 | m_successCallbackWrapper.clear(); |
487 | m_errorCallbackWrapper.clear(); |
488 | } |
489 | |
490 | void SQLTransaction::getNextStatement() |
491 | { |
492 | m_currentStatement = nullptr; |
493 | |
494 | LockHolder locker(m_statementMutex); |
495 | if (!m_statementQueue.isEmpty()) |
496 | m_currentStatement = m_statementQueue.takeFirst(); |
497 | } |
498 | |
499 | bool SQLTransaction::runCurrentStatement() |
500 | { |
501 | if (!m_currentStatement) { |
502 | // No more statements to run. So move on to the next state. |
503 | return false; |
504 | } |
505 | |
506 | m_database->resetAuthorizer(); |
507 | |
508 | if (m_hasVersionMismatch) |
509 | m_currentStatement->setVersionMismatchedError(); |
510 | |
511 | if (m_currentStatement->execute(m_database)) { |
512 | if (m_database->lastActionChangedDatabase()) { |
513 | // Flag this transaction as having changed the database for later delegate notification |
514 | m_modifiedDatabase = true; |
515 | } |
516 | |
517 | if (m_currentStatement->hasStatementCallback()) { |
518 | scheduleCallback(&SQLTransaction::deliverStatementCallback); |
519 | return false; |
520 | } |
521 | |
522 | // If we get here, then the statement doesn't have a callback to invoke. |
523 | // We can move on to the next statement. Hence, stay in this state. |
524 | return true; |
525 | } |
526 | |
527 | if (m_currentStatement->lastExecutionFailedDueToQuota()) { |
528 | scheduleCallback(&SQLTransaction::deliverQuotaIncreaseCallback); |
529 | return false; |
530 | } |
531 | |
532 | handleCurrentStatementError(); |
533 | return false; |
534 | } |
535 | |
536 | void SQLTransaction::handleCurrentStatementError() |
537 | { |
538 | // Spec 4.3.2.6.6: error - Call the statement's error callback, but if there was no error callback, |
539 | // or the transaction was rolled back, jump to the transaction error callback |
540 | if (m_currentStatement->hasStatementErrorCallback() && !m_sqliteTransaction->wasRolledBackBySqlite()) { |
541 | scheduleCallback(&SQLTransaction::deliverStatementCallback); |
542 | return; |
543 | } |
544 | |
545 | m_transactionError = m_currentStatement->sqlError(); |
546 | if (!m_transactionError) |
547 | m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "the statement failed to execute" ); |
548 | |
549 | handleTransactionError(); |
550 | } |
551 | |
552 | void SQLTransaction::handleTransactionError() |
553 | { |
554 | ASSERT(m_transactionError); |
555 | if (m_errorCallbackWrapper.hasCallback()) { |
556 | scheduleCallback(&SQLTransaction::deliverTransactionErrorCallback); |
557 | return; |
558 | } |
559 | |
560 | // No error callback, so fast-forward to the next state and rollback the |
561 | // transaction. |
562 | m_backend.cleanupAfterTransactionErrorCallback(); |
563 | } |
564 | |
565 | void SQLTransaction::postflightAndCommit() |
566 | { |
567 | ASSERT(m_lockAcquired); |
568 | |
569 | // Spec 4.3.2.7: Perform postflight steps, jumping to the error callback if they fail. |
570 | if (m_wrapper && !m_wrapper->performPostflight(*this)) { |
571 | m_transactionError = m_wrapper->sqlError(); |
572 | if (!m_transactionError) |
573 | m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction postflight" ); |
574 | |
575 | handleTransactionError(); |
576 | return; |
577 | } |
578 | |
579 | // Spec 4.3.2.7: Commit the transaction, jumping to the error callback if that fails. |
580 | ASSERT(m_sqliteTransaction); |
581 | |
582 | m_database->disableAuthorizer(); |
583 | m_sqliteTransaction->commit(); |
584 | m_database->enableAuthorizer(); |
585 | |
586 | releaseOriginLockIfNeeded(); |
587 | |
588 | // If the commit failed, the transaction will still be marked as "in progress" |
589 | if (m_sqliteTransaction->inProgress()) { |
590 | if (m_wrapper) |
591 | m_wrapper->handleCommitFailedAfterPostflight(*this); |
592 | m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to commit transaction" , m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); |
593 | |
594 | handleTransactionError(); |
595 | return; |
596 | } |
597 | |
598 | // Vacuum the database if anything was deleted. |
599 | if (m_database->hadDeletes()) |
600 | m_database->incrementalVacuumIfNeeded(); |
601 | |
602 | // The commit was successful. If the transaction modified this database, notify the delegates. |
603 | if (m_modifiedDatabase) |
604 | m_database->didCommitWriteTransaction(); |
605 | |
606 | // Spec 4.3.2.8: Deliver success callback, if there is one. |
607 | scheduleCallback(&SQLTransaction::deliverSuccessCallback); |
608 | } |
609 | |
610 | void SQLTransaction::acquireOriginLock() |
611 | { |
612 | ASSERT(!m_originLock); |
613 | m_originLock = DatabaseTracker::singleton().originLockFor(m_database->securityOrigin()); |
614 | m_originLock->lock(); |
615 | } |
616 | |
617 | void SQLTransaction::releaseOriginLockIfNeeded() |
618 | { |
619 | if (m_originLock) { |
620 | m_originLock->unlock(); |
621 | m_originLock = nullptr; |
622 | } |
623 | } |
624 | |
625 | #if !LOG_DISABLED |
626 | const char* SQLTransaction::debugStepName(void (SQLTransaction::*step)()) |
627 | { |
628 | if (step == &SQLTransaction::acquireLock) |
629 | return "acquireLock" ; |
630 | if (step == &SQLTransaction::openTransactionAndPreflight) |
631 | return "openTransactionAndPreflight" ; |
632 | if (step == &SQLTransaction::runStatements) |
633 | return "runStatements" ; |
634 | if (step == &SQLTransaction::postflightAndCommit) |
635 | return "postflightAndCommit" ; |
636 | if (step == &SQLTransaction::cleanupAfterTransactionErrorCallback) |
637 | return "cleanupAfterTransactionErrorCallback" ; |
638 | if (step == &SQLTransaction::deliverTransactionCallback) |
639 | return "deliverTransactionCallback" ; |
640 | if (step == &SQLTransaction::deliverTransactionErrorCallback) |
641 | return "deliverTransactionErrorCallback" ; |
642 | if (step == &SQLTransaction::deliverStatementCallback) |
643 | return "deliverStatementCallback" ; |
644 | if (step == &SQLTransaction::deliverQuotaIncreaseCallback) |
645 | return "deliverQuotaIncreaseCallback" ; |
646 | if (step == &SQLTransaction::deliverSuccessCallback) |
647 | return "deliverSuccessCallback" ; |
648 | |
649 | ASSERT_NOT_REACHED(); |
650 | return "UNKNOWN" ; |
651 | } |
652 | #endif |
653 | |
654 | } // namespace WebCore |
655 | |