SQLiteIDBTransaction.cpp [plain text]
#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();
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 IDBError { };
return IDBError { UnknownError, ASCIILiteral("Could not start SQLite transaction in database backend") };
}
IDBError SQLiteIDBTransaction::commit()
{
LOG(IndexedDB, "SQLiteIDBTransaction::commit");
if (!m_sqliteTransaction || !m_sqliteTransaction->inProgress())
return IDBError { UnknownError, ASCIILiteral("No SQLite transaction in progress to commit") };
m_sqliteTransaction->commit();
if (m_sqliteTransaction->inProgress())
return IDBError { UnknownError, ASCIILiteral("Unable to commit SQLite transaction in database backend") };
deleteBlobFilesIfNecessary();
moveBlobFilesIfNecessary();
reset();
return IDBError { };
}
void SQLiteIDBTransaction::moveBlobFilesIfNecessary()
{
String databaseDirectory = m_backingStore.fullDatabaseDirectory();
for (auto& entry : m_blobTemporaryAndStoredFilenames) {
m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(entry.first);
if (!FileSystem::hardLinkOrCopyFile(entry.first, FileSystem::pathByAppendingComponent(databaseDirectory, entry.second)))
LOG_ERROR("Failed to link/copy temporary blob file '%s' to location '%s'", entry.first.utf8().data(), FileSystem::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 = FileSystem::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 IDBError { UnknownError, ASCIILiteral("No SQLite transaction in progress to abort") };
m_sqliteTransaction->rollback();
if (m_sqliteTransaction->inProgress())
return IDBError { UnknownError, ASCIILiteral("Unable to abort SQLite transaction in database backend") };
reset();
return IDBError { };
}
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);
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);
}
} }
#endif // ENABLE(INDEXED_DATABASE)