Reviewed by Darin.
[WebKit-https.git] / WebCore / storage / SQLTransaction.cpp
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 #include "ChromeClient.h"
33 #include "Database.h"
34 #include "DatabaseAuthorizer.h"
35 #include "DatabaseDetails.h"
36 #include "DatabaseTracker.h"
37 #include "Document.h"
38 #include "ExceptionCode.h"
39 #include "Logging.h"
40 #include "OriginQuotaManager.h"
41 #include "Page.h"
42 #include "PlatformString.h"
43 #include "SecurityOrigin.h"
44 #include "SQLError.h"
45 #include "SQLiteTransaction.h"
46 #include "SQLResultSet.h"
47 #include "SQLStatement.h"
48 #include "SQLStatementCallback.h"
49 #include "SQLStatementErrorCallback.h"
50 #include "SQLValue.h"
51
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;
56
57 namespace WebCore {
58
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)
63     , m_database(db)
64     , m_wrapper(wrapper)
65     , m_callback(callback)
66     , m_successCallback(successCallback)
67     , m_errorCallback(errorCallback)
68     , m_shouldRetryCurrentStatement(false)
69     , m_shouldCommitAfterErrorCallback(true)
70     , m_modifiedDatabase(false)
71 {
72     ASSERT(m_database);
73 }
74
75 SQLTransaction::~SQLTransaction()
76 {
77 }
78
79 void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e)
80 {
81     if (!m_executeSqlAllowed) {
82         e = INVALID_STATE_ERR;
83         return;
84     }
85     
86     RefPtr<SQLStatement> statement = new SQLStatement(sqlStatement.copy(), arguments, callback, callbackError);
87
88     if (m_database->deleted())
89         statement->setDatabaseDeletedError();
90
91     if (!m_database->versionMatchesExpected())
92         statement->setVersionMismatchedError();
93         
94     enqueueStatement(statement);
95 }
96
97 void SQLTransaction::enqueueStatement(PassRefPtr<SQLStatement> statement)
98 {
99     MutexLocker locker(m_statementMutex);
100     m_statementQueue.append(statement);
101 }
102
103 #ifndef NDEBUG
104 const char* SQLTransaction::debugStepName(SQLTransaction::TransactionStepMethod step)
105 {
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";
126     else
127         return "UNKNOWN";
128 }
129 #endif
130
131 bool SQLTransaction::performNextStep()
132 {
133     LOG(StorageAPI, "Step %s\n", debugStepName(m_nextStep));
134
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);
140                
141     (this->*m_nextStep)();
142
143     // If there is no nextStep after performing the above step, the transaction is complete
144     return !m_nextStep;
145 }
146
147 void SQLTransaction::performPendingCallback()
148 {
149     LOG(StorageAPI, "Callback %s\n", debugStepName(m_nextStep));
150
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);
156            
157     (this->*m_nextStep)();
158 }
159
160 void SQLTransaction::openTransactionAndPreflight()
161 {
162     ASSERT(!m_database->m_sqliteDatabase.transactionInProgress());
163
164     LOG(StorageAPI, "Opening and preflighting transaction %p", this);
165
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);
170         return;
171     }
172
173     // Set the maximum usage for this transaction
174     m_database->m_sqliteDatabase.setMaximumSize(m_database->maximumSize());
175     
176     ASSERT(!m_sqliteTransaction);
177     m_sqliteTransaction.set(new SQLiteTransaction(m_database->m_sqliteDatabase));
178     
179     m_database->m_databaseAuthorizer->disable();
180     m_sqliteTransaction->begin();
181     m_database->m_databaseAuthorizer->enable();    
182     
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);
189         return;
190     }
191     
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");
199
200         handleTransactionError(false);
201         return;
202     }
203     
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);
208 }
209
210 void SQLTransaction::deliverTransactionCallback()
211 {
212     bool shouldDeliverErrorCallback = false;
213
214     if (m_callback) {
215         m_executeSqlAllowed = true;
216         m_callback->handleEvent(this, shouldDeliverErrorCallback);
217         m_executeSqlAllowed = false;
218     } else
219         shouldDeliverErrorCallback = true;
220
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();
225     } else
226         scheduleToRunStatements();
227 }
228
229 void SQLTransaction::scheduleToRunStatements()
230 {
231     m_nextStep = &SQLTransaction::runStatements;
232     LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this);
233     m_database->scheduleTransactionStep(this);
234 }
235
236 void SQLTransaction::runStatements()
237 {
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
240     do {
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
245             
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());
248         } else {
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();
253                 break;
254             }
255             
256             // Otherwise, advance to the next statement
257             getNextStatement();
258         }
259     } while (runCurrentStatement());
260     
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();
266 }
267
268 void SQLTransaction::getNextStatement()
269 {
270     m_currentStatement = 0;
271     
272     MutexLocker locker(m_statementMutex);
273     if (!m_statementQueue.isEmpty()) {
274         m_currentStatement = m_statementQueue.first();
275         m_statementQueue.removeFirst();
276     }
277 }
278
279 bool SQLTransaction::runCurrentStatement()
280 {
281     if (!m_currentStatement)
282         return false;
283         
284     m_database->m_databaseAuthorizer->reset();
285     
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);
293             
294             manager.markDatabase(m_database);
295         }
296             
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);
301             return false;
302         }
303         return true;
304     }
305     
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);
310         return false;
311     }
312     
313     handleCurrentStatementError();
314     
315     return false;
316 }
317
318 void SQLTransaction::handleCurrentStatementError()
319 {
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);
326     } else {
327         m_transactionError = m_currentStatement->sqlError();
328         if (!m_transactionError)
329             m_transactionError = new SQLError(1, "the statement failed to execute");
330         handleTransactionError(false);
331     }
332 }
333
334 void SQLTransaction::deliverStatementCallback()
335 {
336     ASSERT(m_currentStatement);
337     
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;
343
344     if (result) {
345         m_transactionError = new SQLError(0, "the statement callback raised an exception or statement error callback did not return false");
346         handleTransactionError(true);
347     } else
348         scheduleToRunStatements();
349 }
350
351 void SQLTransaction::deliverQuotaIncreaseCallback()
352 {
353     ASSERT(m_currentStatement);
354     ASSERT(!m_shouldRetryCurrentStatement);
355     
356     Page* page = m_database->document()->page();
357     ASSERT(page);
358     
359     RefPtr<SecurityOrigin> origin = m_database->securityOriginCopy();
360     
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());
364     
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;
368         
369     m_nextStep = &SQLTransaction::runStatements;
370     LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this);
371     m_database->scheduleTransactionStep(this);
372 }
373
374 void SQLTransaction::postflightAndCommit()
375 {    
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);
382         return;
383     }
384     
385     // Transacton Step 8+9 - Commit the transaction, jumping to the error callback if that fails
386     ASSERT(m_sqliteTransaction);
387     
388     m_database->m_databaseAuthorizer->disable();
389     m_sqliteTransaction->commit();
390     m_database->m_databaseAuthorizer->enable();
391
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);
397         return;
398     }
399     
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);
403     
404     // Now release our unneeded callbacks, to break reference cycles.
405     m_callback = 0;
406     m_errorCallback = 0;
407     
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);
413     } else 
414         cleanupAfterSuccessCallback();
415 }
416
417 void SQLTransaction::deliverSuccessCallback()
418 {
419     // Transaction Step 10 - Deliver success callback
420     ASSERT(m_successCallback);
421     m_successCallback->handleEvent();
422     
423     // Release the last callback to break reference cycle
424     m_successCallback = 0;
425
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);
431 }
432
433 void SQLTransaction::cleanupAfterSuccessCallback()
434 {
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());
439     m_nextStep = 0;
440 }
441
442 void SQLTransaction::handleTransactionError(bool inCallback)
443 {
444     if (m_errorCallback) {
445         if (inCallback)
446             deliverTransactionErrorCallback();
447         else {
448             m_nextStep = &SQLTransaction::deliverTransactionErrorCallback;
449             LOG(StorageAPI, "Scheduling deliverTransactionErrorCallback for transaction %p\n", this);
450             m_database->scheduleTransactionCallback(this);
451         }
452         return;
453     }
454     
455     // Transaction Step 12 - If the callback couldn't be called, then rollback the transaction.
456     m_shouldCommitAfterErrorCallback = false;
457     if (inCallback) {
458         m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback;
459         LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this);
460         m_database->scheduleTransactionStep(this);
461     } else {
462         cleanupAfterTransactionErrorCallback();
463     }
464 }
465
466 void SQLTransaction::deliverTransactionErrorCallback()
467 {
468     ASSERT(m_transactionError);
469     
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;
474
475     m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback;
476     LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this);
477     m_database->scheduleTransactionStep(this);
478 }
479
480 void SQLTransaction::cleanupAfterTransactionErrorCallback()
481 {
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();
488         
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);
496         }
497         
498         ASSERT(!m_database->m_sqliteDatabase.transactionInProgress());
499         m_sqliteTransaction.clear();
500     }
501     m_database->m_databaseAuthorizer->enable();
502     
503     // Transaction Step 12 - Any still-pending statements in the transaction are discarded.
504     {
505         MutexLocker locker(m_statementMutex);
506         m_statementQueue.clear();
507     }
508     
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());
512     m_nextStep = 0;
513
514     // Now release our callbacks, to break reference cycles.
515     m_callback = 0;
516     m_errorCallback = 0;
517 }
518
519 } // namespace WebCore