diff options
Diffstat (limited to 'Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp')
-rw-r--r-- | Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp new file mode 100644 index 000000000..fc1dd17dd --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2013, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include "SQLiteIDBTransaction.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "FileSystem.h" +#include "IDBCursorInfo.h" +#include "IndexedDB.h" +#include "Logging.h" +#include "SQLiteIDBBackingStore.h" +#include "SQLiteIDBCursor.h" +#include "SQLiteTransaction.h" + +namespace WebCore { +namespace IDBServer { + +SQLiteIDBTransaction::SQLiteIDBTransaction(SQLiteIDBBackingStore& backingStore, const IDBTransactionInfo& info) + : m_info(info) + , m_backingStore(backingStore) +{ +} + +SQLiteIDBTransaction::~SQLiteIDBTransaction() +{ + if (inProgress()) + m_sqliteTransaction->rollback(); + + // Explicitly clear cursors, as that also unregisters them from the backing store. + clearCursors(); +} + + +IDBError SQLiteIDBTransaction::begin(SQLiteDatabase& database) +{ + ASSERT(!m_sqliteTransaction); + + m_sqliteTransaction = std::make_unique<SQLiteTransaction>(database, m_info.mode() == IDBTransactionMode::Readonly); + m_sqliteTransaction->begin(); + + if (m_sqliteTransaction->inProgress()) + return { }; + + return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not start SQLite transaction in database backend") }; +} + +IDBError SQLiteIDBTransaction::commit() +{ + LOG(IndexedDB, "SQLiteIDBTransaction::commit"); + if (!m_sqliteTransaction || !m_sqliteTransaction->inProgress()) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No SQLite transaction in progress to commit") }; + + m_sqliteTransaction->commit(); + + if (m_sqliteTransaction->inProgress()) + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to commit SQLite transaction in database backend") }; + + deleteBlobFilesIfNecessary(); + moveBlobFilesIfNecessary(); + + reset(); + return { }; +} + +void SQLiteIDBTransaction::moveBlobFilesIfNecessary() +{ + String databaseDirectory = m_backingStore.fullDatabaseDirectory(); + for (auto& entry : m_blobTemporaryAndStoredFilenames) { + m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(entry.first); + + if (!hardLinkOrCopyFile(entry.first, pathByAppendingComponent(databaseDirectory, entry.second))) + LOG_ERROR("Failed to link/copy temporary blob file '%s' to location '%s'", entry.first.utf8().data(), pathByAppendingComponent(databaseDirectory, entry.second).utf8().data()); + + m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(entry.first); + } + + m_blobTemporaryAndStoredFilenames.clear(); +} + +void SQLiteIDBTransaction::deleteBlobFilesIfNecessary() +{ + if (m_blobRemovedFilenames.isEmpty()) + return; + + String databaseDirectory = m_backingStore.fullDatabaseDirectory(); + for (auto& entry : m_blobRemovedFilenames) { + String fullPath = pathByAppendingComponent(databaseDirectory, entry); + m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(fullPath); + m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(fullPath); + } + + m_blobRemovedFilenames.clear(); +} + +IDBError SQLiteIDBTransaction::abort() +{ + for (auto& entry : m_blobTemporaryAndStoredFilenames) { + m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(entry.first); + m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(entry.first); + } + + m_blobTemporaryAndStoredFilenames.clear(); + + if (!m_sqliteTransaction || !m_sqliteTransaction->inProgress()) + return { IDBDatabaseException::UnknownError, ASCIILiteral("No SQLite transaction in progress to abort") }; + + m_sqliteTransaction->rollback(); + + if (m_sqliteTransaction->inProgress()) + return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to abort SQLite transaction in database backend") }; + + reset(); + return { }; +} + +void SQLiteIDBTransaction::reset() +{ + m_sqliteTransaction = nullptr; + clearCursors(); + ASSERT(m_blobTemporaryAndStoredFilenames.isEmpty()); +} + +std::unique_ptr<SQLiteIDBCursor> SQLiteIDBTransaction::maybeOpenBackingStoreCursor(uint64_t objectStoreID, uint64_t indexID, const IDBKeyRangeData& range) +{ + ASSERT(m_sqliteTransaction); + ASSERT(m_sqliteTransaction->inProgress()); + + auto cursor = SQLiteIDBCursor::maybeCreateBackingStoreCursor(*this, objectStoreID, indexID, range); + + if (cursor) + m_backingStoreCursors.add(cursor.get()); + + return cursor; +} + +SQLiteIDBCursor* SQLiteIDBTransaction::maybeOpenCursor(const IDBCursorInfo& info) +{ + ASSERT(m_sqliteTransaction); + if (!m_sqliteTransaction->inProgress()) + return nullptr; + + auto addResult = m_cursors.add(info.identifier(), SQLiteIDBCursor::maybeCreate(*this, info)); + + ASSERT(addResult.isNewEntry); + + // It is possible the cursor failed to create and we just stored a null value. + if (!addResult.iterator->value) { + m_cursors.remove(addResult.iterator); + return nullptr; + } + + return addResult.iterator->value.get(); +} + +void SQLiteIDBTransaction::closeCursor(SQLiteIDBCursor& cursor) +{ + auto backingStoreTake = m_backingStoreCursors.take(&cursor); + if (backingStoreTake) { + ASSERT(!m_cursors.contains(cursor.identifier())); + return; + } + + ASSERT(m_cursors.contains(cursor.identifier())); + + m_backingStore.unregisterCursor(cursor); + m_cursors.remove(cursor.identifier()); +} + +void SQLiteIDBTransaction::notifyCursorsOfChanges(int64_t objectStoreID) +{ + for (auto& i : m_cursors) { + if (i.value->objectStoreID() == objectStoreID) + i.value->objectStoreRecordsChanged(); + } + + for (auto* cursor : m_backingStoreCursors) { + if (cursor->objectStoreID() == objectStoreID) + cursor->objectStoreRecordsChanged(); + } +} + +void SQLiteIDBTransaction::clearCursors() +{ + for (auto& cursor : m_cursors.values()) + m_backingStore.unregisterCursor(*cursor); + + m_cursors.clear(); +} + +bool SQLiteIDBTransaction::inProgress() const +{ + return m_sqliteTransaction && m_sqliteTransaction->inProgress(); +} + +void SQLiteIDBTransaction::addBlobFile(const String& temporaryPath, const String& storedFilename) +{ + m_blobTemporaryAndStoredFilenames.append({ temporaryPath, storedFilename }); +} + +void SQLiteIDBTransaction::addRemovedBlobFile(const String& removedFilename) +{ + ASSERT(!m_blobRemovedFilenames.contains(removedFilename)); + m_blobRemovedFilenames.add(removedFilename); +} + + +} // namespace IDBServer +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) |