• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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