• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  * Copyright (C) 2013 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "config.h"
33 #include "modules/webdatabase/SQLTransactionBackendSync.h"
34 
35 #include "bindings/v8/ExceptionState.h"
36 #include "core/dom/ExceptionCode.h"
37 #include "core/dom/ExecutionContext.h"
38 #include "modules/webdatabase/sqlite/SQLValue.h"
39 #include "modules/webdatabase/sqlite/SQLiteTransaction.h"
40 #include "modules/webdatabase/DatabaseAuthorizer.h"
41 #include "modules/webdatabase/DatabaseContext.h"
42 #include "modules/webdatabase/DatabaseSync.h"
43 #include "modules/webdatabase/SQLError.h"
44 #include "modules/webdatabase/SQLResultSet.h"
45 #include "modules/webdatabase/SQLStatementSync.h"
46 #include "modules/webdatabase/SQLTransactionClient.h"
47 #include "modules/webdatabase/SQLTransactionSync.h"
48 #include "modules/webdatabase/SQLTransactionSyncCallback.h"
49 #include "wtf/PassRefPtr.h"
50 #include "wtf/RefPtr.h"
51 
52 namespace WebCore {
53 
SQLTransactionBackendSync(DatabaseSync * db,PassOwnPtr<SQLTransactionSyncCallback> callback,bool readOnly)54 SQLTransactionBackendSync::SQLTransactionBackendSync(DatabaseSync* db, PassOwnPtr<SQLTransactionSyncCallback> callback, bool readOnly)
55     : m_database(db)
56     , m_callback(callback)
57     , m_readOnly(readOnly)
58     , m_hasVersionMismatch(false)
59     , m_modifiedDatabase(false)
60     , m_transactionClient(adoptPtr(new SQLTransactionClient()))
61 {
62     ASSERT(m_database->executionContext()->isContextThread());
63 }
64 
~SQLTransactionBackendSync()65 SQLTransactionBackendSync::~SQLTransactionBackendSync()
66 {
67     ASSERT(m_database->executionContext()->isContextThread());
68     if (m_sqliteTransaction && m_sqliteTransaction->inProgress())
69         rollback();
70 }
71 
executeSQL(const String & sqlStatement,const Vector<SQLValue> & arguments,ExceptionState & exceptionState)72 PassRefPtr<SQLResultSet> SQLTransactionBackendSync::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, ExceptionState& exceptionState)
73 {
74     ASSERT(m_database->executionContext()->isContextThread());
75 
76     m_database->setLastErrorMessage("");
77 
78     if (!m_database->opened()) {
79         m_database->setLastErrorMessage("cannot executeSQL because the database is not open");
80         exceptionState.throwDOMException(UnknownError, SQLError::unknownErrorMessage);
81         return 0;
82     }
83 
84     if (m_hasVersionMismatch) {
85         m_database->setLastErrorMessage("cannot executeSQL because there is a version mismatch");
86         exceptionState.throwDOMException(VersionError, SQLError::versionErrorMessage);
87         return 0;
88     }
89 
90     if (sqlStatement.isEmpty())
91         return 0;
92 
93     int permissions = DatabaseAuthorizer::ReadWriteMask;
94     if (!m_database->databaseContext()->allowDatabaseAccess())
95         permissions |= DatabaseAuthorizer::NoAccessMask;
96     else if (m_readOnly)
97         permissions |= DatabaseAuthorizer::ReadOnlyMask;
98 
99     SQLStatementSync statement(sqlStatement, arguments, permissions);
100 
101     m_database->resetAuthorizer();
102     bool retryStatement = true;
103     RefPtr<SQLResultSet> resultSet;
104     while (retryStatement) {
105         retryStatement = false;
106         resultSet = statement.execute(m_database.get(), exceptionState);
107         if (!resultSet) {
108             if (m_sqliteTransaction->wasRolledBackBySqlite())
109                 return 0;
110 
111             if (exceptionState.code() == QuotaExceededError) {
112                 if (m_transactionClient->didExceedQuota(database())) {
113                     exceptionState.clearException();
114                     retryStatement = true;
115                 } else {
116                     m_database->setLastErrorMessage("there was not enough remaining storage space");
117                     return 0;
118                 }
119             }
120         }
121     }
122 
123     if (m_database->lastActionChangedDatabase())
124         m_modifiedDatabase = true;
125 
126     return resultSet.release();
127 }
128 
begin(ExceptionState & exceptionState)129 void SQLTransactionBackendSync::begin(ExceptionState& exceptionState)
130 {
131     ASSERT(m_database->executionContext()->isContextThread());
132     if (!m_database->opened()) {
133         m_database->reportStartTransactionResult(1, SQLError::UNKNOWN_ERR, 0);
134         m_database->setLastErrorMessage("cannot begin transaction because the database is not open");
135         exceptionState.throwDOMException(UnknownError, SQLError::unknownErrorMessage);
136         return;
137     }
138 
139     ASSERT(!m_database->sqliteDatabase().transactionInProgress());
140 
141     // Set the maximum usage for this transaction if this transactions is not read-only.
142     if (!m_readOnly)
143         m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize());
144 
145     ASSERT(!m_sqliteTransaction);
146     m_sqliteTransaction = adoptPtr(new SQLiteTransaction(m_database->sqliteDatabase(), m_readOnly));
147 
148     m_database->resetDeletes();
149     m_database->disableAuthorizer();
150     m_sqliteTransaction->begin();
151     m_database->enableAuthorizer();
152 
153     // Check if begin() succeeded.
154     if (!m_sqliteTransaction->inProgress()) {
155         ASSERT(!m_database->sqliteDatabase().transactionInProgress());
156         m_database->reportStartTransactionResult(2, SQLError::DATABASE_ERR, m_database->sqliteDatabase().lastError());
157         m_database->setLastErrorMessage("unable to begin transaction",
158             m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
159         m_sqliteTransaction.clear();
160         exceptionState.throwUninformativeAndGenericDOMException(SQLDatabaseError);
161         return;
162     }
163 
164     // Note: We intentionally retrieve the actual version even with an empty expected version.
165     // In multi-process browsers, we take this opportinutiy to update the cached value for
166     // the actual version. In single-process browsers, this is just a map lookup.
167     String actualVersion;
168     if (!m_database->getActualVersionForTransaction(actualVersion)) {
169         m_database->reportStartTransactionResult(3, SQLError::DATABASE_ERR, m_database->sqliteDatabase().lastError());
170         m_database->setLastErrorMessage("unable to read version",
171             m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
172         rollback();
173         exceptionState.throwUninformativeAndGenericDOMException(SQLDatabaseError);
174         return;
175     }
176     m_hasVersionMismatch = !m_database->expectedVersion().isEmpty() && (m_database->expectedVersion() != actualVersion);
177     m_database->reportStartTransactionResult(0, -1, 0); // OK
178 }
179 
execute(ExceptionState & exceptionState)180 void SQLTransactionBackendSync::execute(ExceptionState& exceptionState)
181 {
182     ASSERT(m_database->executionContext()->isContextThread());
183     if (!m_database->opened() || (m_callback && !m_callback->handleEvent(SQLTransactionSync::from(this)))) {
184         if (m_database->lastErrorMessage().isEmpty())
185             m_database->setLastErrorMessage("failed to execute transaction callback");
186         m_callback.clear();
187         exceptionState.throwDOMException(UnknownError, SQLError::unknownErrorMessage);
188         return;
189     }
190 
191     m_callback.clear();
192 }
193 
commit(ExceptionState & exceptionState)194 void SQLTransactionBackendSync::commit(ExceptionState& exceptionState)
195 {
196     ASSERT(m_database->executionContext()->isContextThread());
197     if (!m_database->opened()) {
198         m_database->reportCommitTransactionResult(1, SQLError::UNKNOWN_ERR, 0);
199         m_database->setLastErrorMessage("unable to commit transaction because the database is not open.");
200         exceptionState.throwDOMException(UnknownError, SQLError::unknownErrorMessage);
201         return;
202     }
203 
204     ASSERT(m_sqliteTransaction);
205 
206     m_database->disableAuthorizer();
207     m_sqliteTransaction->commit();
208     m_database->enableAuthorizer();
209 
210     // If the commit failed, the transaction will still be marked as "in progress"
211     if (m_sqliteTransaction->inProgress()) {
212         m_database->reportCommitTransactionResult(2, SQLError::DATABASE_ERR, m_database->sqliteDatabase().lastError());
213         m_database->setLastErrorMessage("unable to commit transaction",
214             m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
215         exceptionState.throwUninformativeAndGenericDOMException(SQLDatabaseError);
216         return;
217     }
218 
219     m_sqliteTransaction.clear();
220 
221     // Vacuum the database if anything was deleted.
222     if (m_database->hadDeletes())
223         m_database->incrementalVacuumIfNeeded();
224 
225     // The commit was successful. If the transaction modified this database, notify the delegates.
226     if (m_modifiedDatabase)
227         m_transactionClient->didCommitWriteTransaction(database());
228 
229     m_database->reportCommitTransactionResult(0, -1, 0); // OK
230 }
231 
rollback()232 void SQLTransactionBackendSync::rollback()
233 {
234     ASSERT(m_database->executionContext()->isContextThread());
235     m_database->disableAuthorizer();
236     if (m_sqliteTransaction) {
237         m_sqliteTransaction->rollback();
238         m_sqliteTransaction.clear();
239     }
240     m_database->enableAuthorizer();
241 
242     ASSERT(!m_database->sqliteDatabase().transactionInProgress());
243 }
244 
245 } // namespace WebCore
246