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 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 65 void SQLTransactionBackendSync::rollbackIfInProgress() 66 { 67 ASSERT(!m_database->executionContext() || m_database->executionContext()->isContextThread()); 68 if (m_sqliteTransaction && m_sqliteTransaction->inProgress()) 69 rollback(); 70 } 71 72 SQLTransactionBackendSync::~SQLTransactionBackendSync() 73 { 74 #if ENABLE(OILPAN) 75 ASSERT(!m_sqliteTransaction || !m_sqliteTransaction->inProgress()); 76 #else 77 rollbackIfInProgress(); 78 #endif 79 } 80 81 void SQLTransactionBackendSync::trace(Visitor* visitor) 82 { 83 visitor->trace(m_database); 84 } 85 86 PassRefPtrWillBeRawPtr<SQLResultSet> SQLTransactionBackendSync::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, ExceptionState& exceptionState) 87 { 88 ASSERT(m_database->executionContext()->isContextThread()); 89 90 m_database->setLastErrorMessage(""); 91 92 if (!m_database->opened()) { 93 m_database->setLastErrorMessage("cannot executeSQL because the database is not open"); 94 exceptionState.throwDOMException(UnknownError, SQLError::unknownErrorMessage); 95 return nullptr; 96 } 97 98 if (m_hasVersionMismatch) { 99 m_database->setLastErrorMessage("cannot executeSQL because there is a version mismatch"); 100 exceptionState.throwDOMException(VersionError, SQLError::versionErrorMessage); 101 return nullptr; 102 } 103 104 if (sqlStatement.isEmpty()) 105 return nullptr; 106 107 int permissions = DatabaseAuthorizer::ReadWriteMask; 108 if (!m_database->databaseContext()->allowDatabaseAccess()) 109 permissions |= DatabaseAuthorizer::NoAccessMask; 110 else if (m_readOnly) 111 permissions |= DatabaseAuthorizer::ReadOnlyMask; 112 113 SQLStatementSync statement(sqlStatement, arguments, permissions); 114 115 m_database->resetAuthorizer(); 116 bool retryStatement = true; 117 RefPtrWillBeRawPtr<SQLResultSet> resultSet; 118 while (retryStatement) { 119 retryStatement = false; 120 resultSet = statement.execute(m_database.get(), exceptionState); 121 if (!resultSet) { 122 if (m_sqliteTransaction->wasRolledBackBySqlite()) 123 return nullptr; 124 125 if (exceptionState.code() == QuotaExceededError) { 126 if (m_transactionClient->didExceedQuota(database())) { 127 exceptionState.clearException(); 128 retryStatement = true; 129 } else { 130 m_database->setLastErrorMessage("there was not enough remaining storage space"); 131 return nullptr; 132 } 133 } 134 } 135 } 136 137 if (m_database->lastActionChangedDatabase()) 138 m_modifiedDatabase = true; 139 140 return resultSet.release(); 141 } 142 143 void SQLTransactionBackendSync::begin(ExceptionState& exceptionState) 144 { 145 ASSERT(m_database->executionContext()->isContextThread()); 146 if (!m_database->opened()) { 147 m_database->reportStartTransactionResult(1, SQLError::UNKNOWN_ERR, 0); 148 m_database->setLastErrorMessage("cannot begin transaction because the database is not open"); 149 exceptionState.throwDOMException(UnknownError, SQLError::unknownErrorMessage); 150 return; 151 } 152 153 ASSERT(!m_database->sqliteDatabase().transactionInProgress()); 154 155 // Set the maximum usage for this transaction if this transactions is not read-only. 156 if (!m_readOnly) 157 m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); 158 159 ASSERT(!m_sqliteTransaction); 160 m_sqliteTransaction = adoptPtr(new SQLiteTransaction(m_database->sqliteDatabase(), m_readOnly)); 161 162 m_database->resetDeletes(); 163 m_database->disableAuthorizer(); 164 m_sqliteTransaction->begin(); 165 m_database->enableAuthorizer(); 166 167 // Check if begin() succeeded. 168 if (!m_sqliteTransaction->inProgress()) { 169 ASSERT(!m_database->sqliteDatabase().transactionInProgress()); 170 m_database->reportStartTransactionResult(2, SQLError::DATABASE_ERR, m_database->sqliteDatabase().lastError()); 171 m_database->setLastErrorMessage("unable to begin transaction", 172 m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); 173 m_sqliteTransaction.clear(); 174 exceptionState.throwDOMException(SQLDatabaseError, "Unable to begin transaction."); 175 return; 176 } 177 178 // Note: We intentionally retrieve the actual version even with an empty expected version. 179 // In multi-process browsers, we take this opportinutiy to update the cached value for 180 // the actual version. In single-process browsers, this is just a map lookup. 181 String actualVersion; 182 if (!m_database->getActualVersionForTransaction(actualVersion)) { 183 m_database->reportStartTransactionResult(3, SQLError::DATABASE_ERR, m_database->sqliteDatabase().lastError()); 184 m_database->setLastErrorMessage("unable to read version", 185 m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); 186 rollback(); 187 exceptionState.throwDOMException(SQLDatabaseError, "Unable to read version."); 188 return; 189 } 190 m_hasVersionMismatch = !m_database->expectedVersion().isEmpty() && (m_database->expectedVersion() != actualVersion); 191 m_database->reportStartTransactionResult(0, -1, 0); // OK 192 } 193 194 void SQLTransactionBackendSync::execute(ExceptionState& exceptionState) 195 { 196 ASSERT(m_database->executionContext()->isContextThread()); 197 if (!m_database->opened() || (m_callback && !m_callback->handleEvent(SQLTransactionSync::from(this)))) { 198 if (m_database->lastErrorMessage().isEmpty()) 199 m_database->setLastErrorMessage("failed to execute transaction callback"); 200 m_callback.clear(); 201 exceptionState.throwDOMException(UnknownError, SQLError::unknownErrorMessage); 202 return; 203 } 204 205 m_callback.clear(); 206 } 207 208 void SQLTransactionBackendSync::commit(ExceptionState& exceptionState) 209 { 210 ASSERT(m_database->executionContext()->isContextThread()); 211 if (!m_database->opened()) { 212 m_database->reportCommitTransactionResult(1, SQLError::UNKNOWN_ERR, 0); 213 m_database->setLastErrorMessage("unable to commit transaction because the database is not open."); 214 exceptionState.throwDOMException(UnknownError, SQLError::unknownErrorMessage); 215 return; 216 } 217 218 ASSERT(m_sqliteTransaction); 219 220 m_database->disableAuthorizer(); 221 m_sqliteTransaction->commit(); 222 m_database->enableAuthorizer(); 223 224 // If the commit failed, the transaction will still be marked as "in progress" 225 if (m_sqliteTransaction->inProgress()) { 226 m_database->reportCommitTransactionResult(2, SQLError::DATABASE_ERR, m_database->sqliteDatabase().lastError()); 227 m_database->setLastErrorMessage("unable to commit transaction", 228 m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); 229 exceptionState.throwDOMException(SQLDatabaseError, "Unable to commit transaction."); 230 return; 231 } 232 233 m_sqliteTransaction.clear(); 234 235 // Vacuum the database if anything was deleted. 236 if (m_database->hadDeletes()) 237 m_database->incrementalVacuumIfNeeded(); 238 239 // The commit was successful. If the transaction modified this database, notify the delegates. 240 if (m_modifiedDatabase) 241 m_transactionClient->didCommitWriteTransaction(database()); 242 243 m_database->reportCommitTransactionResult(0, -1, 0); // OK 244 } 245 246 // This can be called during GC. Do not allocate new on-heap objects. 247 void SQLTransactionBackendSync::rollback() 248 { 249 ASSERT(!m_database->executionContext() || m_database->executionContext()->isContextThread()); 250 m_database->disableAuthorizer(); 251 if (m_sqliteTransaction) { 252 m_sqliteTransaction->rollback(); 253 m_sqliteTransaction.clear(); 254 } 255 m_database->enableAuthorizer(); 256 257 ASSERT(!m_database->sqliteDatabase().transactionInProgress()); 258 } 259 260 } // namespace WebCore 261