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