2fe7d30288f569ba17cedc6cfcf80ce5e2862b62
[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 "Page.h"
41 #include "PlatformString.h"
42 #include "SecurityOrigin.h"
43 #include "SQLError.h"
44 #include "SQLiteTransaction.h"
45 #include "SQLResultSet.h"
46 #include "SQLStatement.h"
47 #include "SQLStatementCallback.h"
48 #include "SQLStatementErrorCallback.h"
49 #include "SQLValue.h"
50
51 // There's no way of knowing exactly how much more space will be required when a statement hits the quota limit.  
52 // For now, we'll arbitrarily choose currentQuota + 1mb.
53 // In the future we decide to track if a size increase wasn't enough, and ask for larger-and-larger increases until its enough.
54 static const int DefaultQuotaSizeIncrease = 1048576;
55
56 namespace WebCore {
57
58 SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<SQLTransactionWrapper> wrapper)
59     : m_nextStep(&SQLTransaction::openTransactionAndPreflight)
60     , m_executeSqlAllowed(false)
61     , m_database(db)
62     , m_wrapper(wrapper)
63     , m_callback(callback)
64     , m_errorCallback(errorCallback)
65     , m_shouldRetryCurrentStatement(false)
66     , m_shouldCommitAfterErrorCallback(true)
67     , m_modifiedDatabase(false)
68 {
69     ASSERT(m_database);
70 }
71
72 void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e)
73 {
74     if (!m_executeSqlAllowed) {
75         e = INVALID_STATE_ERR;
76         return;
77     }
78     
79     RefPtr<SQLStatement> statement = new SQLStatement(sqlStatement.copy(), arguments, callback, callbackError);
80     
81     if (!m_database->versionMatchesExpected())
82         statement->setVersionMismatchedError();
83         
84     enqueueStatement(statement);
85 }
86
87 void SQLTransaction::enqueueStatement(PassRefPtr<SQLStatement> statement)
88 {
89     MutexLocker locker(m_statementMutex);
90     m_statementQueue.append(statement);
91 }
92
93 bool SQLTransaction::performNextStep()
94 {
95     ASSERT(m_nextStep == &SQLTransaction::openTransactionAndPreflight ||
96            m_nextStep == &SQLTransaction::runStatements ||
97            m_nextStep == &SQLTransaction::postflightAndCommit ||
98            m_nextStep == &SQLTransaction::cleanupAfterTransactionErrorCallback);
99                
100     (this->*m_nextStep)();
101
102     // If there is no nextStep after performing the above step, the transaction is complete
103     return !m_nextStep;
104 }
105
106 void SQLTransaction::performPendingCallback()
107 {
108     ASSERT(m_nextStep == &SQLTransaction::deliverTransactionCallback ||
109            m_nextStep == &SQLTransaction::deliverTransactionErrorCallback ||
110            m_nextStep == &SQLTransaction::deliverStatementCallback ||
111            m_nextStep == &SQLTransaction::deliverQuotaIncreaseCallback);
112            
113     (this->*m_nextStep)();
114 }
115
116 void SQLTransaction::openTransactionAndPreflight()
117 {
118     LOG(StorageAPI, "Opening and preflighting transaction %p", this);
119     
120     // FIXME: This is a glaring bug that gives each database in an origin the full quota of that origin
121     // An efficient way to track the size of individual databases in an origin will need to be developed
122     // before we can know
123     // <rdar://problem/5628468> tracks this task
124     m_database->m_sqliteDatabase.setMaximumSize(DatabaseTracker::tracker().quotaForOrigin(m_database->securityOriginCopy().get()));
125     
126     ASSERT(!m_sqliteTransaction);
127     m_sqliteTransaction.set(new SQLiteTransaction(m_database->m_sqliteDatabase));
128     
129     m_database->m_databaseAuthorizer->disable();
130     m_sqliteTransaction->begin();
131     m_database->m_databaseAuthorizer->enable();    
132     
133     // Transaction Steps 1+2 - Open a transaction to the database, jumping to the error callback if that fails
134     if (!m_sqliteTransaction->inProgress()) {
135         m_sqliteTransaction.clear();
136         m_transactionError = new SQLError(0, "unable to open a transaction to the database");
137         handleTransactionError(false);
138         return;
139     }
140     
141     // Transaction Steps 3 - Peform preflight steps, jumping to the error callback if they fail
142     if (m_wrapper && !m_wrapper->performPreflight(this)) {
143         m_sqliteTransaction.clear();
144         m_transactionError = m_wrapper->sqlError();
145         if (!m_transactionError)
146             m_transactionError = new SQLError(0, "unknown error occured setting up transaction");
147
148         handleTransactionError(false);
149         return;
150     }
151     
152     // Transaction Step 4 - Invoke the transaction callback with the new SQLTransaction object
153     m_nextStep = &SQLTransaction::deliverTransactionCallback;
154     m_database->scheduleTransactionCallback(this);
155 }
156
157 void SQLTransaction::deliverTransactionCallback()
158 {
159     bool shouldDeliverErrorCallback = false;
160
161     if (m_callback) {
162         m_executeSqlAllowed = true;
163         m_callback->handleEvent(this, shouldDeliverErrorCallback);
164         m_executeSqlAllowed = false;
165     } else
166         shouldDeliverErrorCallback = true;
167
168     // Transaction Step 5 - If the transaction callback was null or raised an exception, jump to the error callback
169     if (shouldDeliverErrorCallback) {
170         m_transactionError = new SQLError(0, "the SQLTransactionCallback was null or threw an exception");
171         deliverTransactionErrorCallback();
172     } else
173         scheduleToRunStatements();
174 }
175
176 void SQLTransaction::scheduleToRunStatements()
177 {
178     m_currentStatement = 0;
179     m_nextStep = &SQLTransaction::runStatements;
180     m_database->scheduleTransactionStep();
181 }
182
183 void SQLTransaction::runStatements()
184 {
185     // If there is a series of statements queued up that are all successful and have no associated
186     // SQLStatementCallback objects, then we can burn through the queue
187     do {
188         if (m_shouldRetryCurrentStatement) {
189             m_shouldRetryCurrentStatement = false;
190             // FIXME - Another place that needs fixing up after <rdar://problem/5628468> is addressed.
191             // See ::openTransactionAndPreflight() for discussion
192             
193             // Reset the maximum size here, as it was increased to allow us to retry this statement
194             m_database->m_sqliteDatabase.setMaximumSize(DatabaseTracker::tracker().quotaForOrigin(m_database->securityOriginCopy().get()));
195         } else {
196             // If the current statement has already been run, failed due to quota constraints, and we're not retrying it,
197             // that means it ended in an error.  Handle it now
198             if (m_currentStatement && m_currentStatement->lastExecutionFailedDueToQuota()) {
199                 handleCurrentStatementError();
200                 break;
201             }
202             
203             // Otherwise, advance to the next statement
204             getNextStatement();
205         }
206     } while (runCurrentStatement());
207     
208     // If runCurrentStatement() returned false, that means either there was no current statement to run,
209     // or the current statement requires a callback to complete.  In the later case, it also scheduled 
210     // the callback or performed any other additional work so we can return
211     if (!m_currentStatement)
212         postflightAndCommit();
213 }
214
215 void SQLTransaction::getNextStatement()
216 {
217     m_currentStatement = 0;
218     
219     MutexLocker locker(m_statementMutex);
220     if (!m_statementQueue.isEmpty()) {
221         m_currentStatement = m_statementQueue.first();
222         m_statementQueue.removeFirst();
223     }
224 }
225
226 bool SQLTransaction::runCurrentStatement()
227 {
228     if (!m_currentStatement)
229         return false;
230         
231     m_database->m_databaseAuthorizer->reset();
232     
233     if (m_currentStatement->execute(m_database)) {
234         // Flag this transaction as having changed the database for later delegate notification
235         if (m_database->m_databaseAuthorizer->lastActionChangedDatabase())
236             m_modifiedDatabase = true;
237             
238         if (m_currentStatement->hasStatementCallback()) {
239             m_nextStep = &SQLTransaction::deliverStatementCallback;
240             m_database->scheduleTransactionCallback(this);
241             return false;
242         }
243         return true;
244     }
245     
246     if (m_currentStatement->lastExecutionFailedDueToQuota()) {
247         m_nextStep = &SQLTransaction::deliverQuotaIncreaseCallback;
248         m_database->scheduleTransactionCallback(this);
249         return false;
250     }
251     
252     handleCurrentStatementError();
253     
254     return false;
255 }
256
257 void SQLTransaction::handleCurrentStatementError()
258 {
259     // Transaction Steps 6.error - Call the statement's error callback, but if there was no error callback,
260     // jump to the transaction error callback
261     if (m_currentStatement->hasStatementErrorCallback()) {
262         m_nextStep = &SQLTransaction::deliverStatementCallback;
263         m_database->scheduleTransactionCallback(this);
264     } else {
265         m_transactionError = m_currentStatement->sqlError();
266         if (!m_transactionError)
267             m_transactionError = new SQLError(1, "the statement failed to execute");
268         handleTransactionError(false);
269     }
270 }
271
272 void SQLTransaction::deliverStatementCallback()
273 {
274     ASSERT(m_currentStatement);
275     
276     // Transaction Step 6.6 and 6.3(error) - If the statement callback went wrong, jump to the transaction error callback
277     // Otherwise, continue to loop through the statement queue
278     m_executeSqlAllowed = true;
279     bool result = m_currentStatement->performCallback(this);
280     m_executeSqlAllowed = false;
281
282     if (result) {
283         m_transactionError = new SQLError(0, "the statement callback raised an exception or statement error callback did not return false");
284         handleTransactionError(true);
285     } else
286         scheduleToRunStatements();
287 }
288
289 void SQLTransaction::deliverQuotaIncreaseCallback()
290 {
291     ASSERT(m_currentStatement);
292     ASSERT(!m_shouldRetryCurrentStatement);
293     
294     Page* page = m_database->document()->page();
295     ASSERT(page);
296     
297     RefPtr<SecurityOrigin> origin = m_database->securityOriginCopy();
298     
299     unsigned long long currentQuota = DatabaseTracker::tracker().quotaForOrigin(origin.get());
300     page->chrome()->client()->exceededDatabaseQuota(m_database->document()->frame(), m_database->stringIdentifier());
301     unsigned long long newQuota = DatabaseTracker::tracker().quotaForOrigin(origin.get());
302     
303     // If the new quota ended up being larger than the old quota, we will retry the statement.
304     if (newQuota > currentQuota)
305         m_shouldRetryCurrentStatement = true;
306         
307     m_nextStep = &SQLTransaction::runStatements;
308     m_database->scheduleTransactionStep();
309 }
310
311 void SQLTransaction::postflightAndCommit()
312 {    
313     // Transaction Step 7 - Peform postflight steps, jumping to the error callback if they fail
314     if (m_wrapper && !m_wrapper->performPostflight(this)) {
315         m_transactionError = m_wrapper->sqlError();
316         if (!m_transactionError)
317             m_transactionError = new SQLError(0, "unknown error occured setting up transaction");
318         handleTransactionError(false);
319         return;
320     }
321     
322     // Transacton Step 8+9 - Commit the transaction, jumping to the error callback if that fails
323     ASSERT(m_sqliteTransaction);
324     
325     m_database->m_databaseAuthorizer->disable();
326     m_sqliteTransaction->commit();
327     m_database->m_databaseAuthorizer->enable();    
328         
329     // If the commit failed, the transaction will still be marked as "in progress"
330     if (m_sqliteTransaction->inProgress()) {
331         m_shouldCommitAfterErrorCallback = false;
332         m_transactionError = new SQLError(0, "failed to commit the transaction");
333         handleTransactionError(false);
334         return;
335     }
336     
337     // The commit was successful, notify the delegates if the transaction modified this database
338     if (m_modifiedDatabase)
339         DatabaseTracker::tracker().scheduleNotifyDatabaseChanged(m_database->m_securityOrigin.get(), m_database->m_name);
340     
341     // Transaction Step 10 - End transaction steps
342     // There is no next step
343     m_nextStep = 0;
344
345     // Now release our callbacks, to break reference cycles.
346     m_callback = 0;
347     m_errorCallback = 0;
348 }
349
350 void SQLTransaction::handleTransactionError(bool inCallback)
351 {
352     if (m_errorCallback) {
353         if (inCallback)
354             deliverTransactionErrorCallback();
355         else {
356             m_nextStep = &SQLTransaction::deliverTransactionErrorCallback;
357             m_database->scheduleTransactionCallback(this);
358         }
359         return;
360     }
361     
362     // Transaction Step 11 - If the callback couldn't be called, then rollback the transaction.
363     m_shouldCommitAfterErrorCallback = false;
364     if (inCallback) {
365         m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback;
366         m_database->scheduleTransactionStep();
367     } else {
368         cleanupAfterTransactionErrorCallback();
369     }
370 }
371
372 void SQLTransaction::deliverTransactionErrorCallback()
373 {
374     ASSERT(m_transactionError);
375     
376     // Transaction Step 11 - If the callback didn't return false, then rollback the transaction.
377     // This includes the callback not existing, returning true, or throwing an exception
378     if (!m_errorCallback || m_errorCallback->handleEvent(m_transactionError.get()))
379         m_shouldCommitAfterErrorCallback = false;
380
381     m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback;
382     m_database->scheduleTransactionStep();
383 }
384
385 void SQLTransaction::cleanupAfterTransactionErrorCallback()
386 {
387     m_database->m_databaseAuthorizer->disable();
388     if (m_sqliteTransaction) {
389         // Transaction Step 11 -If the error callback returned false, and the last error wasn't itself a 
390         // failure when committing the transaction, then try to commit the transaction
391         if (m_shouldCommitAfterErrorCallback)
392             m_sqliteTransaction->commit();
393         
394         if (m_sqliteTransaction->inProgress()) {
395             // Transaction Step 11 - If that fails, or if the callback couldn't be called 
396             // or if it didn't return false, then rollback the transaction.
397             m_sqliteTransaction->rollback();
398         } else if (m_modifiedDatabase) {
399             // But if the commit was successful, notify the delegates if the transaction modified this database
400             DatabaseTracker::tracker().scheduleNotifyDatabaseChanged(m_database->m_securityOrigin.get(), m_database->m_name);
401         }
402         
403         m_sqliteTransaction.clear();
404     }
405     m_database->m_databaseAuthorizer->enable();
406     
407     // Transaction Step 11 - Any still-pending statements in the transaction are discarded.
408     {
409         MutexLocker locker(m_statementMutex);
410         m_statementQueue.clear();
411     }
412     
413     // Transaction is complete!  There is no next step
414     m_nextStep = 0;
415
416     // Now release our callbacks, to break reference cycles.
417     m_callback = 0;
418     m_errorCallback = 0;
419 }
420
421 } // namespace WebCore