Home | History | Annotate | Download | only in webdatabase
      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