blob: 7155b597a420f59e1395b9281de8c2d64daa8711 [file] [log] [blame]
/*
* Copyright (C) 2015 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. ``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
* 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 "MemoryObjectStore.h"
#if ENABLE(INDEXED_DATABASE)
#include "IDBBindingUtilities.h"
#include "IDBError.h"
#include "IDBGetAllResult.h"
#include "IDBKeyRangeData.h"
#include "IDBSerializationContext.h"
#include "IDBValue.h"
#include "IndexKey.h"
#include "Logging.h"
#include "MemoryBackingStoreTransaction.h"
#include "UniqueIDBDatabase.h"
#include <JavaScriptCore/JSCJSValue.h>
#include <JavaScriptCore/JSCJSValueInlines.h>
#include <JavaScriptCore/JSLock.h>
namespace WebCore {
using namespace JSC;
namespace IDBServer {
Ref<MemoryObjectStore> MemoryObjectStore::create(PAL::SessionID sessionID, const IDBObjectStoreInfo& info)
{
return adoptRef(*new MemoryObjectStore(sessionID, info));
}
MemoryObjectStore::MemoryObjectStore(PAL::SessionID sessionID, const IDBObjectStoreInfo& info)
: m_info(info)
, m_serializationContext(IDBSerializationContext::getOrCreateIDBSerializationContext(sessionID))
{
}
MemoryObjectStore::~MemoryObjectStore()
{
m_writeTransaction = nullptr;
}
MemoryIndex* MemoryObjectStore::indexForIdentifier(uint64_t identifier)
{
ASSERT(identifier);
return m_indexesByIdentifier.get(identifier);
}
void MemoryObjectStore::writeTransactionStarted(MemoryBackingStoreTransaction& transaction)
{
LOG(IndexedDB, "MemoryObjectStore::writeTransactionStarted");
ASSERT(!m_writeTransaction);
m_writeTransaction = &transaction;
}
void MemoryObjectStore::writeTransactionFinished(MemoryBackingStoreTransaction& transaction)
{
LOG(IndexedDB, "MemoryObjectStore::writeTransactionFinished");
ASSERT_UNUSED(transaction, m_writeTransaction == &transaction);
m_writeTransaction = nullptr;
}
IDBError MemoryObjectStore::createIndex(MemoryBackingStoreTransaction& transaction, const IDBIndexInfo& info)
{
LOG(IndexedDB, "MemoryObjectStore::createIndex");
if (!m_writeTransaction || !m_writeTransaction->isVersionChange() || m_writeTransaction != &transaction)
return IDBError(ConstraintError);
ASSERT(!m_indexesByIdentifier.contains(info.identifier()));
auto index = MemoryIndex::create(info, *this);
// If there was an error populating the new index, then the current records in the object store violate its contraints
auto error = populateIndexWithExistingRecords(index.get());
if (!error.isNull())
return error;
m_info.addExistingIndex(info);
transaction.addNewIndex(index.get());
registerIndex(WTFMove(index));
return IDBError { };
}
void MemoryObjectStore::maybeRestoreDeletedIndex(Ref<MemoryIndex>&& index)
{
LOG(IndexedDB, "MemoryObjectStore::maybeRestoreDeletedIndex");
if (m_info.hasIndex(index->info().name()))
return;
m_info.addExistingIndex(index->info());
ASSERT(!m_indexesByIdentifier.contains(index->info().identifier()));
index->clearIndexValueStore();
auto error = populateIndexWithExistingRecords(index.get());
// Since this index was installed in the object store before this transaction started,
// assuming things were in a valid state then, we should definitely be able to successfully
// repopulate the index with the object store's pre-transaction records.
ASSERT_UNUSED(error, error.isNull());
registerIndex(WTFMove(index));
}
RefPtr<MemoryIndex> MemoryObjectStore::takeIndexByIdentifier(uint64_t indexIdentifier)
{
auto indexByIdentifier = m_indexesByIdentifier.take(indexIdentifier);
if (!indexByIdentifier)
return nullptr;
auto index = m_indexesByName.take(indexByIdentifier->info().name());
ASSERT(index);
return index;
}
IDBError MemoryObjectStore::deleteIndex(MemoryBackingStoreTransaction& transaction, uint64_t indexIdentifier)
{
LOG(IndexedDB, "MemoryObjectStore::deleteIndex");
if (!m_writeTransaction || !m_writeTransaction->isVersionChange() || m_writeTransaction != &transaction)
return IDBError(ConstraintError);
auto index = takeIndexByIdentifier(indexIdentifier);
ASSERT(index);
if (!index)
return IDBError(ConstraintError);
m_info.deleteIndex(indexIdentifier);
transaction.indexDeleted(*index);
return IDBError { };
}
void MemoryObjectStore::deleteAllIndexes(MemoryBackingStoreTransaction& transaction)
{
Vector<uint64_t> indexIdentifiers;
indexIdentifiers.reserveInitialCapacity(m_indexesByName.size());
for (auto& index : m_indexesByName.values())
indexIdentifiers.uncheckedAppend(index->info().identifier());
for (auto identifier : indexIdentifiers)
deleteIndex(transaction, identifier);
}
bool MemoryObjectStore::containsRecord(const IDBKeyData& key)
{
if (!m_keyValueStore)
return false;
return m_keyValueStore->contains(key);
}
void MemoryObjectStore::clear()
{
LOG(IndexedDB, "MemoryObjectStore::clear");
ASSERT(m_writeTransaction);
m_writeTransaction->objectStoreCleared(*this, WTFMove(m_keyValueStore), WTFMove(m_orderedKeys));
for (auto& index : m_indexesByIdentifier.values())
index->objectStoreCleared();
for (auto& cursor : m_cursors.values())
cursor->objectStoreCleared();
}
void MemoryObjectStore::replaceKeyValueStore(std::unique_ptr<KeyValueMap>&& store, std::unique_ptr<IDBKeyDataSet>&& orderedKeys)
{
ASSERT(m_writeTransaction);
ASSERT(m_writeTransaction->isAborting());
m_keyValueStore = WTFMove(store);
m_orderedKeys = WTFMove(orderedKeys);
}
void MemoryObjectStore::deleteRecord(const IDBKeyData& key)
{
LOG(IndexedDB, "MemoryObjectStore::deleteRecord");
ASSERT(m_writeTransaction);
if (!m_keyValueStore) {
m_writeTransaction->recordValueChanged(*this, key, nullptr);
return;
}
ASSERT(m_orderedKeys);
auto iterator = m_keyValueStore->find(key);
if (iterator == m_keyValueStore->end()) {
m_writeTransaction->recordValueChanged(*this, key, nullptr);
return;
}
m_writeTransaction->recordValueChanged(*this, key, &iterator->value);
m_keyValueStore->remove(iterator);
m_orderedKeys->erase(key);
updateIndexesForDeleteRecord(key);
updateCursorsForDeleteRecord(key);
}
void MemoryObjectStore::deleteRange(const IDBKeyRangeData& inputRange)
{
LOG(IndexedDB, "MemoryObjectStore::deleteRange");
ASSERT(m_writeTransaction);
if (inputRange.isExactlyOneKey()) {
deleteRecord(inputRange.lowerKey);
return;
}
IDBKeyRangeData range = inputRange;
while (true) {
auto key = lowestKeyWithRecordInRange(range);
if (key.isNull())
break;
deleteRecord(key);
range.lowerKey = key;
range.lowerOpen = true;
}
}
IDBError MemoryObjectStore::addRecord(MemoryBackingStoreTransaction& transaction, const IDBKeyData& keyData, const IDBValue& value)
{
LOG(IndexedDB, "MemoryObjectStore::addRecord");
ASSERT(m_writeTransaction);
ASSERT_UNUSED(transaction, m_writeTransaction == &transaction);
ASSERT(!m_keyValueStore || !m_keyValueStore->contains(keyData));
ASSERT(!m_orderedKeys || m_orderedKeys->find(keyData) == m_orderedKeys->end());
if (!m_keyValueStore) {
ASSERT(!m_orderedKeys);
m_keyValueStore = makeUnique<KeyValueMap>();
m_orderedKeys = makeUniqueWithoutFastMallocCheck<IDBKeyDataSet>();
}
auto mapResult = m_keyValueStore->set(keyData, value.data());
ASSERT(mapResult.isNewEntry);
auto listResult = m_orderedKeys->insert(keyData);
ASSERT(listResult.second);
// If there was an error indexing this addition, then revert it.
auto error = updateIndexesForPutRecord(keyData, value.data());
if (!error.isNull()) {
m_keyValueStore->remove(mapResult.iterator);
m_orderedKeys->erase(listResult.first);
} else
updateCursorsForPutRecord(listResult.first);
return error;
}
void MemoryObjectStore::updateCursorsForPutRecord(IDBKeyDataSet::iterator iterator)
{
for (auto& cursor : m_cursors.values())
cursor->keyAdded(iterator);
}
void MemoryObjectStore::updateCursorsForDeleteRecord(const IDBKeyData& key)
{
for (auto& cursor : m_cursors.values())
cursor->keyDeleted(key);
}
void MemoryObjectStore::updateIndexesForDeleteRecord(const IDBKeyData& value)
{
for (auto& index : m_indexesByName.values())
index->removeEntriesWithValueKey(value);
}
IDBError MemoryObjectStore::updateIndexesForPutRecord(const IDBKeyData& key, const ThreadSafeDataBuffer& value)
{
JSLockHolder locker(m_serializationContext->vm());
auto jsValue = deserializeIDBValueToJSValue(m_serializationContext->execState(), value);
if (jsValue.isUndefinedOrNull())
return IDBError { };
IDBError error;
Vector<std::pair<MemoryIndex*, IndexKey>> changedIndexRecords;
for (auto& index : m_indexesByName.values()) {
IndexKey indexKey;
generateIndexKeyForValue(m_serializationContext->execState(), index->info(), jsValue, indexKey, m_info.keyPath(), key);
if (indexKey.isNull())
continue;
error = index->putIndexKey(key, indexKey);
if (!error.isNull())
break;
changedIndexRecords.append(std::make_pair(index.get(), indexKey));
}
// If any of the index puts failed, revert all of the ones that went through.
if (!error.isNull()) {
for (auto& record : changedIndexRecords)
record.first->removeRecord(key, record.second);
}
return error;
}
IDBError MemoryObjectStore::populateIndexWithExistingRecords(MemoryIndex& index)
{
if (!m_keyValueStore)
return IDBError { };
JSLockHolder locker(m_serializationContext->vm());
for (const auto& iterator : *m_keyValueStore) {
auto jsValue = deserializeIDBValueToJSValue(m_serializationContext->execState(), iterator.value);
if (jsValue.isUndefinedOrNull())
return IDBError { };
IndexKey indexKey;
generateIndexKeyForValue(m_serializationContext->execState(), index.info(), jsValue, indexKey, m_info.keyPath(), iterator.key);
if (indexKey.isNull())
continue;
IDBError error = index.putIndexKey(iterator.key, indexKey);
if (!error.isNull())
return error;
}
return IDBError { };
}
uint64_t MemoryObjectStore::countForKeyRange(uint64_t indexIdentifier, const IDBKeyRangeData& inRange) const
{
LOG(IndexedDB, "MemoryObjectStore::countForKeyRange");
if (indexIdentifier) {
auto* index = m_indexesByIdentifier.get(indexIdentifier);
ASSERT(index);
return index->countForKeyRange(inRange);
}
if (!m_keyValueStore)
return 0;
uint64_t count = 0;
IDBKeyRangeData range = inRange;
while (true) {
auto key = lowestKeyWithRecordInRange(range);
if (key.isNull())
break;
++count;
range.lowerKey = key;
range.lowerOpen = true;
}
return count;
}
ThreadSafeDataBuffer MemoryObjectStore::valueForKey(const IDBKeyData& key) const
{
if (!m_keyValueStore)
return { };
return m_keyValueStore->get(key);
}
ThreadSafeDataBuffer MemoryObjectStore::valueForKeyRange(const IDBKeyRangeData& keyRangeData) const
{
LOG(IndexedDB, "MemoryObjectStore::valueForKey");
IDBKeyData key = lowestKeyWithRecordInRange(keyRangeData);
if (key.isNull())
return ThreadSafeDataBuffer();
ASSERT(m_keyValueStore);
return m_keyValueStore->get(key);
}
void MemoryObjectStore::getAllRecords(const IDBKeyRangeData& keyRangeData, Optional<uint32_t> count, IndexedDB::GetAllType type, IDBGetAllResult& result) const
{
result = { type, m_info.keyPath() };
uint32_t targetCount;
if (count && count.value())
targetCount = count.value();
else
targetCount = std::numeric_limits<uint32_t>::max();
IDBKeyRangeData range = keyRangeData;
uint32_t currentCount = 0;
while (currentCount < targetCount) {
IDBKeyData key = lowestKeyWithRecordInRange(range);
if (key.isNull())
return;
range.lowerKey = key;
range.lowerOpen = true;
if (type == IndexedDB::GetAllType::Values)
result.addValue(valueForKey(key));
result.addKey(WTFMove(key));
++currentCount;
}
}
IDBGetResult MemoryObjectStore::indexValueForKeyRange(uint64_t indexIdentifier, IndexedDB::IndexRecordType recordType, const IDBKeyRangeData& range) const
{
LOG(IndexedDB, "MemoryObjectStore::indexValueForKeyRange");
auto* index = m_indexesByIdentifier.get(indexIdentifier);
ASSERT(index);
return index->getResultForKeyRange(recordType, range);
}
IDBKeyData MemoryObjectStore::lowestKeyWithRecordInRange(const IDBKeyRangeData& keyRangeData) const
{
if (!m_keyValueStore)
return { };
if (keyRangeData.isExactlyOneKey() && m_keyValueStore->contains(keyRangeData.lowerKey))
return keyRangeData.lowerKey;
ASSERT(m_orderedKeys);
auto lowestInRange = m_orderedKeys->lower_bound(keyRangeData.lowerKey);
if (lowestInRange == m_orderedKeys->end())
return { };
if (keyRangeData.lowerOpen && *lowestInRange == keyRangeData.lowerKey)
++lowestInRange;
if (lowestInRange == m_orderedKeys->end())
return { };
if (!keyRangeData.upperKey.isNull()) {
if (lowestInRange->compare(keyRangeData.upperKey) > 0)
return { };
if (keyRangeData.upperOpen && *lowestInRange == keyRangeData.upperKey)
return { };
}
return *lowestInRange;
}
void MemoryObjectStore::registerIndex(Ref<MemoryIndex>&& index)
{
ASSERT(!m_indexesByIdentifier.contains(index->info().identifier()));
ASSERT(!m_indexesByName.contains(index->info().name()));
auto identifier = index->info().identifier();
m_indexesByName.set(index->info().name(), &index.get());
m_indexesByIdentifier.set(identifier, WTFMove(index));
}
void MemoryObjectStore::unregisterIndex(MemoryIndex& index)
{
ASSERT(m_indexesByIdentifier.contains(index.info().identifier()));
ASSERT(m_indexesByName.contains(index.info().name()));
m_indexesByName.remove(index.info().name());
m_indexesByIdentifier.remove(index.info().identifier());
}
MemoryObjectStoreCursor* MemoryObjectStore::maybeOpenCursor(const IDBCursorInfo& info)
{
auto result = m_cursors.add(info.identifier(), nullptr);
if (!result.isNewEntry)
return nullptr;
result.iterator->value = makeUnique<MemoryObjectStoreCursor>(*this, info);
return result.iterator->value.get();
}
void MemoryObjectStore::renameIndex(MemoryIndex& index, const String& newName)
{
ASSERT(m_indexesByName.get(index.info().name()) == &index);
ASSERT(!m_indexesByName.contains(newName));
ASSERT(m_info.infoForExistingIndex(index.info().name()));
m_info.infoForExistingIndex(index.info().name())->rename(newName);
m_indexesByName.set(newName, m_indexesByName.take(index.info().name()));
index.rename(newName);
}
} // namespace IDBServer
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)