IndexedDB: add tracing to IDBLevelDBBackingStore
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / IDBObjectStoreBackendImpl.cpp
index 794d650..e521901 100644 (file)
@@ -42,7 +42,7 @@
 #include "IDBKeyPathBackendImpl.h"
 #include "IDBKeyRange.h"
 #include "IDBTracing.h"
-#include "IDBTransactionBackendInterface.h"
+#include "IDBTransactionBackendImpl.h"
 #include "ScriptExecutionContext.h"
 
 namespace WebCore {
@@ -51,352 +51,306 @@ 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)
+IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(const IDBDatabaseBackendImpl* database, int64_t id, const String& name, const IDBKeyPath& keyPath, bool autoIncrement)
+    : m_database(database)
     , 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)
+IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(const IDBDatabaseBackendImpl* database, const String& name, const IDBKeyPath& keyPath, bool autoIncrement)
+    : m_database(database)
     , m_id(InvalidId)
     , m_name(name)
     , m_keyPath(keyPath)
     , m_autoIncrement(autoIncrement)
-    , m_autoIncrementNumber(-1)
 {
 }
 
-PassRefPtr<DOMStringList> IDBObjectStoreBackendImpl::indexNames() const
+IDBObjectStoreMetadata IDBObjectStoreBackendImpl::metadata() const
 {
-    RefPtr<DOMStringList> indexNames = DOMStringList::create();
+    IDBObjectStoreMetadata metadata(m_name, m_keyPath, m_autoIncrement);
     for (IndexMap::const_iterator it = m_indexes.begin(); it != m_indexes.end(); ++it)
-        indexNames->append(it->first);
-    indexNames->sort();
-    return indexNames.release();
+        metadata.indexes.set(it->first, it->second->metadata());
+    return metadata;
 }
 
-// FIXME: This can be removed once all ports have been updated to call
-// the IDBKeyRange version. https://bugs.webkit.org/show_bug.cgi?id=84285
-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::get(PassRefPtr<IDBKeyRange> prpKeyRange, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
+void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKeyRange> prpKeyRange, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
 {
     IDB_TRACE("IDBObjectStoreBackendImpl::get");
     RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
     RefPtr<IDBKeyRange> keyRange = prpKeyRange;
     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
-    if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::getByRangeInternal, objectStore, keyRange, callbacks)))
+    RefPtr<IDBTransactionBackendImpl> transaction = IDBTransactionBackendImpl::from(transactionPtr);
+    if (!transaction->scheduleTask(
+            createCallbackTask(&IDBObjectStoreBackendImpl::getInternal, objectStore, keyRange, callbacks)))
         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
 }
 
-void IDBObjectStoreBackendImpl::getByRangeInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
+void IDBObjectStoreBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
 {
-    IDB_TRACE("IDBObjectStoreBackendImpl::getByRangeInternal");
-    RefPtr<IDBBackingStore::Cursor> backingStoreCursor = objectStore->backingStore()->openObjectStoreCursor(objectStore->databaseId(), objectStore->id(), keyRange.get(), IDBCursor::NEXT);
-    if (!backingStoreCursor) {
-        callbacks->onSuccess(SerializedScriptValue::undefinedValue());
-        return;
-    }
-
-    String wireData = objectStore->backingStore()->getObjectStoreRecord(objectStore->databaseId(), objectStore->id(), *backingStoreCursor->key());
-    if (wireData.isNull()) {
-        callbacks->onSuccess(SerializedScriptValue::undefinedValue());
+    IDB_TRACE("IDBObjectStoreBackendImpl::getInternal");
+    RefPtr<IDBKey> key;
+    if (keyRange->isOnlyKey())
+        key = keyRange->lower();
+    else {
+        RefPtr<IDBBackingStore::Cursor> backingStoreCursor = objectStore->backingStore()->openObjectStoreCursor(objectStore->databaseId(), objectStore->id(), keyRange.get(), IDBCursor::NEXT);
+        if (!backingStoreCursor) {
+            callbacks->onSuccess(SerializedScriptValue::undefinedValue());
+            return;
+        }
+        key = backingStoreCursor->key();
         backingStoreCursor->close();
-        return;
     }
 
-    callbacks->onSuccess(SerializedScriptValue::createFromWire(wireData));
-    backingStoreCursor->close();
-}
-
-void IDBObjectStoreBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks)
-{
-    IDB_TRACE("IDBObjectStoreBackendImpl::getInternal");
     String wireData = objectStore->backingStore()->getObjectStoreRecord(objectStore->databaseId(), objectStore->id(), *key);
     if (wireData.isNull()) {
         callbacks->onSuccess(SerializedScriptValue::undefinedValue());
         return;
     }
 
+    if (objectStore->autoIncrement() && !objectStore->keyPath().isNull()) {
+        callbacks->onSuccess(SerializedScriptValue::createFromWire(wireData),
+                             key, objectStore->keyPath());
+        return;
+    }
     callbacks->onSuccess(SerializedScriptValue::createFromWire(wireData));
 }
 
-static PassRefPtr<IDBKey> fetchKeyFromKeyPath(SerializedScriptValue* value, const String& keyPath)
+void IDBObjectStoreBackendImpl::putWithIndexKeys(PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, PutMode putMode, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transactionPtr, const Vector<String>& indexNames, const Vector<IndexKeys>& indexKeys, ExceptionCode& ec)
 {
-    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;
-    }
+    IDB_TRACE("IDBObjectStoreBackendImpl::putWithIndexKeys");
 
     RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
     RefPtr<SerializedScriptValue> value = prpValue;
     RefPtr<IDBKey> key = prpKey;
     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
-    RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr;
+    RefPtr<IDBTransactionBackendImpl> transaction = IDBTransactionBackendImpl::from(transactionPtr);
+    ASSERT(transaction->mode() != IDBTransaction::READ_ONLY);
 
-    if (putMode != CursorUpdate) {
-        const bool autoIncrement = objectStore->autoIncrement();
-        const bool hasKeyPath = !objectStore->m_keyPath.isNull();
+    OwnPtr<Vector<String> > newIndexNames = adoptPtr(new Vector<String>(indexNames));
+    OwnPtr<Vector<IndexKeys> > newIndexKeys = adoptPtr(new Vector<IndexKeys>(indexKeys));
 
-        if (hasKeyPath && key) {
-            ec = IDBDatabaseException::DATA_ERR;
-            return;
-        }
-        if (!hasKeyPath && !autoIncrement && !key) {
-            ec = IDBDatabaseException::DATA_ERR;
+    ASSERT(autoIncrement() || key);
+
+    if (!transaction->scheduleTask(
+            createCallbackTask(&IDBObjectStoreBackendImpl::putInternal, objectStore, value, key, putMode, callbacks, transaction, newIndexNames.release(), newIndexKeys.release())))
+        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
+}
+
+namespace {
+class IndexWriter {
+public:
+    explicit IndexWriter(const IDBIndexMetadata& indexMetadata)
+        : m_indexMetadata(indexMetadata)
+    { }
+
+    IndexWriter(const IDBIndexMetadata& indexMetadata,
+                const IDBObjectStoreBackendInterface::IndexKeys& indexKeys)
+        : m_indexMetadata(indexMetadata)
+        , m_indexKeys(indexKeys)
+    { }
+
+    // FIXME: remove this once createIndex() generates these in the renderer.
+    void generateIndexKeysForValue(SerializedScriptValue* objectValue)
+    {
+        m_indexKeys.clear();
+
+        RefPtr<IDBKey> indexKey = fetchKeyFromKeyPath(objectValue);
+
+        if (!indexKey)
             return;
-        }
-        if (hasKeyPath) {
-            RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath);
-            if (keyPathKey && !keyPathKey->isValid()) {
-                ec = IDBDatabaseException::DATA_ERR;
-                return;
-            }
-            if (!autoIncrement && !keyPathKey) {
-                ec = IDBDatabaseException::DATA_ERR;
+
+        if (!m_indexMetadata.multiEntry || indexKey->type() != IDBKey::ArrayType) {
+            if (!indexKey->isValid())
                 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->isValid()) {
-            ec = IDBDatabaseException::DATA_ERR;
-            return;
+
+            m_indexKeys.append(indexKey);
+        } else {
+            ASSERT(m_indexMetadata.multiEntry);
+            ASSERT(indexKey->type() == IDBKey::ArrayType);
+            indexKey = IDBKey::createMultiEntryArray(indexKey->array());
+
+            for (size_t i = 0; i < indexKey->array().size(); ++i)
+                m_indexKeys.append(indexKey->array()[i]);
         }
-        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->isValid()) {
-                ec = IDBDatabaseException::DATA_ERR;
-                return;
+    }
+
+    bool verifyIndexKeys(IDBBackingStore& backingStore,
+                         int64_t databaseId, int64_t objectStoreId, int64_t indexId,
+                         const IDBKey* primaryKey = 0, String* errorMessage = 0)
+    {
+        for (size_t i = 0; i < m_indexKeys.size(); ++i) {
+            if (!addingKeyAllowed(backingStore, databaseId, objectStoreId, indexId,
+                                  (m_indexKeys)[i].get(), primaryKey)) {
+                if (errorMessage)
+                    *errorMessage = String::format("Unable to add key to index '%s': at least one key does not satisfy the uniqueness requirements.",
+                                                   m_indexMetadata.name.utf8().data());
+                return false;
             }
         }
-    } 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;
-            }
+        return true;
+    }
+
+    bool writeIndexKeys(const IDBBackingStore::ObjectStoreRecordIdentifier* recordIdentifier, IDBBackingStore& backingStore, int64_t databaseId, int64_t objectStoreId, int64_t indexId) const
+    {
+        for (size_t i = 0; i < m_indexKeys.size(); ++i) {
+            if (!backingStore.deleteIndexDataForRecord(databaseId, objectStoreId, indexId, recordIdentifier))
+                return false;
+            if (!backingStore.putIndexDataForRecord(databaseId, objectStoreId, indexId, *(m_indexKeys)[i].get(), recordIdentifier))
+                return false;
         }
+        return true;
     }
 
-    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;
-}
+    const String& indexName() const { return m_indexMetadata.name; }
 
-void IDBObjectStoreBackendImpl::revertAutoIncrementKeyCache(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore)
-{
-    objectStore->resetAutoIncrementKeyCache();
+private:
+
+    bool addingKeyAllowed(IDBBackingStore& backingStore,
+                          int64_t databaseId, int64_t objectStoreId, int64_t indexId,
+                          const IDBKey* indexKey, const IDBKey* primaryKey) const
+    {
+        if (!m_indexMetadata.unique)
+            return true;
+
+        RefPtr<IDBKey> foundPrimaryKey;
+        bool found = backingStore.keyExistsInIndex(databaseId, objectStoreId, indexId, *indexKey, foundPrimaryKey);
+        if (!found)
+            return true;
+        if (primaryKey && foundPrimaryKey->isEqual(primaryKey))
+            return true;
+        return false;
+    }
+
+    PassRefPtr<IDBKey> fetchKeyFromKeyPath(SerializedScriptValue* value)
+    {
+        IDB_TRACE("IndexWriter::fetchKeyFromKeyPath");
+
+        Vector<RefPtr<SerializedScriptValue> > values;
+        values.append(value);
+        Vector<RefPtr<IDBKey> > keys;
+        IDBKeyPathBackendImpl::createIDBKeysFromSerializedValuesAndKeyPath(values, m_indexMetadata.keyPath, keys);
+        if (keys.isEmpty())
+            return 0;
+        ASSERT(keys.size() == 1);
+        return keys[0].release();
+    }
+
+    const IDBIndexMetadata m_indexMetadata;
+    IDBObjectStoreBackendInterface::IndexKeys m_indexKeys;
+};
 }
 
-void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
+void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendImpl> transaction, PassOwnPtr<Vector<String> > popIndexNames, PassOwnPtr<Vector<IndexKeys> > popIndexKeys)
 {
     IDB_TRACE("IDBObjectStoreBackendImpl::putInternal");
+    ASSERT(transaction->mode() != IDBTransaction::READ_ONLY);
     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 (!autoIncKey->isValid()) {
-                    callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "Maximum key generator value reached."));
-                    return;
-                }
-                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();
-            }
+    OwnPtr<Vector<String> > indexNames = popIndexNames;
+    OwnPtr<Vector<IndexKeys> > indexKeys = popIndexKeys;
+    ASSERT(indexNames && indexKeys && indexNames->size() == indexKeys->size());
+    const bool autoIncrement = objectStore->autoIncrement();
+    bool keyWasGenerated = false;
+
+    if (putMode != CursorUpdate && autoIncrement && !key) {
+        RefPtr<IDBKey> autoIncKey = objectStore->generateKey();
+        keyWasGenerated = true;
+        if (!autoIncKey->isValid()) {
+            callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "Maximum key generator value reached."));
+            return;
         }
+        key = autoIncKey;
     }
 
     ASSERT(key && key->isValid());
 
     RefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> recordIdentifier = objectStore->backingStore()->createInvalidRecordIdentifier();
     if (putMode == AddOnly && objectStore->backingStore()->keyExistsInObjectStore(objectStore->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;
+    Vector<OwnPtr<IndexWriter> > indexWriters;
+    HashMap<String, IndexKeys> indexKeyMap;
+    for (size_t i = 0; i < indexNames->size(); ++i) {
+        IndexKeys keys = indexKeys->at(i);
+
+        // If the objectStore is using autoIncrement, then any indexes with an identical keyPath need to also use the primary (generated) key as a key.
+        if (keyWasGenerated) {
+            const IDBKeyPath& indexKeyPath = objectStore->m_indexes.get(indexNames->at(i))->keyPath();
+            if (indexKeyPath == objectStore->keyPath())
+                keys.append(key);
+        }
+
+        indexKeyMap.add(indexNames->at(i), keys);
+    }
+
     for (IndexMap::iterator it = objectStore->m_indexes.begin(); it != objectStore->m_indexes.end(); ++it) {
+
         const RefPtr<IDBIndexBackendImpl>& index = it->second;
+        if (!index->hasValidId())
+            continue; // The index object has been created, but does not exist in the database yet.
 
-        RefPtr<IDBKey> indexKey = fetchKeyFromKeyPath(value.get(), index->keyPath());
-        if (!indexKey) {
-            indexKeys.append(indexKey.release());
-            continue;
-        }
-        ASSERT(indexKey->isValid());
+        OwnPtr<IndexWriter> indexWriter;
+        indexWriter = adoptPtr(new IndexWriter(index->metadata(), indexKeyMap.get(it->first)));
 
-        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."));
+        String errorMessage;
+        if (!indexWriter->verifyIndexKeys(*objectStore->backingStore(),
+                                          objectStore->databaseId(),
+                                          objectStore->id(),
+                                          index->id(), key.get(), &errorMessage)) {
+            callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, errorMessage));
             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());
+        indexWriters.append(indexWriter.release());
     }
 
     // Before this point, don't do any mutation.  After this point, rollback the transaction in case of error.
 
     if (!objectStore->backingStore()->putObjectStoreRecord(objectStore->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.
+    for (size_t i = 0; i < indexWriters.size(); ++i) {
+        IndexWriter* indexWriter = indexWriters[i].get();
+        if (!indexWriter->writeIndexKeys(recordIdentifier.get(),
+                                         *objectStore->backingStore(),
+                                         objectStore->databaseId(),
+                                         objectStore->m_id,
+                                         objectStore->m_indexes.get(indexWriter->indexName())->id())) {
 
-        if (!objectStore->backingStore()->deleteIndexDataForRecord(objectStore->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->backingStore()->putIndexDataForRecord(objectStore->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->backingStore()->putIndexDataForRecord(objectStore->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->isValid()) {
-        ec = IDBDatabaseException::DATA_ERR;
-        return;
     }
 
-    RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
-    if (ec)
-        return;
+    if (autoIncrement && putMode != CursorUpdate && key->type() == IDBKey::NumberType)
+        objectStore->updateKeyGenerator(key.get(), !keyWasGenerated);
 
-    deleteFunction(keyRange.release(), prpCallbacks, transaction, ec);
+    callbacks->onSuccess(key.release());
 }
 
 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;
-    }
+
+    ASSERT(IDBTransactionBackendImpl::from(transaction)->mode() != IDBTransaction::READ_ONLY);
 
     RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
     RefPtr<IDBKeyRange> keyRange = prpKeyRange;
     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
 
-    if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::deleteInternal, objectStore, keyRange, callbacks)))
+    if (!IDBTransactionBackendImpl::from(transaction)->scheduleTask(
+            createCallbackTask(&IDBObjectStoreBackendImpl::deleteInternal, objectStore, keyRange, callbacks)))
         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
 }
 
@@ -432,15 +386,14 @@ void IDBObjectStoreBackendImpl::deleteInternal(ScriptExecutionContext*, PassRefP
 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;
-    }
+
+    ASSERT(IDBTransactionBackendImpl::from(transaction)->mode() != IDBTransaction::READ_ONLY);
 
     RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
 
-    if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::clearInternal, objectStore, callbacks)))
+    if (!IDBTransactionBackendImpl::from(transaction)->scheduleTask(
+            createCallbackTask(&IDBObjectStoreBackendImpl::clearInternal, objectStore, callbacks)))
         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
 }
 
@@ -453,81 +406,55 @@ void IDBObjectStoreBackendImpl::clearInternal(ScriptExecutionContext*, PassRefPt
 namespace {
 class PopulateIndexCallback : public IDBBackingStore::ObjectStoreRecordCallback {
 public:
-    PopulateIndexCallback(IDBBackingStore& backingStore, int64_t databaseId, int64_t objectStoreId, PassRefPtr<IDBIndexBackendImpl> index)
+    PopulateIndexCallback(IDBBackingStore& backingStore, const IDBIndexMetadata& indexMetadata, int64_t databaseId, int64_t objectStoreId, int64_t indexId)
         : m_backingStore(backingStore)
         , m_databaseId(databaseId)
         , m_objectStoreId(objectStoreId)
-        , m_index(index)
+        , m_indexId(indexId)
+        , m_writer(indexMetadata)
     {
     }
 
     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;
+        m_writer.generateIndexKeysForValue(objectValue.get());
+        if (!m_writer.verifyIndexKeys(m_backingStore, m_databaseId, m_objectStoreId, m_indexId))
+            return false;
+        return m_writer.writeIndexKeys(recordIdentifier, m_backingStore, m_databaseId, m_objectStoreId, m_indexId);
     }
 
 private:
     IDBBackingStore& m_backingStore;
     int64_t m_databaseId;
     int64_t m_objectStoreId;
-    RefPtr<IDBIndexBackendImpl> m_index;
+    int64_t m_indexId;
+    IndexWriter m_writer;
 };
 }
 
 bool IDBObjectStoreBackendImpl::populateIndex(IDBBackingStore& backingStore, int64_t databaseId, int64_t objectStoreId, PassRefPtr<IDBIndexBackendImpl> index)
 {
-    PopulateIndexCallback callback(backingStore, databaseId, objectStoreId, index);
+    PopulateIndexCallback callback(backingStore, index->metadata(), databaseId, objectStoreId, index->id());
     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)
+PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry, IDBTransactionBackendInterface* transactionPtr, 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;
-    }
+    ASSERT(!m_indexes.contains(name));
 
-    RefPtr<IDBIndexBackendImpl> index = IDBIndexBackendImpl::create(backingStore().get(), databaseId(), this, name, keyPath, unique, multiEntry);
+    RefPtr<IDBIndexBackendImpl> index = IDBIndexBackendImpl::create(m_database, this, name, keyPath, unique, multiEntry);
     ASSERT(index->name() == name);
 
+    RefPtr<IDBTransactionBackendImpl> transaction = IDBTransactionBackendImpl::from(transactionPtr);
+    ASSERT(transaction->mode() == IDBTransaction::VERSION_CHANGE);
+
     RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
-    RefPtr<IDBTransactionBackendInterface> transactionPtr = transaction;
     if (!transaction->scheduleTask(
-              createCallbackTask(&IDBObjectStoreBackendImpl::createIndexInternal,
-                                 objectStore, index, transactionPtr),
-              createCallbackTask(&IDBObjectStoreBackendImpl::removeIndexFromMap,
-                                 objectStore, index))) {
+              createCallbackTask(&IDBObjectStoreBackendImpl::createIndexInternal, objectStore, index, transaction),
+              createCallbackTask(&IDBObjectStoreBackendImpl::removeIndexFromMap, objectStore, index))) {
         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
         return 0;
     }
@@ -536,7 +463,7 @@ PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(cons
     return index.release();
 }
 
-void IDBObjectStoreBackendImpl::createIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBTransactionBackendInterface> transaction)
+void IDBObjectStoreBackendImpl::createIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBTransactionBackendImpl> transaction)
 {
     int64_t id;
     if (!objectStore->backingStore()->createIndex(objectStore->databaseId(), objectStore->id(), index->name(), index->keyPath(), index->unique(), index->multiEntry(), id)) {
@@ -558,59 +485,50 @@ PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::index(const Stri
 {
     RefPtr<IDBIndexBackendInterface> index = m_indexes.get(name);
     if (!index) {
-        ec = IDBDatabaseException::NOT_FOUND_ERR;
+        ec = IDBDatabaseException::IDB_NOT_FOUND_ERR;
         return 0;
     }
     return index.release();
 }
 
-void IDBObjectStoreBackendImpl::deleteIndex(const String& name, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
+void IDBObjectStoreBackendImpl::deleteIndex(const String& name, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
 {
-    if (transaction->mode() != IDBTransaction::VERSION_CHANGE) {
-        ec = IDBDatabaseException::NOT_ALLOWED_ERR;
-        return;
-    }
+    ASSERT(m_indexes.contains(name));
 
+    RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
     RefPtr<IDBIndexBackendImpl> index = m_indexes.get(name);
-    if (!index) {
-        ec = IDBDatabaseException::NOT_FOUND_ERR;
-        return;
-    }
+    RefPtr<IDBTransactionBackendImpl> transaction = IDBTransactionBackendImpl::from(transactionPtr);
+    ASSERT(transaction->mode() == IDBTransaction::VERSION_CHANGE);
 
-    RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
-    RefPtr<IDBTransactionBackendInterface> transactionPtr = transaction;
     if (!transaction->scheduleTask(
-              createCallbackTask(&IDBObjectStoreBackendImpl::deleteIndexInternal,
-                                 objectStore, index, transactionPtr),
-              createCallbackTask(&IDBObjectStoreBackendImpl::addIndexToMap,
-                                 objectStore, index))) {
+              createCallbackTask(&IDBObjectStoreBackendImpl::deleteIndexInternal, objectStore, index, transaction),
+              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)
+void IDBObjectStoreBackendImpl::deleteIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBTransactionBackendImpl> transaction)
 {
     objectStore->backingStore()->deleteIndex(objectStore->databaseId(), objectStore->id(), index->id());
     transaction->didCompleteTaskEvents();
 }
 
-void IDBObjectStoreBackendImpl::openCursor(PassRefPtr<IDBKeyRange> prpRange, unsigned short direction, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
+void IDBObjectStoreBackendImpl::openCursor(PassRefPtr<IDBKeyRange> prpRange, unsigned short direction, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
 {
     IDB_TRACE("IDBObjectStoreBackendImpl::openCursor");
     RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
     RefPtr<IDBKeyRange> range = prpRange;
     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
-    RefPtr<IDBTransactionBackendInterface> transactionPtr = transaction;
+    RefPtr<IDBTransactionBackendImpl> transaction = IDBTransactionBackendImpl::from(transactionPtr);
     if (!transaction->scheduleTask(
-            createCallbackTask(&IDBObjectStoreBackendImpl::openCursorInternal,
-                               objectStore, range, direction, callbacks, transactionPtr))) {
+            createCallbackTask(&IDBObjectStoreBackendImpl::openCursorInternal, objectStore, range, direction, callbacks, transaction))) {
         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
     }
 }
 
-void IDBObjectStoreBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKeyRange> range, unsigned short tmpDirection, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
+void IDBObjectStoreBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKeyRange> range, unsigned short tmpDirection, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendImpl> transaction)
 {
     IDB_TRACE("IDBObjectStoreBackendImpl::openCursorInternal");
     IDBCursor::Direction direction = static_cast<IDBCursor::Direction>(tmpDirection);
@@ -621,18 +539,19 @@ void IDBObjectStoreBackendImpl::openCursorInternal(ScriptExecutionContext*, Pass
         return;
     }
 
-    RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(backingStoreCursor.release(), direction, IDBCursorBackendInterface::ObjectStoreCursor, transaction.get(), objectStore.get());
-    callbacks->onSuccess(cursor.release());
+    RefPtr<IDBCursorBackendImpl> cursor = IDBCursorBackendImpl::create(backingStoreCursor.release(), IDBCursorBackendInterface::ObjectStoreCursor, transaction.get(), objectStore.get());
+    callbacks->onSuccess(cursor, cursor->key(), cursor->primaryKey(), cursor->value());
 }
 
 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)))
+    if (!IDBTransactionBackendImpl::from(transaction)->scheduleTask(
+            createCallbackTask(&IDBObjectStoreBackendImpl::countInternal, this, range, callbacks)))
         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
 }
 
-void IDBObjectStoreBackendImpl::countInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKeyRange> range, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface>)
+void IDBObjectStoreBackendImpl::countInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKeyRange> range, PassRefPtr<IDBCallbacks> callbacks)
 {
     IDB_TRACE("IDBObjectStoreBackendImpl::countInternal");
     uint32_t count = 0;
@@ -654,7 +573,7 @@ void IDBObjectStoreBackendImpl::loadIndexes()
 {
     Vector<int64_t> ids;
     Vector<String> names;
-    Vector<String> keyPaths;
+    Vector<IDBKeyPath> keyPaths;
     Vector<bool> uniqueFlags;
     Vector<bool> multiEntryFlags;
     backingStore()->getIndexes(databaseId(), m_id, ids, names, keyPaths, uniqueFlags, multiEntryFlags);
@@ -664,8 +583,8 @@ void IDBObjectStoreBackendImpl::loadIndexes()
     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(backingStore().get(), databaseId(), this, ids[i], names[i], keyPaths[i], uniqueFlags[i], multiEntryFlags[i]));
+    for (size_t i = 0; i < ids.size(); ++i)
+        m_indexes.set(names[i], IDBIndexBackendImpl::create(m_database, this, ids[i], names[i], keyPaths[i], uniqueFlags[i], multiEntryFlags[i]));
 }
 
 void IDBObjectStoreBackendImpl::removeIndexFromMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index)
@@ -681,18 +600,20 @@ void IDBObjectStoreBackendImpl::addIndexToMap(ScriptExecutionContext*, PassRefPt
     objectStore->m_indexes.set(indexPtr->name(), indexPtr);
 }
 
-PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::genAutoIncrementKey()
+PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::generateKey()
 {
-    const int64_t kMaxGeneratorValue = 9007199254740992LL; // Maximum integer storable as ECMAScript number.
-    if (m_autoIncrementNumber > kMaxGeneratorValue)
+    const int64_t maxGeneratorValue = 9007199254740992LL; // Maximum integer storable as ECMAScript number.
+    int64_t currentNumber = backingStore()->getKeyGeneratorCurrentNumber(databaseId(), id());
+    if (currentNumber < 0 || currentNumber > maxGeneratorValue)
         return IDBKey::createInvalid();
-    if (m_autoIncrementNumber > 0)
-        return IDBKey::createNumber(m_autoIncrementNumber++);
 
-    m_autoIncrementNumber = backingStore()->nextAutoIncrementNumber(databaseId(), id());
-    if (m_autoIncrementNumber > kMaxGeneratorValue)
-        return IDBKey::createInvalid();
-    return IDBKey::createNumber(m_autoIncrementNumber++);
+    return IDBKey::createNumber(currentNumber);
+}
+
+void IDBObjectStoreBackendImpl::updateKeyGenerator(const IDBKey* key, bool checkCurrent)
+{
+    ASSERT(key && key->type() == IDBKey::NumberType);
+    backingStore()->maybeUpdateKeyGeneratorCurrentNumber(databaseId(), id(), static_cast<int64_t>(floor(key->number())) + 1, checkCurrent);
 }