blob: 548f1a293c807d4020c22066caa2fc29fdd29e89 [file] [log] [blame]
/*
* Copyright (C) 2011 Google 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 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 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 "IDBObjectStoreBackendImpl.h"
#if ENABLE(INDEXED_DATABASE)
#include "CrossThreadTask.h"
#include "DOMStringList.h"
#include "IDBBackingStore.h"
#include "IDBBindingUtilities.h"
#include "IDBCallbacks.h"
#include "IDBCursorBackendImpl.h"
#include "IDBDatabaseBackendImpl.h"
#include "IDBDatabaseException.h"
#include "IDBIndexBackendImpl.h"
#include "IDBKey.h"
#include "IDBKeyPath.h"
#include "IDBKeyPathBackendImpl.h"
#include "IDBKeyRange.h"
#include "IDBTracing.h"
#include "IDBTransactionBackendInterface.h"
#include "ScriptExecutionContext.h"
namespace WebCore {
IDBObjectStoreBackendImpl::~IDBObjectStoreBackendImpl()
{
}
IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBBackingStore* backingStore, int64_t databaseId, int64_t id, const String& name, const String& keyPath, bool autoIncrement)
: m_backingStore(backingStore)
, m_databaseId(databaseId)
, m_id(id)
, m_name(name)
, m_keyPath(keyPath)
, m_autoIncrement(autoIncrement)
, m_autoIncrementNumber(-1)
{
loadIndexes();
}
IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBBackingStore* backingStore, int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement)
: m_backingStore(backingStore)
, m_databaseId(databaseId)
, m_id(InvalidId)
, m_name(name)
, m_keyPath(keyPath)
, m_autoIncrement(autoIncrement)
, m_autoIncrementNumber(-1)
{
}
PassRefPtr<DOMStringList> IDBObjectStoreBackendImpl::indexNames() const
{
RefPtr<DOMStringList> indexNames = DOMStringList::create();
for (IndexMap::const_iterator it = m_indexes.begin(); it != m_indexes.end(); ++it)
indexNames->append(it->first);
indexNames->sort();
return indexNames.release();
}
void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
{
IDB_TRACE("IDBObjectStoreBackendImpl::get");
RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
RefPtr<IDBKey> key = prpKey;
RefPtr<IDBCallbacks> callbacks = prpCallbacks;
if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::getInternal, objectStore, key, callbacks)))
ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
}
void IDBObjectStoreBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks)
{
IDB_TRACE("IDBObjectStoreBackendImpl::getInternal");
String wireData = objectStore->m_backingStore->getObjectStoreRecord(objectStore->m_databaseId, objectStore->id(), *key);
if (wireData.isNull()) {
callbacks->onSuccess(SerializedScriptValue::undefinedValue());
return;
}
callbacks->onSuccess(SerializedScriptValue::createFromWire(wireData));
}
static PassRefPtr<IDBKey> fetchKeyFromKeyPath(SerializedScriptValue* value, const String& keyPath)
{
IDB_TRACE("IDBObjectStoreBackendImpl::fetchKeyFromKeyPath");
Vector<RefPtr<SerializedScriptValue> > values;
values.append(value);
Vector<RefPtr<IDBKey> > keys;
IDBKeyPathBackendImpl::createIDBKeysFromSerializedValuesAndKeyPath(values, keyPath, keys);
if (keys.isEmpty())
return 0;
ASSERT(keys.size() == 1);
return keys[0].release();
}
static PassRefPtr<SerializedScriptValue> injectKeyIntoKeyPath(PassRefPtr<IDBKey> key, PassRefPtr<SerializedScriptValue> value, const String& keyPath)
{
IDB_TRACE("IDBObjectStoreBackendImpl::injectKeyIntoKeyPath");
return IDBKeyPathBackendImpl::injectIDBKeyIntoSerializedValue(key, value, keyPath);
}
void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, PutMode putMode, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
{
IDB_TRACE("IDBObjectStoreBackendImpl::put");
if (transactionPtr->mode() == IDBTransaction::READ_ONLY) {
ec = IDBDatabaseException::READ_ONLY_ERR;
return;
}
RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
RefPtr<SerializedScriptValue> value = prpValue;
RefPtr<IDBKey> key = prpKey;
RefPtr<IDBCallbacks> callbacks = prpCallbacks;
RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr;
if (putMode != CursorUpdate) {
const bool autoIncrement = objectStore->autoIncrement();
const bool hasKeyPath = !objectStore->m_keyPath.isNull();
if (hasKeyPath && key) {
ec = IDBDatabaseException::DATA_ERR;
return;
}
if (!hasKeyPath && !autoIncrement && !key) {
ec = IDBDatabaseException::DATA_ERR;
return;
}
if (hasKeyPath) {
RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath);
if (keyPathKey && !keyPathKey->valid()) {
ec = IDBDatabaseException::DATA_ERR;
return;
}
if (!autoIncrement && !keyPathKey) {
ec = IDBDatabaseException::DATA_ERR;
return;
}
if (autoIncrement && !keyPathKey) {
RefPtr<IDBKey> dummyKey = IDBKey::createNumber(-1);
RefPtr<SerializedScriptValue> valueAfterInjection = injectKeyIntoKeyPath(dummyKey, value, objectStore->m_keyPath);
if (!valueAfterInjection) {
ec = IDBDatabaseException::DATA_ERR;
return;
}
}
}
if (key && !key->valid()) {
ec = IDBDatabaseException::DATA_ERR;
return;
}
for (IndexMap::iterator it = m_indexes.begin(); it != m_indexes.end(); ++it) {
const RefPtr<IDBIndexBackendImpl>& index = it->second;
RefPtr<IDBKey> indexKey = fetchKeyFromKeyPath(value.get(), index->keyPath());
if (indexKey && !indexKey->valid()) {
ec = IDBDatabaseException::DATA_ERR;
return;
}
}
} else {
ASSERT(key);
const bool hasKeyPath = !objectStore->m_keyPath.isNull();
if (hasKeyPath) {
RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath);
if (!keyPathKey || !keyPathKey->isEqual(key.get())) {
ec = IDBDatabaseException::DATA_ERR;
return;
}
}
}
if (!transaction->scheduleTask(
createCallbackTask(&IDBObjectStoreBackendImpl::putInternal, objectStore, value, key, putMode, callbacks, transaction),
// FIXME: One of these per put() is overkill, since it's simply a cache invalidation.
createCallbackTask(&IDBObjectStoreBackendImpl::revertAutoIncrementKeyCache, objectStore)))
ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
}
void IDBObjectStoreBackendImpl::revertAutoIncrementKeyCache(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore)
{
objectStore->resetAutoIncrementKeyCache();
}
void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
{
IDB_TRACE("IDBObjectStoreBackendImpl::putInternal");
RefPtr<SerializedScriptValue> value = prpValue;
RefPtr<IDBKey> key = prpKey;
if (putMode != CursorUpdate) {
const bool autoIncrement = objectStore->autoIncrement();
const bool hasKeyPath = !objectStore->m_keyPath.isNull();
if (hasKeyPath) {
ASSERT(!key);
RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath);
if (keyPathKey)
key = keyPathKey;
}
if (autoIncrement) {
if (!key) {
RefPtr<IDBKey> autoIncKey = objectStore->genAutoIncrementKey();
if (hasKeyPath) {
RefPtr<SerializedScriptValue> valueAfterInjection = injectKeyIntoKeyPath(autoIncKey, value, objectStore->m_keyPath);
ASSERT(valueAfterInjection);
if (!valueAfterInjection) {
objectStore->resetAutoIncrementKeyCache();
// Checks in put() ensure this should only happen if I/O error occurs.
// FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error inserting generated key into the object."));
return;
}
value = valueAfterInjection;
}
key = autoIncKey;
} else {
// FIXME: Logic to update generator state should go here. Currently it does a scan.
objectStore->resetAutoIncrementKeyCache();
}
}
}
ASSERT(key && key->valid());
RefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> recordIdentifier = objectStore->m_backingStore->createInvalidRecordIdentifier();
if (putMode == AddOnly && objectStore->m_backingStore->keyExistsInObjectStore(objectStore->m_databaseId, objectStore->id(), *key, recordIdentifier.get())) {
objectStore->resetAutoIncrementKeyCache();
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "Key already exists in the object store."));
return;
}
Vector<RefPtr<IDBKey> > indexKeys;
for (IndexMap::iterator it = objectStore->m_indexes.begin(); it != objectStore->m_indexes.end(); ++it) {
const RefPtr<IDBIndexBackendImpl>& index = it->second;
RefPtr<IDBKey> indexKey = fetchKeyFromKeyPath(value.get(), index->keyPath());
if (!indexKey) {
indexKeys.append(indexKey.release());
continue;
}
ASSERT(indexKey->valid());
if ((!index->multiEntry() || indexKey->type() != IDBKey::ArrayType) && !index->addingKeyAllowed(indexKey.get(), key.get())) {
objectStore->resetAutoIncrementKeyCache();
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "One of the derived (from a keyPath) keys for an index does not satisfy its uniqueness requirements."));
return;
}
if (index->multiEntry() && indexKey->type() == IDBKey::ArrayType) {
for (size_t j = 0; j < indexKey->array().size(); ++j) {
if (!index->addingKeyAllowed(indexKey->array()[j].get(), key.get())) {
objectStore->resetAutoIncrementKeyCache();
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "One of the derived (from a keyPath) keys for an index does not satisfy its uniqueness requirements."));
return;
}
}
}
indexKeys.append(indexKey.release());
}
// Before this point, don't do any mutation. After this point, rollback the transaction in case of error.
if (!objectStore->m_backingStore->putObjectStoreRecord(objectStore->m_databaseId, objectStore->id(), *key, value->toWireString(), recordIdentifier.get())) {
// FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
transaction->abort();
return;
}
int i = 0;
for (IndexMap::iterator it = objectStore->m_indexes.begin(); it != objectStore->m_indexes.end(); ++it, ++i) {
RefPtr<IDBIndexBackendImpl>& index = it->second;
if (!index->hasValidId())
continue; // The index object has been created, but does not exist in the database yet.
if (!objectStore->m_backingStore->deleteIndexDataForRecord(objectStore->m_databaseId, objectStore->id(), index->id(), recordIdentifier.get())) {
// FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
transaction->abort();
return;
}
if (!indexKeys[i])
continue;
RefPtr<IDBKey> indexKey = indexKeys[i];
if (!index->multiEntry() || indexKey->type() != IDBKey::ArrayType) {
if (!objectStore->m_backingStore->putIndexDataForRecord(objectStore->m_databaseId, objectStore->id(), index->id(), *indexKey, recordIdentifier.get())) {
// FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
transaction->abort();
return;
}
} else {
ASSERT(index->multiEntry());
ASSERT(indexKey->type() == IDBKey::ArrayType);
for (size_t j = 0; j < indexKey->array().size(); ++j) {
if (!objectStore->m_backingStore->putIndexDataForRecord(objectStore->m_databaseId, objectStore->id(), index->id(), *indexKey->array()[j], recordIdentifier.get())) {
// FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
transaction->abort();
return;
}
}
}
}
callbacks->onSuccess(key.get());
}
void IDBObjectStoreBackendImpl::deleteFunction(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
{
IDB_TRACE("IDBObjectStoreBackendImpl::delete");
RefPtr<IDBKey> key = prpKey;
if (!key || !key->valid()) {
ec = IDBDatabaseException::DATA_ERR;
return;
}
RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
if (ec)
return;
deleteFunction(keyRange.release(), prpCallbacks, transaction, ec);
}
void IDBObjectStoreBackendImpl::deleteFunction(PassRefPtr<IDBKeyRange> prpKeyRange, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
{
IDB_TRACE("IDBObjectStoreBackendImpl::delete");
if (transaction->mode() == IDBTransaction::READ_ONLY) {
ec = IDBDatabaseException::READ_ONLY_ERR;
return;
}
RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
RefPtr<IDBKeyRange> keyRange = prpKeyRange;
RefPtr<IDBCallbacks> callbacks = prpCallbacks;
if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::deleteInternal, objectStore, keyRange, callbacks)))
ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
}
void IDBObjectStoreBackendImpl::deleteInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
{
IDB_TRACE("IDBObjectStoreBackendImpl::deleteInternal");
RefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> recordIdentifier;
RefPtr<IDBBackingStore::Cursor> backingStoreCursor = objectStore->m_backingStore->openObjectStoreCursor(objectStore->m_databaseId, objectStore->id(), keyRange.get(), IDBCursor::NEXT);
if (backingStoreCursor) {
do {
recordIdentifier = backingStoreCursor->objectStoreRecordIdentifier();
for (IndexMap::iterator it = objectStore->m_indexes.begin(); it != objectStore->m_indexes.end(); ++it) {
if (!it->second->hasValidId())
continue; // The index object has been created, but does not exist in the database yet.
bool success = objectStore->m_backingStore->deleteIndexDataForRecord(objectStore->m_databaseId, objectStore->id(), it->second->id(), recordIdentifier.get());
ASSERT_UNUSED(success, success);
}
objectStore->m_backingStore->deleteObjectStoreRecord(objectStore->m_databaseId, objectStore->id(), recordIdentifier.get());
} while (backingStoreCursor->continueFunction(0));
backingStoreCursor->close();
}
callbacks->onSuccess(SerializedScriptValue::undefinedValue());
}
void IDBObjectStoreBackendImpl::clear(PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
{
IDB_TRACE("IDBObjectStoreBackendImpl::clear");
if (transaction->mode() == IDBTransaction::READ_ONLY) {
ec = IDBDatabaseException::READ_ONLY_ERR;
return;
}
RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
RefPtr<IDBCallbacks> callbacks = prpCallbacks;
if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::clearInternal, objectStore, callbacks)))
ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
}
void IDBObjectStoreBackendImpl::clearInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBCallbacks> callbacks)
{
objectStore->m_backingStore->clearObjectStore(objectStore->m_databaseId, objectStore->id());
callbacks->onSuccess(SerializedScriptValue::undefinedValue());
}
namespace {
class PopulateIndexCallback : public IDBBackingStore::ObjectStoreRecordCallback {
public:
PopulateIndexCallback(IDBBackingStore& backingStore, int64_t databaseId, int64_t objectStoreId, PassRefPtr<IDBIndexBackendImpl> index)
: m_backingStore(backingStore)
, m_databaseId(databaseId)
, m_objectStoreId(objectStoreId)
, m_index(index)
{
}
virtual bool callback(const IDBBackingStore::ObjectStoreRecordIdentifier* recordIdentifier, const String& value)
{
RefPtr<SerializedScriptValue> objectValue = SerializedScriptValue::createFromWire(value);
RefPtr<IDBKey> indexKey = fetchKeyFromKeyPath(objectValue.get(), m_index->keyPath());
if (!indexKey)
return true;
if (!m_index->multiEntry() || indexKey->type() != IDBKey::ArrayType) {
if (!m_index->addingKeyAllowed(indexKey.get()))
return false;
if (!m_backingStore.putIndexDataForRecord(m_databaseId, m_objectStoreId, m_index->id(), *indexKey, recordIdentifier))
return false;
} else {
ASSERT(m_index->multiEntry());
ASSERT(indexKey->type() == IDBKey::ArrayType);
for (size_t i = 0; i < indexKey->array().size(); ++i) {
if (!m_index->addingKeyAllowed(indexKey.get()))
return false;
if (!m_backingStore.putIndexDataForRecord(m_databaseId, m_objectStoreId, m_index->id(), *indexKey->array()[i], recordIdentifier))
return false;
}
}
return true;
}
private:
IDBBackingStore& m_backingStore;
int64_t m_databaseId;
int64_t m_objectStoreId;
RefPtr<IDBIndexBackendImpl> m_index;
};
}
bool IDBObjectStoreBackendImpl::populateIndex(IDBBackingStore& backingStore, int64_t databaseId, int64_t objectStoreId, PassRefPtr<IDBIndexBackendImpl> index)
{
PopulateIndexCallback callback(backingStore, databaseId, objectStoreId, index);
if (!backingStore.forEachObjectStoreRecord(databaseId, objectStoreId, callback))
return false;
return true;
}
PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(const String& name, const String& keyPath, bool unique, bool multiEntry, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
{
if (name.isNull()) {
ec = IDBDatabaseException::NON_TRANSIENT_ERR;
return 0;
}
if (m_indexes.contains(name)) {
ec = IDBDatabaseException::CONSTRAINT_ERR;
return 0;
}
if (transaction->mode() != IDBTransaction::VERSION_CHANGE) {
ec = IDBDatabaseException::NOT_ALLOWED_ERR;
return 0;
}
RefPtr<IDBIndexBackendImpl> index = IDBIndexBackendImpl::create(m_backingStore.get(), m_databaseId, this, name, m_name, keyPath, unique, multiEntry);
ASSERT(index->name() == name);
RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
RefPtr<IDBTransactionBackendInterface> transactionPtr = transaction;
if (!transaction->scheduleTask(
createCallbackTask(&IDBObjectStoreBackendImpl::createIndexInternal,
objectStore, index, transactionPtr),
createCallbackTask(&IDBObjectStoreBackendImpl::removeIndexFromMap,
objectStore, index))) {
ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
return 0;
}
m_indexes.set(name, index);
return index.release();
}
void IDBObjectStoreBackendImpl::createIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBTransactionBackendInterface> transaction)
{
int64_t id;
if (!objectStore->m_backingStore->createIndex(objectStore->m_databaseId, objectStore->id(), index->name(), index->keyPath(), index->unique(), index->multiEntry(), id)) {
transaction->abort();
return;
}
index->setId(id);
if (!populateIndex(*objectStore->m_backingStore, objectStore->m_databaseId, objectStore->m_id, index)) {
transaction->abort();
return;
}
transaction->didCompleteTaskEvents();
}
PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::index(const String& name, ExceptionCode& ec)
{
RefPtr<IDBIndexBackendInterface> index = m_indexes.get(name);
if (!index) {
ec = IDBDatabaseException::NOT_FOUND_ERR;
return 0;
}
return index.release();
}
void IDBObjectStoreBackendImpl::deleteIndex(const String& name, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
{
if (transaction->mode() != IDBTransaction::VERSION_CHANGE) {
ec = IDBDatabaseException::NOT_ALLOWED_ERR;
return;
}
RefPtr<IDBIndexBackendImpl> index = m_indexes.get(name);
if (!index) {
ec = IDBDatabaseException::NOT_FOUND_ERR;
return;
}
RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
RefPtr<IDBTransactionBackendInterface> transactionPtr = transaction;
if (!transaction->scheduleTask(
createCallbackTask(&IDBObjectStoreBackendImpl::deleteIndexInternal,
objectStore, index, transactionPtr),
createCallbackTask(&IDBObjectStoreBackendImpl::addIndexToMap,
objectStore, index))) {
ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
return;
}
m_indexes.remove(name);
}
void IDBObjectStoreBackendImpl::deleteIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBTransactionBackendInterface> transaction)
{
objectStore->m_backingStore->deleteIndex(objectStore->m_databaseId, objectStore->id(), index->id());
transaction->didCompleteTaskEvents();
}
void IDBObjectStoreBackendImpl::openCursor(PassRefPtr<IDBKeyRange> prpRange, unsigned short direction, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
{
IDB_TRACE("IDBObjectStoreBackendImpl::openCursor");
RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
RefPtr<IDBKeyRange> range = prpRange;
RefPtr<IDBCallbacks> callbacks = prpCallbacks;
RefPtr<IDBTransactionBackendInterface> transactionPtr = transaction;
if (!transaction->scheduleTask(
createCallbackTask(&IDBObjectStoreBackendImpl::openCursorInternal,
objectStore, range, direction, callbacks, transactionPtr))) {
ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
}
}
void IDBObjectStoreBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKeyRange> range, unsigned short tmpDirection, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
{
IDB_TRACE("IDBObjectStoreBackendImpl::openCursorInternal");
IDBCursor::Direction direction = static_cast<IDBCursor::Direction>(tmpDirection);
RefPtr<IDBBackingStore::Cursor> backingStoreCursor = objectStore->m_backingStore->openObjectStoreCursor(objectStore->m_databaseId, objectStore->id(), range.get(), direction);
if (!backingStoreCursor) {
callbacks->onSuccess(SerializedScriptValue::nullValue());
return;
}
RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(backingStoreCursor.release(), direction, IDBCursorBackendInterface::ObjectStoreCursor, transaction.get(), objectStore.get());
callbacks->onSuccess(cursor.release());
}
void IDBObjectStoreBackendImpl::count(PassRefPtr<IDBKeyRange> range, PassRefPtr<IDBCallbacks> callbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
{
IDB_TRACE("IDBObjectStoreBackendImpl::count");
if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::countInternal, this, range, callbacks, transaction)))
ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
}
void IDBObjectStoreBackendImpl::countInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKeyRange> range, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
{
IDB_TRACE("IDBObjectStoreBackendImpl::countInternal");
uint32_t count = 0;
RefPtr<IDBBackingStore::Cursor> backingStoreCursor = objectStore->m_backingStore->openObjectStoreCursor(objectStore->m_databaseId, objectStore->id(), range.get(), IDBCursor::NEXT);
if (!backingStoreCursor) {
callbacks->onSuccess(SerializedScriptValue::numberValue(count));
return;
}
do {
++count;
} while (backingStoreCursor->continueFunction(0));
backingStoreCursor->close();
callbacks->onSuccess(SerializedScriptValue::numberValue(count));
}
void IDBObjectStoreBackendImpl::loadIndexes()
{
Vector<int64_t> ids;
Vector<String> names;
Vector<String> keyPaths;
Vector<bool> uniqueFlags;
Vector<bool> multiEntryFlags;
m_backingStore->getIndexes(m_databaseId, m_id, ids, names, keyPaths, uniqueFlags, multiEntryFlags);
ASSERT(names.size() == ids.size());
ASSERT(keyPaths.size() == ids.size());
ASSERT(uniqueFlags.size() == ids.size());
ASSERT(multiEntryFlags.size() == ids.size());
for (size_t i = 0; i < ids.size(); i++)
m_indexes.set(names[i], IDBIndexBackendImpl::create(m_backingStore.get(), m_databaseId, this, ids[i], names[i], m_name, keyPaths[i], uniqueFlags[i], multiEntryFlags[i]));
}
void IDBObjectStoreBackendImpl::removeIndexFromMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index)
{
ASSERT(objectStore->m_indexes.contains(index->name()));
objectStore->m_indexes.remove(index->name());
}
void IDBObjectStoreBackendImpl::addIndexToMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index)
{
RefPtr<IDBIndexBackendImpl> indexPtr = index;
ASSERT(!objectStore->m_indexes.contains(indexPtr->name()));
objectStore->m_indexes.set(indexPtr->name(), indexPtr);
}
PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::genAutoIncrementKey()
{
if (m_autoIncrementNumber > 0)
return IDBKey::createNumber(m_autoIncrementNumber++);
m_autoIncrementNumber = static_cast<int>(m_backingStore->nextAutoIncrementNumber(m_databaseId, id()));
return IDBKey::createNumber(m_autoIncrementNumber++);
}
} // namespace WebCore
#endif