|
1 /* |
|
2 * Copyright (C) 2007, 2008 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 Computer, 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(DATABASE) |
|
33 |
|
34 #include "Database.h" |
|
35 #include "DatabaseThread.h" |
|
36 #include "Logging.h" |
|
37 #include "PlatformString.h" |
|
38 #include "ScriptExecutionContext.h" |
|
39 #include "SQLError.h" |
|
40 #include "SQLiteTransaction.h" |
|
41 #include "SQLStatement.h" |
|
42 #include "SQLStatementCallback.h" |
|
43 #include "SQLStatementErrorCallback.h" |
|
44 #include "SQLTransactionCallback.h" |
|
45 #include "SQLTransactionClient.h" |
|
46 #include "SQLTransactionCoordinator.h" |
|
47 #include "SQLTransactionErrorCallback.h" |
|
48 #include "SQLValue.h" |
|
49 #include "VoidCallback.h" |
|
50 #include <wtf/OwnPtr.h> |
|
51 #include <wtf/PassRefPtr.h> |
|
52 #include <wtf/RefPtr.h> |
|
53 |
|
54 // There's no way of knowing exactly how much more space will be required when a statement hits the quota limit. |
|
55 // For now, we'll arbitrarily choose currentQuota + 1mb. |
|
56 // In the future we decide to track if a size increase wasn't enough, and ask for larger-and-larger increases until its enough. |
|
57 static const int DefaultQuotaSizeIncrease = 1048576; |
|
58 |
|
59 namespace WebCore { |
|
60 |
|
61 PassRefPtr<SQLTransaction> SQLTransaction::create(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, |
|
62 PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionWrapper> wrapper, bool readOnly) |
|
63 { |
|
64 return adoptRef(new SQLTransaction(db, callback, errorCallback, successCallback, wrapper, readOnly)); |
|
65 } |
|
66 |
|
67 SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, |
|
68 PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionWrapper> wrapper, bool readOnly) |
|
69 : m_nextStep(&SQLTransaction::acquireLock) |
|
70 , m_executeSqlAllowed(false) |
|
71 , m_database(db) |
|
72 , m_wrapper(wrapper) |
|
73 , m_callback(callback) |
|
74 , m_successCallback(successCallback) |
|
75 , m_errorCallback(errorCallback) |
|
76 , m_shouldRetryCurrentStatement(false) |
|
77 , m_modifiedDatabase(false) |
|
78 , m_lockAcquired(false) |
|
79 , m_readOnly(readOnly) |
|
80 { |
|
81 ASSERT(m_database); |
|
82 } |
|
83 |
|
84 SQLTransaction::~SQLTransaction() |
|
85 { |
|
86 ASSERT(!m_sqliteTransaction); |
|
87 } |
|
88 |
|
89 void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e) |
|
90 { |
|
91 if (!m_executeSqlAllowed || !m_database->opened()) { |
|
92 e = INVALID_STATE_ERR; |
|
93 return; |
|
94 } |
|
95 |
|
96 bool readOnlyMode = m_readOnly; |
|
97 if (!readOnlyMode) { |
|
98 if (m_database->scriptExecutionContext()->isDatabaseReadOnly()) |
|
99 readOnlyMode = true; |
|
100 } |
|
101 |
|
102 RefPtr<SQLStatement> statement = SQLStatement::create(sqlStatement, arguments, callback, callbackError, readOnlyMode); |
|
103 |
|
104 if (m_database->deleted()) |
|
105 statement->setDatabaseDeletedError(); |
|
106 |
|
107 if (!m_database->versionMatchesExpected()) |
|
108 statement->setVersionMismatchedError(); |
|
109 |
|
110 enqueueStatement(statement); |
|
111 } |
|
112 |
|
113 void SQLTransaction::enqueueStatement(PassRefPtr<SQLStatement> statement) |
|
114 { |
|
115 MutexLocker locker(m_statementMutex); |
|
116 m_statementQueue.append(statement); |
|
117 } |
|
118 |
|
119 #ifndef NDEBUG |
|
120 const char* SQLTransaction::debugStepName(SQLTransaction::TransactionStepMethod step) |
|
121 { |
|
122 if (step == &SQLTransaction::acquireLock) |
|
123 return "acquireLock"; |
|
124 else if (step == &SQLTransaction::openTransactionAndPreflight) |
|
125 return "openTransactionAndPreflight"; |
|
126 else if (step == &SQLTransaction::runStatements) |
|
127 return "runStatements"; |
|
128 else if (step == &SQLTransaction::postflightAndCommit) |
|
129 return "postflightAndCommit"; |
|
130 else if (step == &SQLTransaction::cleanupAfterTransactionErrorCallback) |
|
131 return "cleanupAfterTransactionErrorCallback"; |
|
132 else if (step == &SQLTransaction::deliverTransactionCallback) |
|
133 return "deliverTransactionCallback"; |
|
134 else if (step == &SQLTransaction::deliverTransactionErrorCallback) |
|
135 return "deliverTransactionErrorCallback"; |
|
136 else if (step == &SQLTransaction::deliverStatementCallback) |
|
137 return "deliverStatementCallback"; |
|
138 else if (step == &SQLTransaction::deliverQuotaIncreaseCallback) |
|
139 return "deliverQuotaIncreaseCallback"; |
|
140 else if (step == &SQLTransaction::deliverSuccessCallback) |
|
141 return "deliverSuccessCallback"; |
|
142 else if (step == &SQLTransaction::cleanupAfterSuccessCallback) |
|
143 return "cleanupAfterSuccessCallback"; |
|
144 else |
|
145 return "UNKNOWN"; |
|
146 } |
|
147 #endif |
|
148 |
|
149 void SQLTransaction::checkAndHandleClosedDatabase() |
|
150 { |
|
151 if (m_database->opened()) |
|
152 return; |
|
153 |
|
154 // If the database was stopped, don't do anything and cancel queued work |
|
155 LOG(StorageAPI, "Database was stopped - cancelling work for this transaction"); |
|
156 MutexLocker locker(m_statementMutex); |
|
157 m_statementQueue.clear(); |
|
158 m_nextStep = 0; |
|
159 |
|
160 // The next steps should be executed only if we're on the DB thread. |
|
161 if (currentThread() != database()->scriptExecutionContext()->databaseThread()->getThreadID()) |
|
162 return; |
|
163 |
|
164 // The current SQLite transaction should be stopped, as well |
|
165 if (m_sqliteTransaction) { |
|
166 m_sqliteTransaction->stop(); |
|
167 m_sqliteTransaction.clear(); |
|
168 } |
|
169 |
|
170 if (m_lockAcquired) |
|
171 m_database->transactionCoordinator()->releaseLock(this); |
|
172 } |
|
173 |
|
174 |
|
175 bool SQLTransaction::performNextStep() |
|
176 { |
|
177 LOG(StorageAPI, "Step %s\n", debugStepName(m_nextStep)); |
|
178 |
|
179 ASSERT(m_nextStep == &SQLTransaction::acquireLock || |
|
180 m_nextStep == &SQLTransaction::openTransactionAndPreflight || |
|
181 m_nextStep == &SQLTransaction::runStatements || |
|
182 m_nextStep == &SQLTransaction::postflightAndCommit || |
|
183 m_nextStep == &SQLTransaction::cleanupAfterSuccessCallback || |
|
184 m_nextStep == &SQLTransaction::cleanupAfterTransactionErrorCallback); |
|
185 |
|
186 checkAndHandleClosedDatabase(); |
|
187 |
|
188 if (m_nextStep) |
|
189 (this->*m_nextStep)(); |
|
190 |
|
191 // If there is no nextStep after performing the above step, the transaction is complete |
|
192 return !m_nextStep; |
|
193 } |
|
194 |
|
195 void SQLTransaction::performPendingCallback() |
|
196 { |
|
197 LOG(StorageAPI, "Callback %s\n", debugStepName(m_nextStep)); |
|
198 |
|
199 ASSERT(m_nextStep == &SQLTransaction::deliverTransactionCallback || |
|
200 m_nextStep == &SQLTransaction::deliverTransactionErrorCallback || |
|
201 m_nextStep == &SQLTransaction::deliverStatementCallback || |
|
202 m_nextStep == &SQLTransaction::deliverQuotaIncreaseCallback || |
|
203 m_nextStep == &SQLTransaction::deliverSuccessCallback); |
|
204 |
|
205 checkAndHandleClosedDatabase(); |
|
206 |
|
207 if (m_nextStep) |
|
208 (this->*m_nextStep)(); |
|
209 } |
|
210 |
|
211 void SQLTransaction::notifyDatabaseThreadIsShuttingDown() |
|
212 { |
|
213 ASSERT(currentThread() == database()->scriptExecutionContext()->databaseThread()->getThreadID()); |
|
214 |
|
215 // If the transaction is in progress, we should roll it back here, since this is our last |
|
216 // oportunity to do something related to this transaction on the DB thread. |
|
217 // Clearing m_sqliteTransaction invokes SQLiteTransaction's destructor which does just that. |
|
218 m_sqliteTransaction.clear(); |
|
219 } |
|
220 |
|
221 void SQLTransaction::acquireLock() |
|
222 { |
|
223 m_database->transactionCoordinator()->acquireLock(this); |
|
224 } |
|
225 |
|
226 void SQLTransaction::lockAcquired() |
|
227 { |
|
228 m_lockAcquired = true; |
|
229 m_nextStep = &SQLTransaction::openTransactionAndPreflight; |
|
230 LOG(StorageAPI, "Scheduling openTransactionAndPreflight immediately for transaction %p\n", this); |
|
231 m_database->scheduleTransactionStep(this, true); |
|
232 } |
|
233 |
|
234 void SQLTransaction::openTransactionAndPreflight() |
|
235 { |
|
236 ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
|
237 ASSERT(m_lockAcquired); |
|
238 |
|
239 LOG(StorageAPI, "Opening and preflighting transaction %p", this); |
|
240 |
|
241 // If the database was deleted, jump to the error callback |
|
242 if (m_database->deleted()) { |
|
243 m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to open a transaction, because the user deleted the database"); |
|
244 handleTransactionError(false); |
|
245 return; |
|
246 } |
|
247 |
|
248 // Set the maximum usage for this transaction if this transactions is not read-only |
|
249 if (!m_readOnly) |
|
250 m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); |
|
251 |
|
252 ASSERT(!m_sqliteTransaction); |
|
253 m_sqliteTransaction.set(new SQLiteTransaction(m_database->sqliteDatabase(), m_readOnly)); |
|
254 |
|
255 m_database->resetDeletes(); |
|
256 m_database->disableAuthorizer(); |
|
257 m_sqliteTransaction->begin(); |
|
258 m_database->enableAuthorizer(); |
|
259 |
|
260 // Transaction Steps 1+2 - Open a transaction to the database, jumping to the error callback if that fails |
|
261 if (!m_sqliteTransaction->inProgress()) { |
|
262 ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
|
263 m_sqliteTransaction.clear(); |
|
264 m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to open a transaction to the database"); |
|
265 handleTransactionError(false); |
|
266 return; |
|
267 } |
|
268 |
|
269 // Transaction Steps 3 - Peform preflight steps, jumping to the error callback if they fail |
|
270 if (m_wrapper && !m_wrapper->performPreflight(this)) { |
|
271 ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
|
272 m_sqliteTransaction.clear(); |
|
273 m_transactionError = m_wrapper->sqlError(); |
|
274 if (!m_transactionError) |
|
275 m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occured setting up transaction"); |
|
276 |
|
277 handleTransactionError(false); |
|
278 return; |
|
279 } |
|
280 |
|
281 // Transaction Step 4 - Invoke the transaction callback with the new SQLTransaction object |
|
282 m_nextStep = &SQLTransaction::deliverTransactionCallback; |
|
283 LOG(StorageAPI, "Scheduling deliverTransactionCallback for transaction %p\n", this); |
|
284 m_database->scheduleTransactionCallback(this); |
|
285 } |
|
286 |
|
287 void SQLTransaction::deliverTransactionCallback() |
|
288 { |
|
289 bool shouldDeliverErrorCallback = false; |
|
290 |
|
291 if (m_callback) { |
|
292 m_executeSqlAllowed = true; |
|
293 shouldDeliverErrorCallback = !m_callback->handleEvent(m_database->scriptExecutionContext(), this); |
|
294 m_executeSqlAllowed = false; |
|
295 } else |
|
296 shouldDeliverErrorCallback = true; |
|
297 |
|
298 // Transaction Step 5 - If the transaction callback was null or raised an exception, jump to the error callback |
|
299 if (shouldDeliverErrorCallback) { |
|
300 m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the SQLTransactionCallback was null or threw an exception"); |
|
301 deliverTransactionErrorCallback(); |
|
302 } else |
|
303 scheduleToRunStatements(); |
|
304 } |
|
305 |
|
306 void SQLTransaction::scheduleToRunStatements() |
|
307 { |
|
308 m_nextStep = &SQLTransaction::runStatements; |
|
309 LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this); |
|
310 m_database->scheduleTransactionStep(this); |
|
311 } |
|
312 |
|
313 void SQLTransaction::runStatements() |
|
314 { |
|
315 ASSERT(m_lockAcquired); |
|
316 |
|
317 // If there is a series of statements queued up that are all successful and have no associated |
|
318 // SQLStatementCallback objects, then we can burn through the queue |
|
319 do { |
|
320 if (m_shouldRetryCurrentStatement && !m_sqliteTransaction->wasRolledBackBySqlite()) { |
|
321 m_shouldRetryCurrentStatement = false; |
|
322 // FIXME - Another place that needs fixing up after <rdar://problem/5628468> is addressed. |
|
323 // See ::openTransactionAndPreflight() for discussion |
|
324 |
|
325 // Reset the maximum size here, as it was increased to allow us to retry this statement. |
|
326 // m_shouldRetryCurrentStatement is set to true only when a statement exceeds |
|
327 // the quota, which can happen only in a read-write transaction. Therefore, there |
|
328 // is no need to check here if the transaction is read-write. |
|
329 m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); |
|
330 } else { |
|
331 // If the current statement has already been run, failed due to quota constraints, and we're not retrying it, |
|
332 // that means it ended in an error. Handle it now |
|
333 if (m_currentStatement && m_currentStatement->lastExecutionFailedDueToQuota()) { |
|
334 handleCurrentStatementError(); |
|
335 break; |
|
336 } |
|
337 |
|
338 // Otherwise, advance to the next statement |
|
339 getNextStatement(); |
|
340 } |
|
341 } while (runCurrentStatement()); |
|
342 |
|
343 // If runCurrentStatement() returned false, that means either there was no current statement to run, |
|
344 // or the current statement requires a callback to complete. In the later case, it also scheduled |
|
345 // the callback or performed any other additional work so we can return |
|
346 if (!m_currentStatement) |
|
347 postflightAndCommit(); |
|
348 } |
|
349 |
|
350 void SQLTransaction::getNextStatement() |
|
351 { |
|
352 m_currentStatement = 0; |
|
353 |
|
354 MutexLocker locker(m_statementMutex); |
|
355 if (!m_statementQueue.isEmpty()) { |
|
356 m_currentStatement = m_statementQueue.takeFirst(); |
|
357 } |
|
358 } |
|
359 |
|
360 bool SQLTransaction::runCurrentStatement() |
|
361 { |
|
362 if (!m_currentStatement) |
|
363 return false; |
|
364 |
|
365 m_database->resetAuthorizer(); |
|
366 |
|
367 if (m_currentStatement->execute(m_database.get())) { |
|
368 if (m_database->lastActionChangedDatabase()) { |
|
369 // Flag this transaction as having changed the database for later delegate notification |
|
370 m_modifiedDatabase = true; |
|
371 // Also dirty the size of this database file for calculating quota usage |
|
372 m_database->transactionClient()->didExecuteStatement(database()); |
|
373 } |
|
374 |
|
375 if (m_currentStatement->hasStatementCallback()) { |
|
376 m_nextStep = &SQLTransaction::deliverStatementCallback; |
|
377 LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this); |
|
378 m_database->scheduleTransactionCallback(this); |
|
379 return false; |
|
380 } |
|
381 return true; |
|
382 } |
|
383 |
|
384 if (m_currentStatement->lastExecutionFailedDueToQuota()) { |
|
385 m_nextStep = &SQLTransaction::deliverQuotaIncreaseCallback; |
|
386 LOG(StorageAPI, "Scheduling deliverQuotaIncreaseCallback for transaction %p\n", this); |
|
387 m_database->scheduleTransactionCallback(this); |
|
388 return false; |
|
389 } |
|
390 |
|
391 handleCurrentStatementError(); |
|
392 |
|
393 return false; |
|
394 } |
|
395 |
|
396 void SQLTransaction::handleCurrentStatementError() |
|
397 { |
|
398 // Transaction Steps 6.error - Call the statement's error callback, but if there was no error callback, |
|
399 // or the transaction was rolled back, jump to the transaction error callback |
|
400 if (m_currentStatement->hasStatementErrorCallback() && !m_sqliteTransaction->wasRolledBackBySqlite()) { |
|
401 m_nextStep = &SQLTransaction::deliverStatementCallback; |
|
402 LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this); |
|
403 m_database->scheduleTransactionCallback(this); |
|
404 } else { |
|
405 m_transactionError = m_currentStatement->sqlError(); |
|
406 if (!m_transactionError) |
|
407 m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "the statement failed to execute"); |
|
408 handleTransactionError(false); |
|
409 } |
|
410 } |
|
411 |
|
412 void SQLTransaction::deliverStatementCallback() |
|
413 { |
|
414 ASSERT(m_currentStatement); |
|
415 |
|
416 // Transaction Step 6.6 and 6.3(error) - If the statement callback went wrong, jump to the transaction error callback |
|
417 // Otherwise, continue to loop through the statement queue |
|
418 m_executeSqlAllowed = true; |
|
419 bool result = m_currentStatement->performCallback(this); |
|
420 m_executeSqlAllowed = false; |
|
421 |
|
422 if (result) { |
|
423 m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the statement callback raised an exception or statement error callback did not return false"); |
|
424 handleTransactionError(true); |
|
425 } else |
|
426 scheduleToRunStatements(); |
|
427 } |
|
428 |
|
429 void SQLTransaction::deliverQuotaIncreaseCallback() |
|
430 { |
|
431 ASSERT(m_currentStatement); |
|
432 ASSERT(!m_shouldRetryCurrentStatement); |
|
433 |
|
434 m_shouldRetryCurrentStatement = m_database->transactionClient()->didExceedQuota(database()); |
|
435 |
|
436 m_nextStep = &SQLTransaction::runStatements; |
|
437 LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this); |
|
438 m_database->scheduleTransactionStep(this); |
|
439 } |
|
440 |
|
441 void SQLTransaction::postflightAndCommit() |
|
442 { |
|
443 ASSERT(m_lockAcquired); |
|
444 |
|
445 // Transaction Step 7 - Peform postflight steps, jumping to the error callback if they fail |
|
446 if (m_wrapper && !m_wrapper->performPostflight(this)) { |
|
447 m_transactionError = m_wrapper->sqlError(); |
|
448 if (!m_transactionError) |
|
449 m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occured setting up transaction"); |
|
450 handleTransactionError(false); |
|
451 return; |
|
452 } |
|
453 |
|
454 // Transacton Step 8+9 - Commit the transaction, jumping to the error callback if that fails |
|
455 ASSERT(m_sqliteTransaction); |
|
456 |
|
457 m_database->disableAuthorizer(); |
|
458 m_sqliteTransaction->commit(); |
|
459 m_database->enableAuthorizer(); |
|
460 |
|
461 // If the commit failed, the transaction will still be marked as "in progress" |
|
462 if (m_sqliteTransaction->inProgress()) { |
|
463 m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "failed to commit the transaction"); |
|
464 handleTransactionError(false); |
|
465 return; |
|
466 } |
|
467 |
|
468 // Vacuum the database if anything was deleted. |
|
469 if (m_database->hadDeletes()) |
|
470 m_database->incrementalVacuumIfNeeded(); |
|
471 |
|
472 // The commit was successful. If the transaction modified this database, notify the delegates. |
|
473 if (m_modifiedDatabase) |
|
474 m_database->transactionClient()->didCommitWriteTransaction(database()); |
|
475 |
|
476 // Now release our unneeded callbacks, to break reference cycles. |
|
477 m_callback = 0; |
|
478 m_errorCallback = 0; |
|
479 |
|
480 // Transaction Step 10 - Deliver success callback, if there is one |
|
481 if (m_successCallback) { |
|
482 m_nextStep = &SQLTransaction::deliverSuccessCallback; |
|
483 LOG(StorageAPI, "Scheduling deliverSuccessCallback for transaction %p\n", this); |
|
484 m_database->scheduleTransactionCallback(this); |
|
485 } else |
|
486 cleanupAfterSuccessCallback(); |
|
487 } |
|
488 |
|
489 void SQLTransaction::deliverSuccessCallback() |
|
490 { |
|
491 // Transaction Step 10 - Deliver success callback |
|
492 ASSERT(m_successCallback); |
|
493 m_successCallback->handleEvent(); |
|
494 |
|
495 // Release the last callback to break reference cycle |
|
496 m_successCallback = 0; |
|
497 |
|
498 // Schedule a "post-success callback" step to return control to the database thread in case there |
|
499 // are further transactions queued up for this Database |
|
500 m_nextStep = &SQLTransaction::cleanupAfterSuccessCallback; |
|
501 LOG(StorageAPI, "Scheduling cleanupAfterSuccessCallback for transaction %p\n", this); |
|
502 m_database->scheduleTransactionStep(this); |
|
503 } |
|
504 |
|
505 void SQLTransaction::cleanupAfterSuccessCallback() |
|
506 { |
|
507 ASSERT(m_lockAcquired); |
|
508 |
|
509 // Transaction Step 11 - End transaction steps |
|
510 // There is no next step |
|
511 LOG(StorageAPI, "Transaction %p is complete\n", this); |
|
512 ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
|
513 m_sqliteTransaction.clear(); |
|
514 m_nextStep = 0; |
|
515 |
|
516 // Release the lock on this database |
|
517 m_database->transactionCoordinator()->releaseLock(this); |
|
518 } |
|
519 |
|
520 void SQLTransaction::handleTransactionError(bool inCallback) |
|
521 { |
|
522 if (m_errorCallback) { |
|
523 if (inCallback) |
|
524 deliverTransactionErrorCallback(); |
|
525 else { |
|
526 m_nextStep = &SQLTransaction::deliverTransactionErrorCallback; |
|
527 LOG(StorageAPI, "Scheduling deliverTransactionErrorCallback for transaction %p\n", this); |
|
528 m_database->scheduleTransactionCallback(this); |
|
529 } |
|
530 return; |
|
531 } |
|
532 |
|
533 // No error callback, so fast-forward to: |
|
534 // Transaction Step 12 - Rollback the transaction. |
|
535 if (inCallback) { |
|
536 m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback; |
|
537 LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this); |
|
538 m_database->scheduleTransactionStep(this); |
|
539 } else { |
|
540 cleanupAfterTransactionErrorCallback(); |
|
541 } |
|
542 } |
|
543 |
|
544 void SQLTransaction::deliverTransactionErrorCallback() |
|
545 { |
|
546 ASSERT(m_transactionError); |
|
547 |
|
548 // Transaction Step 12 - If exists, invoke error callback with the last |
|
549 // error to have occurred in this transaction. |
|
550 if (m_errorCallback) |
|
551 m_errorCallback->handleEvent(m_database->scriptExecutionContext(), m_transactionError.get()); |
|
552 |
|
553 m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback; |
|
554 LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this); |
|
555 m_database->scheduleTransactionStep(this); |
|
556 } |
|
557 |
|
558 void SQLTransaction::cleanupAfterTransactionErrorCallback() |
|
559 { |
|
560 ASSERT(m_lockAcquired); |
|
561 |
|
562 m_database->disableAuthorizer(); |
|
563 if (m_sqliteTransaction) { |
|
564 // Transaction Step 12 - Rollback the transaction. |
|
565 m_sqliteTransaction->rollback(); |
|
566 |
|
567 ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
|
568 m_sqliteTransaction.clear(); |
|
569 } |
|
570 m_database->enableAuthorizer(); |
|
571 |
|
572 // Transaction Step 12 - Any still-pending statements in the transaction are discarded. |
|
573 { |
|
574 MutexLocker locker(m_statementMutex); |
|
575 m_statementQueue.clear(); |
|
576 } |
|
577 |
|
578 // Transaction is complete! There is no next step |
|
579 LOG(StorageAPI, "Transaction %p is complete with an error\n", this); |
|
580 ASSERT(!m_database->sqliteDatabase().transactionInProgress()); |
|
581 m_nextStep = 0; |
|
582 |
|
583 // Now release our callbacks, to break reference cycles. |
|
584 m_callback = 0; |
|
585 m_errorCallback = 0; |
|
586 |
|
587 // Now release the lock on this database |
|
588 m_database->transactionCoordinator()->releaseLock(this); |
|
589 } |
|
590 |
|
591 } // namespace WebCore |
|
592 |
|
593 #endif // ENABLE(DATABASE) |