2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #include "SQLTransaction.h"
32 #include "ChromeClient.h"
34 #include "DatabaseAuthorizer.h"
35 #include "DatabaseDetails.h"
36 #include "DatabaseTracker.h"
38 #include "ExceptionCode.h"
40 #include "OriginQuotaManager.h"
42 #include "PlatformString.h"
43 #include "SecurityOrigin.h"
45 #include "SQLiteTransaction.h"
46 #include "SQLResultSet.h"
47 #include "SQLStatement.h"
48 #include "SQLStatementCallback.h"
49 #include "SQLStatementErrorCallback.h"
52 // There's no way of knowing exactly how much more space will be required when a statement hits the quota limit.
53 // For now, we'll arbitrarily choose currentQuota + 1mb.
54 // In the future we decide to track if a size increase wasn't enough, and ask for larger-and-larger increases until its enough.
55 static const int DefaultQuotaSizeIncrease = 1048576;
59 SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback,
60 PassRefPtr<SQLTransactionWrapper> wrapper)
61 : m_nextStep(&SQLTransaction::openTransactionAndPreflight)
62 , m_executeSqlAllowed(false)
65 , m_callback(callback)
66 , m_successCallback(successCallback)
67 , m_errorCallback(errorCallback)
68 , m_shouldRetryCurrentStatement(false)
69 , m_shouldCommitAfterErrorCallback(true)
70 , m_modifiedDatabase(false)
75 SQLTransaction::~SQLTransaction()
79 void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e)
81 if (!m_executeSqlAllowed) {
82 e = INVALID_STATE_ERR;
86 RefPtr<SQLStatement> statement = new SQLStatement(sqlStatement.copy(), arguments, callback, callbackError);
88 if (m_database->deleted())
89 statement->setDatabaseDeletedError();
91 if (!m_database->versionMatchesExpected())
92 statement->setVersionMismatchedError();
94 enqueueStatement(statement);
97 void SQLTransaction::enqueueStatement(PassRefPtr<SQLStatement> statement)
99 MutexLocker locker(m_statementMutex);
100 m_statementQueue.append(statement);
104 const char* SQLTransaction::debugStepName(SQLTransaction::TransactionStepMethod step)
106 if (step == &SQLTransaction::openTransactionAndPreflight)
107 return "openTransactionAndPreflight";
108 else if (step == &SQLTransaction::runStatements)
109 return "runStatements";
110 else if (step == &SQLTransaction::postflightAndCommit)
111 return "postflightAndCommit";
112 else if (step == &SQLTransaction::cleanupAfterTransactionErrorCallback)
113 return "cleanupAfterTransactionErrorCallback";
114 else if (step == &SQLTransaction::deliverTransactionCallback)
115 return "deliverTransactionCallback";
116 else if (step == &SQLTransaction::deliverTransactionErrorCallback)
117 return "deliverTransactionErrorCallback";
118 else if (step == &SQLTransaction::deliverStatementCallback)
119 return "deliverStatementCallback";
120 else if (step == &SQLTransaction::deliverQuotaIncreaseCallback)
121 return "deliverQuotaIncreaseCallback";
122 else if (step == &SQLTransaction::deliverSuccessCallback)
123 return "deliverSuccessCallback";
124 else if (step == &SQLTransaction::cleanupAfterSuccessCallback)
125 return "cleanupAfterSuccessCallback";
131 bool SQLTransaction::performNextStep()
133 LOG(StorageAPI, "Step %s\n", debugStepName(m_nextStep));
135 ASSERT(m_nextStep == &SQLTransaction::openTransactionAndPreflight ||
136 m_nextStep == &SQLTransaction::runStatements ||
137 m_nextStep == &SQLTransaction::postflightAndCommit ||
138 m_nextStep == &SQLTransaction::cleanupAfterSuccessCallback ||
139 m_nextStep == &SQLTransaction::cleanupAfterTransactionErrorCallback);
141 (this->*m_nextStep)();
143 // If there is no nextStep after performing the above step, the transaction is complete
147 void SQLTransaction::performPendingCallback()
149 LOG(StorageAPI, "Callback %s\n", debugStepName(m_nextStep));
151 ASSERT(m_nextStep == &SQLTransaction::deliverTransactionCallback ||
152 m_nextStep == &SQLTransaction::deliverTransactionErrorCallback ||
153 m_nextStep == &SQLTransaction::deliverStatementCallback ||
154 m_nextStep == &SQLTransaction::deliverQuotaIncreaseCallback ||
155 m_nextStep == &SQLTransaction::deliverSuccessCallback);
157 (this->*m_nextStep)();
160 void SQLTransaction::openTransactionAndPreflight()
162 ASSERT(!m_database->m_sqliteDatabase.transactionInProgress());
164 LOG(StorageAPI, "Opening and preflighting transaction %p", this);
166 // If the database was deleted, jump to the error callback
167 if (m_database->deleted()) {
168 m_transactionError = new SQLError(0, "unable to open a transaction, because the user deleted the database");
169 handleTransactionError(false);
173 // Set the maximum usage for this transaction
174 m_database->m_sqliteDatabase.setMaximumSize(m_database->maximumSize());
176 ASSERT(!m_sqliteTransaction);
177 m_sqliteTransaction.set(new SQLiteTransaction(m_database->m_sqliteDatabase));
179 m_database->m_databaseAuthorizer->disable();
180 m_sqliteTransaction->begin();
181 m_database->m_databaseAuthorizer->enable();
183 // Transaction Steps 1+2 - Open a transaction to the database, jumping to the error callback if that fails
184 if (!m_sqliteTransaction->inProgress()) {
185 ASSERT(!m_database->m_sqliteDatabase.transactionInProgress());
186 m_sqliteTransaction.clear();
187 m_transactionError = new SQLError(0, "unable to open a transaction to the database");
188 handleTransactionError(false);
192 // Transaction Steps 3 - Peform preflight steps, jumping to the error callback if they fail
193 if (m_wrapper && !m_wrapper->performPreflight(this)) {
194 ASSERT(!m_database->m_sqliteDatabase.transactionInProgress());
195 m_sqliteTransaction.clear();
196 m_transactionError = m_wrapper->sqlError();
197 if (!m_transactionError)
198 m_transactionError = new SQLError(0, "unknown error occured setting up transaction");
200 handleTransactionError(false);
204 // Transaction Step 4 - Invoke the transaction callback with the new SQLTransaction object
205 m_nextStep = &SQLTransaction::deliverTransactionCallback;
206 LOG(StorageAPI, "Scheduling deliverTransactionCallback for transaction %p\n", this);
207 m_database->scheduleTransactionCallback(this);
210 void SQLTransaction::deliverTransactionCallback()
212 bool shouldDeliverErrorCallback = false;
215 m_executeSqlAllowed = true;
216 m_callback->handleEvent(this, shouldDeliverErrorCallback);
217 m_executeSqlAllowed = false;
219 shouldDeliverErrorCallback = true;
221 // Transaction Step 5 - If the transaction callback was null or raised an exception, jump to the error callback
222 if (shouldDeliverErrorCallback) {
223 m_transactionError = new SQLError(0, "the SQLTransactionCallback was null or threw an exception");
224 deliverTransactionErrorCallback();
226 scheduleToRunStatements();
229 void SQLTransaction::scheduleToRunStatements()
231 m_nextStep = &SQLTransaction::runStatements;
232 LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this);
233 m_database->scheduleTransactionStep(this);
236 void SQLTransaction::runStatements()
238 // If there is a series of statements queued up that are all successful and have no associated
239 // SQLStatementCallback objects, then we can burn through the queue
241 if (m_shouldRetryCurrentStatement) {
242 m_shouldRetryCurrentStatement = false;
243 // FIXME - Another place that needs fixing up after <rdar://problem/5628468> is addressed.
244 // See ::openTransactionAndPreflight() for discussion
246 // Reset the maximum size here, as it was increased to allow us to retry this statement
247 m_database->m_sqliteDatabase.setMaximumSize(m_database->maximumSize());
249 // If the current statement has already been run, failed due to quota constraints, and we're not retrying it,
250 // that means it ended in an error. Handle it now
251 if (m_currentStatement && m_currentStatement->lastExecutionFailedDueToQuota()) {
252 handleCurrentStatementError();
256 // Otherwise, advance to the next statement
259 } while (runCurrentStatement());
261 // If runCurrentStatement() returned false, that means either there was no current statement to run,
262 // or the current statement requires a callback to complete. In the later case, it also scheduled
263 // the callback or performed any other additional work so we can return
264 if (!m_currentStatement)
265 postflightAndCommit();
268 void SQLTransaction::getNextStatement()
270 m_currentStatement = 0;
272 MutexLocker locker(m_statementMutex);
273 if (!m_statementQueue.isEmpty()) {
274 m_currentStatement = m_statementQueue.first();
275 m_statementQueue.removeFirst();
279 bool SQLTransaction::runCurrentStatement()
281 if (!m_currentStatement)
284 m_database->m_databaseAuthorizer->reset();
286 if (m_currentStatement->execute(m_database)) {
287 if (m_database->m_databaseAuthorizer->lastActionChangedDatabase()) {
288 // Flag this transaction as having changed the database for later delegate notification
289 m_modifiedDatabase = true;
290 // Also dirty the size of this database file for calculating quota usage
291 OriginQuotaManager& manager(DatabaseTracker::tracker().originQuotaManager());
292 Locker<OriginQuotaManager> locker(manager);
294 manager.markDatabase(m_database);
297 if (m_currentStatement->hasStatementCallback()) {
298 m_nextStep = &SQLTransaction::deliverStatementCallback;
299 LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this);
300 m_database->scheduleTransactionCallback(this);
306 if (m_currentStatement->lastExecutionFailedDueToQuota()) {
307 m_nextStep = &SQLTransaction::deliverQuotaIncreaseCallback;
308 LOG(StorageAPI, "Scheduling deliverQuotaIncreaseCallback for transaction %p\n", this);
309 m_database->scheduleTransactionCallback(this);
313 handleCurrentStatementError();
318 void SQLTransaction::handleCurrentStatementError()
320 // Transaction Steps 6.error - Call the statement's error callback, but if there was no error callback,
321 // jump to the transaction error callback
322 if (m_currentStatement->hasStatementErrorCallback()) {
323 m_nextStep = &SQLTransaction::deliverStatementCallback;
324 LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this);
325 m_database->scheduleTransactionCallback(this);
327 m_transactionError = m_currentStatement->sqlError();
328 if (!m_transactionError)
329 m_transactionError = new SQLError(1, "the statement failed to execute");
330 handleTransactionError(false);
334 void SQLTransaction::deliverStatementCallback()
336 ASSERT(m_currentStatement);
338 // Transaction Step 6.6 and 6.3(error) - If the statement callback went wrong, jump to the transaction error callback
339 // Otherwise, continue to loop through the statement queue
340 m_executeSqlAllowed = true;
341 bool result = m_currentStatement->performCallback(this);
342 m_executeSqlAllowed = false;
345 m_transactionError = new SQLError(0, "the statement callback raised an exception or statement error callback did not return false");
346 handleTransactionError(true);
348 scheduleToRunStatements();
351 void SQLTransaction::deliverQuotaIncreaseCallback()
353 ASSERT(m_currentStatement);
354 ASSERT(!m_shouldRetryCurrentStatement);
356 Page* page = m_database->document()->page();
359 RefPtr<SecurityOrigin> origin = m_database->securityOriginCopy();
361 unsigned long long currentQuota = DatabaseTracker::tracker().quotaForOrigin(origin.get());
362 page->chrome()->client()->exceededDatabaseQuota(m_database->document()->frame(), m_database->stringIdentifier());
363 unsigned long long newQuota = DatabaseTracker::tracker().quotaForOrigin(origin.get());
365 // If the new quota ended up being larger than the old quota, we will retry the statement.
366 if (newQuota > currentQuota)
367 m_shouldRetryCurrentStatement = true;
369 m_nextStep = &SQLTransaction::runStatements;
370 LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this);
371 m_database->scheduleTransactionStep(this);
374 void SQLTransaction::postflightAndCommit()
376 // Transaction Step 7 - Peform postflight steps, jumping to the error callback if they fail
377 if (m_wrapper && !m_wrapper->performPostflight(this)) {
378 m_transactionError = m_wrapper->sqlError();
379 if (!m_transactionError)
380 m_transactionError = new SQLError(0, "unknown error occured setting up transaction");
381 handleTransactionError(false);
385 // Transacton Step 8+9 - Commit the transaction, jumping to the error callback if that fails
386 ASSERT(m_sqliteTransaction);
388 m_database->m_databaseAuthorizer->disable();
389 m_sqliteTransaction->commit();
390 m_database->m_databaseAuthorizer->enable();
392 // If the commit failed, the transaction will still be marked as "in progress"
393 if (m_sqliteTransaction->inProgress()) {
394 m_shouldCommitAfterErrorCallback = false;
395 m_transactionError = new SQLError(0, "failed to commit the transaction");
396 handleTransactionError(false);
400 // The commit was successful, notify the delegates if the transaction modified this database
401 if (m_modifiedDatabase)
402 DatabaseTracker::tracker().scheduleNotifyDatabaseChanged(m_database->m_securityOrigin.get(), m_database->m_name);
404 // Now release our unneeded callbacks, to break reference cycles.
408 // Transaction Step 10 - Deliver success callback, if there is one
409 if (m_successCallback) {
410 m_nextStep = &SQLTransaction::deliverSuccessCallback;
411 LOG(StorageAPI, "Scheduling deliverSuccessCallback for transaction %p\n", this);
412 m_database->scheduleTransactionCallback(this);
414 cleanupAfterSuccessCallback();
417 void SQLTransaction::deliverSuccessCallback()
419 // Transaction Step 10 - Deliver success callback
420 ASSERT(m_successCallback);
421 m_successCallback->handleEvent();
423 // Release the last callback to break reference cycle
424 m_successCallback = 0;
426 // Schedule a "post-success callback" step to return control to the database thread in case there
427 // are further transactions queued up for this Database
428 m_nextStep = &SQLTransaction::cleanupAfterSuccessCallback;
429 LOG(StorageAPI, "Scheduling cleanupAfterSuccessCallback for transaction %p\n", this);
430 m_database->scheduleTransactionStep(this);
433 void SQLTransaction::cleanupAfterSuccessCallback()
435 // Transaction Step 11 - End transaction steps
436 // There is no next step
437 LOG(StorageAPI, "Transaction %p is complete\n", this);
438 ASSERT(!m_database->m_sqliteDatabase.transactionInProgress());
442 void SQLTransaction::handleTransactionError(bool inCallback)
444 if (m_errorCallback) {
446 deliverTransactionErrorCallback();
448 m_nextStep = &SQLTransaction::deliverTransactionErrorCallback;
449 LOG(StorageAPI, "Scheduling deliverTransactionErrorCallback for transaction %p\n", this);
450 m_database->scheduleTransactionCallback(this);
455 // Transaction Step 12 - If the callback couldn't be called, then rollback the transaction.
456 m_shouldCommitAfterErrorCallback = false;
458 m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback;
459 LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this);
460 m_database->scheduleTransactionStep(this);
462 cleanupAfterTransactionErrorCallback();
466 void SQLTransaction::deliverTransactionErrorCallback()
468 ASSERT(m_transactionError);
470 // Transaction Step 12 - If the callback didn't return false, then rollback the transaction.
471 // This includes the callback not existing, returning true, or throwing an exception
472 if (!m_errorCallback || m_errorCallback->handleEvent(m_transactionError.get()))
473 m_shouldCommitAfterErrorCallback = false;
475 m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback;
476 LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this);
477 m_database->scheduleTransactionStep(this);
480 void SQLTransaction::cleanupAfterTransactionErrorCallback()
482 m_database->m_databaseAuthorizer->disable();
483 if (m_sqliteTransaction) {
484 // Transaction Step 12 -If the error callback returned false, and the last error wasn't itself a
485 // failure when committing the transaction, then try to commit the transaction
486 if (m_shouldCommitAfterErrorCallback)
487 m_sqliteTransaction->commit();
489 if (m_sqliteTransaction->inProgress()) {
490 // Transaction Step 12 - If that fails, or if the callback couldn't be called
491 // or if it didn't return false, then rollback the transaction.
492 m_sqliteTransaction->rollback();
493 } else if (m_modifiedDatabase) {
494 // But if the commit was successful, notify the delegates if the transaction modified this database
495 DatabaseTracker::tracker().scheduleNotifyDatabaseChanged(m_database->m_securityOrigin.get(), m_database->m_name);
498 ASSERT(!m_database->m_sqliteDatabase.transactionInProgress());
499 m_sqliteTransaction.clear();
501 m_database->m_databaseAuthorizer->enable();
503 // Transaction Step 12 - Any still-pending statements in the transaction are discarded.
505 MutexLocker locker(m_statementMutex);
506 m_statementQueue.clear();
509 // Transaction is complete! There is no next step
510 LOG(StorageAPI, "Transaction %p is complete with an error\n", this);
511 ASSERT(!m_database->m_sqliteDatabase.transactionInProgress());
514 // Now release our callbacks, to break reference cycles.
519 } // namespace WebCore