diff options
author | Ben Murdoch <benm@google.com> | 2011-06-02 12:07:03 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2011-06-10 10:47:21 +0100 |
commit | 2daae5fd11344eaa88a0d92b0f6d65f8d2255c00 (patch) | |
tree | e4964fbd1cb70599f7718ff03e50ea1dab33890b /Source/WebCore/storage | |
parent | 87bdf0060a247bfbe668342b87e0874182e0ffa9 (diff) | |
download | external_webkit-2daae5fd11344eaa88a0d92b0f6d65f8d2255c00.zip external_webkit-2daae5fd11344eaa88a0d92b0f6d65f8d2255c00.tar.gz external_webkit-2daae5fd11344eaa88a0d92b0f6d65f8d2255c00.tar.bz2 |
Merge WebKit at r84325: Initial merge by git.
Change-Id: Ic1a909300ecc0a13ddc6b4e784371d2ac6e3d59b
Diffstat (limited to 'Source/WebCore/storage')
21 files changed, 3356 insertions, 158 deletions
diff --git a/Source/WebCore/storage/IDBBackingStore.h b/Source/WebCore/storage/IDBBackingStore.h index 29523a2..4b96442 100644 --- a/Source/WebCore/storage/IDBBackingStore.h +++ b/Source/WebCore/storage/IDBBackingStore.h @@ -50,30 +50,38 @@ public: virtual bool setIDBDatabaseMetaData(const String& name, const String& version, int64_t& rowId, bool invalidRowId) = 0; virtual void getObjectStores(int64_t databaseId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundAutoIncrementFlags) = 0; - virtual bool createObjectStore(const String& name, const String& keyPath, bool autoIncrement, int64_t databaseId, int64_t& assignedObjectStoreId) = 0; - virtual void deleteObjectStore(int64_t objectStoreId) = 0; - virtual String getObjectStoreRecord(int64_t objectStoreId, const IDBKey&) = 0; - virtual bool putObjectStoreRecord(int64_t objectStoreId, const IDBKey&, const String& value, int64_t& rowId, bool invalidRowId) = 0; - virtual void clearObjectStore(int64_t objectStoreId) = 0; - virtual void deleteObjectStoreRecord(int64_t objectStoreId, int64_t objectStoreDataId) = 0; - virtual double nextAutoIncrementNumber(int64_t objectStoreId) = 0; - virtual bool keyExistsInObjectStore(int64_t objectStoreId, const IDBKey&, int64_t& foundObjectStoreDataId) = 0; + virtual bool createObjectStore(int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement, int64_t& assignedObjectStoreId) = 0; + virtual void deleteObjectStore(int64_t databaseId, int64_t objectStoreId) = 0; + + class ObjectStoreRecordIdentifier : public RefCounted<ObjectStoreRecordIdentifier> { + public: + virtual bool isValid() const = 0; + virtual ~ObjectStoreRecordIdentifier() {} + }; + virtual PassRefPtr<ObjectStoreRecordIdentifier> createInvalidRecordIdentifier() = 0; + + virtual String getObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey&) = 0; + virtual bool putObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey&, const String& value, ObjectStoreRecordIdentifier*) = 0; + virtual void clearObjectStore(int64_t databaseId, int64_t objectStoreId) = 0; + virtual void deleteObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const ObjectStoreRecordIdentifier*) = 0; + virtual double nextAutoIncrementNumber(int64_t databaseId, int64_t objectStoreId) = 0; + virtual bool keyExistsInObjectStore(int64_t databaseId, int64_t objectStoreId, const IDBKey&, ObjectStoreRecordIdentifier* foundRecordIdentifier) = 0; class ObjectStoreRecordCallback { public: - virtual bool callback(int64_t objectStoreDataId, const String& value) = 0; + virtual bool callback(const ObjectStoreRecordIdentifier*, const String& value) = 0; virtual ~ObjectStoreRecordCallback() {}; }; - virtual bool forEachObjectStoreRecord(int64_t objectStoreId, ObjectStoreRecordCallback&) = 0; + virtual bool forEachObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, ObjectStoreRecordCallback&) = 0; - virtual void getIndexes(int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags) = 0; - virtual bool createIndex(int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId) = 0; - virtual void deleteIndex(int64_t indexId) = 0; - virtual bool putIndexDataForRecord(int64_t indexId, const IDBKey&, int64_t objectStoreDataId) = 0; - virtual bool deleteIndexDataForRecord(int64_t objectStoreDataId) = 0; - virtual String getObjectViaIndex(int64_t indexId, const IDBKey&) = 0; - virtual PassRefPtr<IDBKey> getPrimaryKeyViaIndex(int64_t indexId, const IDBKey&) = 0; - virtual bool keyExistsInIndex(int64_t indexId, const IDBKey&) = 0; + virtual void getIndexes(int64_t databaseId, int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags) = 0; + virtual bool createIndex(int64_t databaseId, int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId) = 0; + virtual void deleteIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId) = 0; + virtual bool putIndexDataForRecord(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, const ObjectStoreRecordIdentifier*) = 0; + virtual bool deleteIndexDataForRecord(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const ObjectStoreRecordIdentifier*) = 0; + virtual String getObjectViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&) = 0; + virtual PassRefPtr<IDBKey> getPrimaryKeyViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&) = 0; + virtual bool keyExistsInIndex(int64_t databaseid, int64_t objectStoreId, int64_t indexId, const IDBKey&) = 0; class Cursor : public RefCounted<Cursor> { public: @@ -81,14 +89,14 @@ public: virtual PassRefPtr<IDBKey> key() = 0; virtual PassRefPtr<IDBKey> primaryKey() = 0; virtual String value() = 0; - virtual int64_t objectStoreDataId() = 0; + virtual PassRefPtr<ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() = 0; virtual int64_t indexDataId() = 0; virtual ~Cursor() {}; }; - virtual PassRefPtr<Cursor> openObjectStoreCursor(int64_t objectStoreId, const IDBKeyRange*, IDBCursor::Direction) = 0; - virtual PassRefPtr<Cursor> openIndexKeyCursor(int64_t indexId, const IDBKeyRange*, IDBCursor::Direction) = 0; - virtual PassRefPtr<Cursor> openIndexCursor(int64_t indexId, const IDBKeyRange*, IDBCursor::Direction) = 0; + virtual PassRefPtr<Cursor> openObjectStoreCursor(int64_t databaseId, int64_t objectStoreId, const IDBKeyRange*, IDBCursor::Direction) = 0; + virtual PassRefPtr<Cursor> openIndexKeyCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IDBCursor::Direction) = 0; + virtual PassRefPtr<Cursor> openIndexCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IDBCursor::Direction) = 0; class Transaction : public RefCounted<Transaction> { public: diff --git a/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp b/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp index 94e305b..8fe971a 100644 --- a/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp +++ b/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * 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 @@ -97,7 +97,7 @@ PassRefPtr<DOMStringList> IDBDatabaseBackendImpl::objectStoreNames() const return objectStoreNames.release(); } -PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec) +PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec) { ASSERT(transactionPtr->mode() == IDBTransaction::VERSION_CHANGE); @@ -106,7 +106,7 @@ PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObject return 0; } - RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(m_backingStore.get(), name, keyPath, autoIncrement); + RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(m_backingStore.get(), m_id, name, keyPath, autoIncrement); ASSERT(objectStore->name() == name); RefPtr<IDBDatabaseBackendImpl> database = this; @@ -125,7 +125,7 @@ void IDBDatabaseBackendImpl::createObjectStoreInternal(ScriptExecutionContext*, { int64_t objectStoreId; - if (!database->m_backingStore->createObjectStore(objectStore->name(), objectStore->keyPath(), objectStore->autoIncrement(), database->id(), objectStoreId)) { + if (!database->m_backingStore->createObjectStore(database->id(), objectStore->name(), objectStore->keyPath(), objectStore->autoIncrement(), objectStoreId)) { transaction->abort(); return; } @@ -158,7 +158,7 @@ void IDBDatabaseBackendImpl::deleteObjectStore(const String& name, IDBTransactio void IDBDatabaseBackendImpl::deleteObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction) { - database->m_backingStore->deleteObjectStore(objectStore->id()); + database->m_backingStore->deleteObjectStore(database->id(), objectStore->id()); transaction->didCompleteTaskEvents(); } @@ -250,7 +250,7 @@ void IDBDatabaseBackendImpl::loadObjectStores() ASSERT(autoIncrementFlags.size() == ids.size()); for (size_t i = 0; i < ids.size(); i++) - m_objectStores.set(names[i], IDBObjectStoreBackendImpl::create(m_backingStore.get(), ids[i], names[i], keyPaths[i], autoIncrementFlags[i])); + m_objectStores.set(names[i], IDBObjectStoreBackendImpl::create(m_backingStore.get(), m_id, ids[i], names[i], keyPaths[i], autoIncrementFlags[i])); } void IDBDatabaseBackendImpl::removeObjectStoreFromMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore) diff --git a/Source/WebCore/storage/IDBFactoryBackendImpl.cpp b/Source/WebCore/storage/IDBFactoryBackendImpl.cpp index 461e930..4768b3e 100644 --- a/Source/WebCore/storage/IDBFactoryBackendImpl.cpp +++ b/Source/WebCore/storage/IDBFactoryBackendImpl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * 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 @@ -32,6 +32,7 @@ #include "DOMStringList.h" #include "IDBDatabaseBackendImpl.h" #include "IDBDatabaseException.h" +#include "IDBLevelDBBackingStore.h" #include "IDBSQLiteBackingStore.h" #include "IDBTransactionCoordinator.h" #include "SecurityOrigin.h" @@ -69,7 +70,7 @@ void IDBFactoryBackendImpl::removeIDBBackingStore(const String& uniqueIdentifier m_backingStoreMap.remove(uniqueIdentifier); } -void IDBFactoryBackendImpl::open(const String& name, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<SecurityOrigin> securityOrigin, Frame*, const String& dataDir, int64_t maximumSize, BackingStoreType) +void IDBFactoryBackendImpl::open(const String& name, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<SecurityOrigin> securityOrigin, Frame*, const String& dataDir, int64_t maximumSize, BackingStoreType backingStoreType) { String fileIdentifier = securityOrigin->databaseIdentifier(); String uniqueIdentifier = fileIdentifier + "@" + name; @@ -86,7 +87,12 @@ void IDBFactoryBackendImpl::open(const String& name, PassRefPtr<IDBCallbacks> ca if (it2 != m_backingStoreMap.end()) backingStore = it2->second; else { - backingStore = IDBSQLiteBackingStore::open(securityOrigin.get(), dataDir, maximumSize, fileIdentifier, this); + if (backingStoreType == DefaultBackingStore) + backingStore = IDBSQLiteBackingStore::open(securityOrigin.get(), dataDir, maximumSize, fileIdentifier, this); +#if ENABLE(LEVELDB) + else if (backingStoreType == LevelDBBackingStore) + backingStore = IDBLevelDBBackingStore::open(securityOrigin.get(), dataDir, maximumSize, fileIdentifier, this); +#endif if (!backingStore) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error.")); return; diff --git a/Source/WebCore/storage/IDBIndexBackendImpl.cpp b/Source/WebCore/storage/IDBIndexBackendImpl.cpp index 62e5364..38f95f1 100644 --- a/Source/WebCore/storage/IDBIndexBackendImpl.cpp +++ b/Source/WebCore/storage/IDBIndexBackendImpl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * 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 @@ -40,8 +40,10 @@ namespace WebCore { -IDBIndexBackendImpl::IDBIndexBackendImpl(IDBBackingStore* backingStore, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique) +IDBIndexBackendImpl::IDBIndexBackendImpl(IDBBackingStore* backingStore, int64_t databaseId, const IDBObjectStoreBackendImpl* objectStoreBackend, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique) : m_backingStore(backingStore) + , m_databaseId(databaseId) + , m_objectStoreBackend(objectStoreBackend) , m_id(id) , m_name(name) , m_storeName(storeName) @@ -50,8 +52,10 @@ IDBIndexBackendImpl::IDBIndexBackendImpl(IDBBackingStore* backingStore, int64_t { } -IDBIndexBackendImpl::IDBIndexBackendImpl(IDBBackingStore* backingStore, const String& name, const String& storeName, const String& keyPath, bool unique) +IDBIndexBackendImpl::IDBIndexBackendImpl(IDBBackingStore* backingStore, int64_t databaseId, const IDBObjectStoreBackendImpl* objectStoreBackend, const String& name, const String& storeName, const String& keyPath, bool unique) : m_backingStore(backingStore) + , m_databaseId(databaseId) + , m_objectStoreBackend(objectStoreBackend) , m_id(InvalidId) , m_name(name) , m_storeName(storeName) @@ -72,10 +76,10 @@ void IDBIndexBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr switch (cursorType) { case IDBCursorBackendInterface::IndexKeyCursor: - backingStoreCursor = index->m_backingStore->openIndexKeyCursor(index->id(), range.get(), direction); + backingStoreCursor = index->m_backingStore->openIndexKeyCursor(index->m_databaseId, index->m_objectStoreBackend->id(), index->id(), range.get(), direction); break; case IDBCursorBackendInterface::IndexCursor: - backingStoreCursor = index->m_backingStore->openIndexCursor(index->id(), range.get(), direction); + backingStoreCursor = index->m_backingStore->openIndexCursor(index->m_databaseId, index->m_objectStoreBackend->id(), index->id(), range.get(), direction); break; case IDBCursorBackendInterface::ObjectStoreCursor: case IDBCursorBackendInterface::InvalidCursorType: @@ -120,14 +124,14 @@ void IDBIndexBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr<IDBInd { // FIXME: Split getInternal into two functions, getting rid off |getObject|. if (getObject) { - String value = index->m_backingStore->getObjectViaIndex(index->id(), *key); + String value = index->m_backingStore->getObjectViaIndex(index->m_databaseId, index->m_objectStoreBackend->id(), index->id(), *key); if (value.isNull()) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the index.")); return; } callbacks->onSuccess(SerializedScriptValue::createFromWire(value)); } else { - RefPtr<IDBKey> keyResult = index->m_backingStore->getPrimaryKeyViaIndex(index->id(), *key); + RefPtr<IDBKey> keyResult = index->m_backingStore->getPrimaryKeyViaIndex(index->m_databaseId, index->m_objectStoreBackend->id(), index->id(), *key); if (!keyResult) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the index.")); return; @@ -159,7 +163,7 @@ bool IDBIndexBackendImpl::addingKeyAllowed(IDBKey* key) if (!m_unique) return true; - return !m_backingStore->keyExistsInIndex(m_id, *key); + return !m_backingStore->keyExistsInIndex(m_databaseId, m_objectStoreBackend->id(), m_id, *key); } } // namespace WebCore diff --git a/Source/WebCore/storage/IDBIndexBackendImpl.h b/Source/WebCore/storage/IDBIndexBackendImpl.h index e2a06b8..2d42a08 100644 --- a/Source/WebCore/storage/IDBIndexBackendImpl.h +++ b/Source/WebCore/storage/IDBIndexBackendImpl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * 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 @@ -40,13 +40,13 @@ class ScriptExecutionContext; class IDBIndexBackendImpl : public IDBIndexBackendInterface { public: - static PassRefPtr<IDBIndexBackendImpl> create(IDBBackingStore* backingStore, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique) + static PassRefPtr<IDBIndexBackendImpl> create(IDBBackingStore* backingStore, int64_t databaseId, const IDBObjectStoreBackendImpl* objectStoreBackend, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique) { - return adoptRef(new IDBIndexBackendImpl(backingStore, id, name, storeName, keyPath, unique)); + return adoptRef(new IDBIndexBackendImpl(backingStore, databaseId, objectStoreBackend, id, name, storeName, keyPath, unique)); } - static PassRefPtr<IDBIndexBackendImpl> create(IDBBackingStore* backingStore, const String& name, const String& storeName, const String& keyPath, bool unique) + static PassRefPtr<IDBIndexBackendImpl> create(IDBBackingStore* backingStore, int64_t databaseId, const IDBObjectStoreBackendImpl* objectStoreBackend, const String& name, const String& storeName, const String& keyPath, bool unique) { - return adoptRef(new IDBIndexBackendImpl(backingStore, name, storeName, keyPath, unique)); + return adoptRef(new IDBIndexBackendImpl(backingStore, databaseId, objectStoreBackend, name, storeName, keyPath, unique)); } virtual ~IDBIndexBackendImpl(); @@ -72,8 +72,8 @@ public: virtual void getKey(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); private: - IDBIndexBackendImpl(IDBBackingStore*, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique); - IDBIndexBackendImpl(IDBBackingStore*, const String& name, const String& storeName, const String& keyPath, bool unique); + IDBIndexBackendImpl(IDBBackingStore*, int64_t databaseId, const IDBObjectStoreBackendImpl*, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique); + IDBIndexBackendImpl(IDBBackingStore*, int64_t databaseId, const IDBObjectStoreBackendImpl*, const String& name, const String& storeName, const String& keyPath, bool unique); static void openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBKeyRange>, unsigned short direction, IDBCursorBackendInterface::CursorType, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>); static void getInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBKey>, bool getObject, PassRefPtr<IDBCallbacks>); @@ -82,6 +82,8 @@ private: RefPtr<IDBBackingStore> m_backingStore; + int64_t m_databaseId; + const IDBObjectStoreBackendImpl* m_objectStoreBackend; int64_t m_id; String m_name; String m_storeName; diff --git a/Source/WebCore/storage/IDBLevelDBBackingStore.cpp b/Source/WebCore/storage/IDBLevelDBBackingStore.cpp new file mode 100644 index 0000000..09d667f --- /dev/null +++ b/Source/WebCore/storage/IDBLevelDBBackingStore.cpp @@ -0,0 +1,2613 @@ +/* + * 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 "IDBLevelDBBackingStore.h" + +#if ENABLE(INDEXED_DATABASE) +#if ENABLE(LEVELDB) + +#include "Assertions.h" +#include "FileSystem.h" +#include "IDBFactoryBackendImpl.h" +#include "IDBKeyRange.h" +#include "LevelDBComparator.h" +#include "LevelDBDatabase.h" +#include "LevelDBIterator.h" +#include "LevelDBSlice.h" +#include "SecurityOrigin.h" + +#ifndef INT64_MAX +// FIXME: We shouldn't need to rely on these macros. +#define INT64_MAX 0x7fffffffffffffffLL +#endif +#ifndef INT32_MAX +#define INT32_MAX 0x7fffffffL +#endif + +// LevelDB stores key/value pairs. Keys and values are strings of bytes, normally of type Vector<char>. +// +// The keys in the backing store are variable-length tuples with different types +// of fields. Each key in the backing store starts with a ternary prefix: (database id, object store id, index id). For each, 0 is reserved for meta-data. +// The prefix makes sure that data for a specific database, object store, and index are grouped together. The locality is important for performance: common +// operations should only need a minimal number of seek operations. For example, all the meta-data for a database is grouped together so that reading that +// meta-data only requires one seek. +// +// Each key type has a class (in square brackets below) which knows how to encode, decode, and compare that key type. +// +// Global meta-data have keys with prefix (0,0,0), followed by a type byte: +// +// <0, 0, 0, 0> => IndexedDB/LevelDB schema version (0 for now) [SchemaVersionKey] +// <0, 0, 0, 1> => The maximum database id ever allocated [MaxDatabaseIdKey] +// <0, 0, 0, 100, database id> => Existence implies the database id is in the free list [DatabaseFreeListKey] +// <0, 0, 0, 201, utf16 origin name, utf16 database name> => Database id [DatabaseNameKey] +// +// +// Database meta-data: +// +// Again, the prefix is followed by a type byte. +// +// <database id, 0, 0, 0> => utf16 origin name [DatabaseMetaDataKey] +// <database id, 0, 0, 1> => utf16 database name [DatabaseMetaDataKey] +// <database id, 0, 0, 2> => utf16 user version data [DatabaseMetaDataKey] +// <database id, 0, 0, 3> => maximum object store id ever allocated [DatabaseMetaDataKey] +// +// +// Object store meta-data: +// +// The prefix is followed by a type byte, then a variable-length integer, and then another variable-length integer (FIXME: this should be a byte). +// +// <database id, 0, 0, 50, object store id, 0> => utf16 object store name [ObjectStoreMetaDataKey] +// <database id, 0, 0, 50, object store id, 1> => utf16 key path [ObjectStoreMetaDataKey] +// <database id, 0, 0, 50, object store id, 2> => has auto increment [ObjectStoreMetaDataKey] +// <database id, 0, 0, 50, object store id, 3> => is evictable [ObjectStoreMetaDataKey] +// <database id, 0, 0, 50, object store id, 4> => last "version" number [ObjectStoreMetaDataKey] +// <database id, 0, 0, 50, object store id, 5> => maximum index id ever allocated [ObjectStoreMetaDataKey] +// +// +// Index meta-data: +// +// The prefix is followed by a type byte, then two variable-length integers, and then another type byte. +// +// <database id, 0, 0, 100, object store id, index id, 0> => utf16 index name [IndexMetaDataKey] +// <database id, 0, 0, 100, object store id, index id, 1> => are index keys unique [IndexMetaDataKey] +// <database id, 0, 0, 100, object store id, index id, 2> => utf16 key path [IndexMetaDataKey] +// +// +// Other object store and index meta-data: +// +// The prefix is followed by a type byte. The object store and index id are variable length integers, the utf16 strings are variable length strings. +// +// <database id, 0, 0, 150, object store id> => existence implies the object store id is in the free list [ObjectStoreFreeListKey] +// <database id, 0, 0, 151, object store id, index id> => existence implies the index id is in the free list [IndexFreeListKey] +// <database id, 0, 0, 200, utf16 object store name> => object store id [ObjectStoreNamesKey] +// <database id, 0, 0, 201, object store id, utf16 index name> => index id [IndexNamesKey] +// +// +// Object store data: +// +// The prefix is followed by a type byte. The user key is an encoded IDBKey. +// +// <database id, object store id, 1, user key> => "version", serialized script value [ObjectStoreDataKey] +// +// +// "Exists" entry: +// +// The prefix is followed by a type byte. The user key is an encoded IDBKey. +// +// <database id, object store id, 2, user key> => "version" [ExistsEntryKey] +// +// +// Index data: +// +// The prefix is followed by a type byte. The user key is an encoded IDBKey. The sequence number is a variable length integer. +// +// <database id, object store id, index id, user key, sequence number> => "version", user key [IndexDataKey] +// +// (The sequence number is used to allow two entries with the same user key +// in non-unique indexes. The "version" field is used to weed out stale +// index data. Whenever new object store data is inserted, it gets a new +// "version" number, and new index data is written with this number. When +// the index is used for look-ups, entries are validated against the +// "exists" entries, and records with old "version" numbers are deleted +// when they are encountered in getPrimaryKeyViaIndex, +// IndexCursorImpl::loadCurrentRow, and IndexKeyCursorImpl::loadCurrentRow). + +static const unsigned char kIDBKeyNullTypeByte = 0; +static const unsigned char kIDBKeyStringTypeByte = 1; +static const unsigned char kIDBKeyDateTypeByte = 2; +static const unsigned char kIDBKeyNumberTypeByte = 3; +static const unsigned char kIDBKeyMinKeyTypeByte = 4; + +static const unsigned char kMinimumIndexId = 30; +static const unsigned char kObjectStoreDataIndexId = 1; +static const unsigned char kExistsEntryIndexId = 2; + +static const unsigned char kSchemaVersionTypeByte = 0; +static const unsigned char kMaxDatabaseIdTypeByte = 1; +static const unsigned char kDatabaseFreeListTypeByte = 100; +static const unsigned char kDatabaseNameTypeByte = 201; + +static const unsigned char kObjectStoreMetaDataTypeByte = 50; +static const unsigned char kIndexMetaDataTypeByte = 100; +static const unsigned char kObjectStoreFreeListTypeByte = 150; +static const unsigned char kIndexFreeListTypeByte = 151; +static const unsigned char kObjectStoreNamesTypeByte = 200; +static const unsigned char kIndexNamesKeyTypeByte = 201; + +namespace WebCore { + +static Vector<char> encodeByte(unsigned char c) +{ + Vector<char> v; + v.append(c); + return v; +} + +static Vector<char> maxIDBKey() +{ + return encodeByte(kIDBKeyNullTypeByte); +} + +static Vector<char> minIDBKey() +{ + return encodeByte(kIDBKeyMinKeyTypeByte); +} + +static Vector<char> encodeInt(int64_t n) +{ + ASSERT(n >= 0); + Vector<char> ret; // FIXME: Size this at creation. + + do { + unsigned char c = n; + ret.append(c); + n >>= 8; + } while (n); + + return ret; +} + +static int64_t decodeInt(const char* begin, const char* end) +{ + ASSERT(begin <= end); + int64_t ret = 0; + + while (begin < end) { + unsigned char c = *begin++; + ret = (ret << 8) | c; + } + + return ret; +} + +static Vector<char> encodeVarInt(int64_t n) +{ + Vector<char> ret; // FIXME: Size this at creation. + + do { + unsigned char c = n & 0x7f; + n >>= 7; + if (n) + c |= 128; + ret.append(c); + } while (n); + + return ret; +} + +static const char* decodeVarInt(const char *p, const char* limit, int64_t& foundInt) +{ + ASSERT(limit >= p); + foundInt = 0; + + do { + if (p >= limit) + return 0; + + foundInt = (foundInt << 7) | (*p & 0x7f); + } while (*p++ & 128); + return p; +} + +static Vector<char> encodeString(const String& s) +{ + Vector<char> ret; // FIXME: Size this at creation. + + for (unsigned i = 0; i < s.length(); ++i) { + UChar u = s[i]; + unsigned char hi = u >> 8; + unsigned char lo = u; + ret.append(hi); + ret.append(lo); + } + + return ret; +} + +static String decodeString(const char* p, const char* end) +{ + ASSERT(end >= p); + ASSERT(!((end - p) % 2)); + + size_t len = (end - p) / 2; + Vector<UChar> vector(len); + + for (size_t i = 0; i < len; ++i) { + unsigned char hi = *p++; + unsigned char lo = *p++; + + vector[i] = (hi << 8) | lo; + } + + return String::adopt(vector); +} + +static Vector<char> encodeStringWithLength(const String& s) +{ + Vector<char> ret = encodeVarInt(s.length()); + ret.append(encodeString(s)); + return ret; +} + +static const char* decodeStringWithLength(const char* p, const char* limit, String& foundString) +{ + ASSERT(limit >= p); + int64_t len; + p = decodeVarInt(p, limit, len); + if (!p) + return 0; + if (p + len * 2 > limit) + return 0; + + foundString = decodeString(p, p + len * 2); + p += len * 2; + return p; +} + +static Vector<char> encodeDouble(double x) +{ + // FIXME: It would be nice if we could be byte order independent. + const char* p = reinterpret_cast<char*>(&x); + Vector<char> v; + v.append(p, sizeof(x)); + ASSERT(v.size() == sizeof(x)); + return v; +} + +static const char* decodeDouble(const char* p, const char* limit, double* d) +{ + if (p + sizeof(*d) > limit) + return 0; + + char* x = reinterpret_cast<char*>(d); + for (size_t i = 0; i < sizeof(*d); ++i) + *x++ = *p++; + return p; +} + +static Vector<char> encodeIDBKey(const IDBKey& key) +{ + Vector<char> ret; + + switch (key.type()) { + case IDBKey::NullType: + return encodeByte(kIDBKeyNullTypeByte); + case IDBKey::StringType: + ret = encodeByte(kIDBKeyStringTypeByte); + ret.append(encodeStringWithLength(key.string())); + return ret; + case IDBKey::DateType: + ret = encodeByte(kIDBKeyDateTypeByte); + ret.append(encodeDouble(key.date())); + ASSERT(ret.size() == 9); + return ret; + case IDBKey::NumberType: + ret = encodeByte(kIDBKeyNumberTypeByte); + ret.append(encodeDouble(key.number())); + ASSERT(ret.size() == 9); + return ret; + } + + ASSERT_NOT_REACHED(); + return Vector<char>(); +} + +static const char* decodeIDBKey(const char* p, const char* limit, RefPtr<IDBKey>& foundKey) +{ + ASSERT(limit >= p); + if (p >= limit) + return 0; + + unsigned char type = *p++; + String s; + double d; + + switch (type) { + case kIDBKeyNullTypeByte: + // Null. + foundKey = IDBKey::createNull(); + return p; + case kIDBKeyStringTypeByte: + // String. + p = decodeStringWithLength(p, limit, s); + if (!p) + return 0; + foundKey = IDBKey::createString(s); + return p; + case kIDBKeyDateTypeByte: + // Date. + p = decodeDouble(p, limit, &d); + if (!p) + return 0; + foundKey = IDBKey::createDate(d); + return p; + case kIDBKeyNumberTypeByte: + // Number. + p = decodeDouble(p, limit, &d); + if (!p) + return 0; + foundKey = IDBKey::createNumber(d); + return p; + } + + ASSERT_NOT_REACHED(); + return 0; +} + +static const char* extractEncodedIDBKey(const char* start, const char* limit, Vector<char>* result) +{ + const char* p = start; + if (p >= limit) + return 0; + + unsigned char type = *p++; + + int64_t stringLen; + + switch (type) { + case kIDBKeyNullTypeByte: + case kIDBKeyMinKeyTypeByte: + *result = encodeByte(type); + return p; + case kIDBKeyStringTypeByte: + // String. + p = decodeVarInt(p, limit, stringLen); + if (!p) + return 0; + if (p + stringLen * 2 > limit) + return 0; + result->clear(); + result->append(start, p - start + stringLen * 2); + return p + stringLen * 2; + case kIDBKeyDateTypeByte: + case kIDBKeyNumberTypeByte: + // Date or number. + if (p + sizeof(double) > limit) + return 0; + result->clear(); + result->append(start, 1 + sizeof(double)); + return p + sizeof(double); + } + ASSERT_NOT_REACHED(); + return 0; +} + +static int compareEncodedIDBKeys(const Vector<char>& keyA, const Vector<char>& keyB) +{ + ASSERT(keyA.size() >= 1); + ASSERT(keyB.size() >= 1); + + const char* p = keyA.data(); + const char* limitA = p + keyA.size(); + const char* q = keyB.data(); + const char* limitB = q + keyB.size(); + + unsigned char typeA = *p++; + unsigned char typeB = *q++; + + String s, t; + double d, e; + + if (int x = typeB - typeA) // FIXME: Note the subtleness! + return x; + + switch (typeA) { + case kIDBKeyNullTypeByte: + case kIDBKeyMinKeyTypeByte: + // Null type or max type; no payload to compare. + return 0; + case kIDBKeyStringTypeByte: + // String type. + p = decodeStringWithLength(p, limitA, s); // FIXME: Compare without actually decoding the String! + ASSERT(p); + q = decodeStringWithLength(q, limitB, t); + ASSERT(q); + return codePointCompare(s, t); + case kIDBKeyDateTypeByte: + case kIDBKeyNumberTypeByte: + // Date or number. + p = decodeDouble(p, limitA, &d); + ASSERT(p); + q = decodeDouble(q, limitB, &e); + ASSERT(q); + if (d < e) + return -1; + if (d > e) + return 1; + return 0; + } + + ASSERT_NOT_REACHED(); + return 0; +} + +static bool getInt(LevelDBDatabase* db, const Vector<char>& key, int64_t& foundInt) +{ + Vector<char> result; + if (!db->get(key, result)) + return false; + + foundInt = decodeInt(result.begin(), result.end()); + return true; +} + +static bool putInt(LevelDBDatabase* db, const Vector<char>& key, int64_t value) +{ + return db->put(key, encodeInt(value)); +} + +static bool getString(LevelDBDatabase* db, const Vector<char>& key, String& foundString) +{ + Vector<char> result; + if (!db->get(key, result)) + return false; + + foundString = decodeString(result.begin(), result.end()); + return true; +} + +static bool putString(LevelDBDatabase* db, const Vector<char> key, const String& value) +{ + if (!db->put(key, encodeString(value))) + return false; + return true; +} + +namespace { +class KeyPrefix { +public: + KeyPrefix() + : m_databaseId(kInvalidType) + , m_objectStoreId(kInvalidType) + , m_indexId(kInvalidType) + { + } + + KeyPrefix(int64_t databaseId, int64_t objectStoreId, int64_t indexId) + : m_databaseId(databaseId) + , m_objectStoreId(objectStoreId) + , m_indexId(indexId) + { + } + + static const char* decode(const char* start, const char* limit, KeyPrefix* result) + { + if (start == limit) + return 0; + + unsigned char firstByte = *start++; + + int databaseIdBytes = ((firstByte >> 5) & 0x7) + 1; + int objectStoreIdBytes = ((firstByte >> 2) & 0x7) + 1; + int indexIdBytes = (firstByte & 0x3) + 1; + + if (start + databaseIdBytes + objectStoreIdBytes + indexIdBytes > limit) + return 0; + + result->m_databaseId = decodeInt(start, start + databaseIdBytes); + start += databaseIdBytes; + result->m_objectStoreId = decodeInt(start, start + objectStoreIdBytes); + start += objectStoreIdBytes; + result->m_indexId = decodeInt(start, start + indexIdBytes); + start += indexIdBytes; + + return start; + } + + Vector<char> encode() const + { + ASSERT(m_databaseId != kInvalidId); + ASSERT(m_objectStoreId != kInvalidId); + ASSERT(m_indexId != kInvalidId); + + Vector<char> databaseIdString = encodeInt(m_databaseId); + Vector<char> objectStoreIdString = encodeInt(m_objectStoreId); + Vector<char> indexIdString = encodeInt(m_indexId); + + ASSERT(databaseIdString.size() <= 8); + ASSERT(objectStoreIdString.size() <= 8); + ASSERT(indexIdString.size() <= 4); + + + unsigned char firstByte = (databaseIdString.size() - 1) << 5 | (objectStoreIdString.size() - 1) << 2 | (indexIdString.size() - 1); + Vector<char> ret; + ret.append(firstByte); + ret.append(databaseIdString); + ret.append(objectStoreIdString); + ret.append(indexIdString); + + return ret; + } + + int compare(const KeyPrefix& other) const + { + ASSERT(m_databaseId != kInvalidId); + ASSERT(m_objectStoreId != kInvalidId); + ASSERT(m_indexId != kInvalidId); + + if (m_databaseId != other.m_databaseId) + return m_databaseId - other.m_databaseId; + if (m_objectStoreId != other.m_objectStoreId) + return m_objectStoreId - other.m_objectStoreId; + if (m_indexId != other.m_indexId) + return m_indexId - other.m_indexId; + return 0; + } + + enum Type { + kGlobalMetaData, + kDatabaseMetaData, + kObjectStoreData, + kExistsEntry, + kIndexData, + kInvalidType + }; + + Type type() const + { + ASSERT(m_databaseId != kInvalidId); + ASSERT(m_objectStoreId != kInvalidId); + ASSERT(m_indexId != kInvalidId); + + if (!m_databaseId) + return kGlobalMetaData; + if (!m_objectStoreId) + return kDatabaseMetaData; + if (m_indexId == kObjectStoreDataIndexId) + return kObjectStoreData; + if (m_indexId == kExistsEntryIndexId) + return kExistsEntry; + if (m_indexId >= kMinimumIndexId) + return kIndexData; + + ASSERT_NOT_REACHED(); + return kInvalidType; + } + + int64_t m_databaseId; + int64_t m_objectStoreId; + int64_t m_indexId; + + static const int64_t kInvalidId = -1; +}; + +class SchemaVersionKey { +public: + static Vector<char> encode() + { + KeyPrefix prefix(0, 0, 0); + Vector<char> ret = prefix.encode(); + ret.append(encodeByte(kSchemaVersionTypeByte)); + return ret; + } +}; + +class MaxDatabaseIdKey { +public: + static Vector<char> encode() + { + KeyPrefix prefix(0, 0, 0); + Vector<char> ret = prefix.encode(); + ret.append(encodeByte(kMaxDatabaseIdTypeByte)); + return ret; + } +}; + +class DatabaseFreeListKey { +public: + DatabaseFreeListKey() + : m_databaseId(-1) + { + } + + static const char* decode(const char* start, const char* limit, DatabaseFreeListKey* result) + { + KeyPrefix prefix; + const char *p = KeyPrefix::decode(start, limit, &prefix); + if (!p) + return 0; + ASSERT(!prefix.m_databaseId); + ASSERT(!prefix.m_objectStoreId); + ASSERT(!prefix.m_indexId); + if (p == limit) + return 0; + unsigned char typeByte = *p++; + ASSERT_UNUSED(typeByte, typeByte == kDatabaseFreeListTypeByte); + if (p == limit) + return 0; + return decodeVarInt(p, limit, result->m_databaseId); + } + + static Vector<char> encode(int64_t databaseId) + { + KeyPrefix prefix(0, 0, 0); + Vector<char> ret = prefix.encode(); + ret.append(encodeByte(kDatabaseFreeListTypeByte)); + ret.append(encodeVarInt(databaseId)); + return ret; + } + + int64_t databaseId() const + { + ASSERT(m_databaseId >= 0); + return m_databaseId; + } + + int compare(const DatabaseFreeListKey& other) const + { + ASSERT(m_databaseId >= 0); + return m_databaseId - other.m_databaseId; + } + +private: + int64_t m_databaseId; +}; + +class DatabaseNameKey { +public: + static const char* decode(const char* start, const char* limit, DatabaseNameKey* result) + { + KeyPrefix prefix; + const char* p = KeyPrefix::decode(start, limit, &prefix); + if (!p) + return p; + ASSERT(!prefix.m_databaseId); + ASSERT(!prefix.m_objectStoreId); + ASSERT(!prefix.m_indexId); + if (p == limit) + return 0; + unsigned char typeByte = *p++; + ASSERT_UNUSED(typeByte, typeByte == kDatabaseNameTypeByte); + if (p == limit) + return 0; + p = decodeStringWithLength(p, limit, result->m_origin); + if (!p) + return 0; + return decodeStringWithLength(p, limit, result->m_databaseName); + } + + static Vector<char> encode(const String& origin, const String& databaseName) + { + KeyPrefix prefix(0, 0, 0); + Vector<char> ret = prefix.encode(); + ret.append(encodeByte(kDatabaseNameTypeByte)); + ret.append(encodeStringWithLength(origin)); + ret.append(encodeStringWithLength(databaseName)); + return ret; + } + + String origin() const { return m_origin; } + String databaseName() const { return m_databaseName; } + + int compare(const DatabaseNameKey& other) + { + if (int x = codePointCompare(m_origin, other.m_origin)) + return x; + return codePointCompare(m_databaseName, other.m_databaseName); + } + +private: + String m_origin; // FIXME: Store encoded strings, or just pointers. + String m_databaseName; +}; + +class DatabaseMetaDataKey { +public: + enum MetaDataType { + kOriginName = 0, + kDatabaseName = 1, + kUserVersion = 2, + kMaxObjectStoreId = 3 + }; + + static Vector<char> encode(int64_t databaseId, MetaDataType metaDataType) + { + KeyPrefix prefix(databaseId, 0, 0); + Vector<char> ret = prefix.encode(); + ret.append(encodeByte(metaDataType)); + return ret; + } +}; + +class ObjectStoreMetaDataKey { +public: + ObjectStoreMetaDataKey() + : m_objectStoreId(-1) + , m_metaDataType(-1) + { + } + + static const char* decode(const char* start, const char* limit, ObjectStoreMetaDataKey* result) + { + KeyPrefix prefix; + const char* p = KeyPrefix::decode(start, limit, &prefix); + if (!p) + return 0; + ASSERT(prefix.m_databaseId); + ASSERT(!prefix.m_objectStoreId); + ASSERT(!prefix.m_indexId); + if (p == limit) + return 0; + unsigned char typeByte = *p++; + ASSERT_UNUSED(typeByte, typeByte == kObjectStoreMetaDataTypeByte); + if (p == limit) + return 0; + p = decodeVarInt(p, limit, result->m_objectStoreId); + if (!p) + return 0; + ASSERT(result->m_objectStoreId); + if (p == limit) + return 0; + return decodeVarInt(p, limit, result->m_metaDataType); + } + + static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t metaDataType) + { + KeyPrefix prefix(databaseId, 0, 0); + Vector<char> ret = prefix.encode(); + ret.append(encodeByte(kObjectStoreMetaDataTypeByte)); + ret.append(encodeVarInt(objectStoreId)); + ret.append(encodeVarInt(metaDataType)); + return ret; + } + + int64_t objectStoreId() const + { + ASSERT(m_objectStoreId >= 0); + return m_objectStoreId; + } + int64_t metaDataType() const + { + ASSERT(m_metaDataType >= 0); + return m_metaDataType; + } + + int compare(const ObjectStoreMetaDataKey& other) + { + ASSERT(m_objectStoreId >= 0); + ASSERT(m_metaDataType >= 0); + if (int x = m_objectStoreId - other.m_objectStoreId) + return x; // FIXME: Is this cast safe? I.e., will it preserve the sign? + return m_metaDataType - other.m_metaDataType; + } + +private: + int64_t m_objectStoreId; + int64_t m_metaDataType; // FIXME: Make this a byte. +}; + +class IndexMetaDataKey { +public: + IndexMetaDataKey() + : m_objectStoreId(-1) + , m_indexId(-1) + , m_metaDataType(0) + { + } + + static const char* decode(const char* start, const char* limit, IndexMetaDataKey* result) + { + KeyPrefix prefix; + const char* p = KeyPrefix::decode(start, limit, &prefix); + if (!p) + return 0; + ASSERT(prefix.m_databaseId); + ASSERT(!prefix.m_objectStoreId); + ASSERT(!prefix.m_indexId); + if (p == limit) + return 0; + unsigned char typeByte = *p++; + ASSERT_UNUSED(typeByte, typeByte == kIndexMetaDataTypeByte); + if (p == limit) + return 0; + p = decodeVarInt(p, limit, result->m_objectStoreId); + if (!p) + return 0; + p = decodeVarInt(p, limit, result->m_indexId); + if (!p) + return 0; + if (p == limit) + return 0; + result->m_metaDataType = *p++; + return p; + } + + static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, unsigned char metaDataType) + { + KeyPrefix prefix(databaseId, 0, 0); + Vector<char> ret = prefix.encode(); + ret.append(encodeByte(kIndexMetaDataTypeByte)); + ret.append(encodeVarInt(objectStoreId)); + ret.append(encodeVarInt(indexId)); + ret.append(encodeByte(metaDataType)); + return ret; + } + + int compare(const IndexMetaDataKey& other) + { + ASSERT(m_objectStoreId >= 0); + ASSERT(m_indexId >= 0); + + if (int x = m_objectStoreId - other.m_objectStoreId) + return x; + if (int x = m_indexId - other.m_indexId) + return x; + return m_metaDataType - other.m_metaDataType; + } + + int64_t indexId() const + { + ASSERT(m_indexId >= 0); + return m_indexId; + } + + unsigned char metaDataType() const { return m_metaDataType; } + +private: + int64_t m_objectStoreId; + int64_t m_indexId; + unsigned char m_metaDataType; +}; + +class ObjectStoreFreeListKey { +public: + ObjectStoreFreeListKey() + : m_objectStoreId(-1) + { + } + + static const char* decode(const char* start, const char* limit, ObjectStoreFreeListKey* result) + { + KeyPrefix prefix; + const char *p = KeyPrefix::decode(start, limit, &prefix); + if (!p) + return 0; + ASSERT(prefix.m_databaseId); + ASSERT(!prefix.m_objectStoreId); + ASSERT(!prefix.m_indexId); + if (p == limit) + return 0; + unsigned char typeByte = *p++; + ASSERT_UNUSED(typeByte, typeByte == kObjectStoreFreeListTypeByte); + if (p == limit) + return 0; + return decodeVarInt(p, limit, result->m_objectStoreId); + } + + static Vector<char> encode(int64_t databaseId, int64_t objectStoreId) + { + KeyPrefix prefix(databaseId, 0, 0); + Vector<char> ret = prefix.encode(); + ret.append(encodeByte(kObjectStoreFreeListTypeByte)); + ret.append(encodeVarInt(objectStoreId)); + return ret; + } + + int64_t objectStoreId() const + { + ASSERT(m_objectStoreId >= 0); + return m_objectStoreId; + } + + int compare(const ObjectStoreFreeListKey& other) + { + // FIXME: It may seem strange that we're not comparing database id's, + // but that comparison will have been made earlier. + // We should probably make this more clear, though... + ASSERT(m_objectStoreId >= 0); + return m_objectStoreId - other.m_objectStoreId; + } + +private: + int64_t m_objectStoreId; +}; + +class IndexFreeListKey { +public: + IndexFreeListKey() + : m_objectStoreId(-1) + , m_indexId(-1) + { + } + + static const char* decode(const char* start, const char* limit, IndexFreeListKey* result) + { + KeyPrefix prefix; + const char* p = KeyPrefix::decode(start, limit, &prefix); + if (!p) + return 0; + ASSERT(prefix.m_databaseId); + ASSERT(!prefix.m_objectStoreId); + ASSERT(!prefix.m_indexId); + if (p == limit) + return 0; + unsigned char typeByte = *p++; + ASSERT_UNUSED(typeByte, typeByte == kIndexFreeListTypeByte); + if (p == limit) + return 0; + p = decodeVarInt(p, limit, result->m_objectStoreId); + if (!p) + return 0; + return decodeVarInt(p, limit, result->m_indexId); + } + + static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId) + { + KeyPrefix prefix(databaseId, 0, 0); + Vector<char> ret = prefix.encode(); + ret.append(encodeByte(kIndexFreeListTypeByte)); + ret.append(encodeVarInt(objectStoreId)); + ret.append(encodeVarInt(indexId)); + return ret; + } + + int compare(const IndexFreeListKey& other) + { + ASSERT(m_objectStoreId >= 0); + ASSERT(m_indexId >= 0); + if (int x = m_objectStoreId - other.m_objectStoreId) + return x; + return m_indexId - other.m_indexId; + } + + int64_t objectStoreId() const + { + ASSERT(m_objectStoreId >= 0); + return m_objectStoreId; + } + + int64_t indexId() const + { + ASSERT(m_indexId >= 0); + return m_indexId; + } + +private: + int64_t m_objectStoreId; + int64_t m_indexId; +}; + +class ObjectStoreNamesKey { +public: + // FIXME: We never use this to look up object store ids, because a mapping + // is kept in the IDBDatabaseBackendImpl. Can the mapping become unreliable? + // Can we remove this? + static const char* decode(const char* start, const char* limit, ObjectStoreNamesKey* result) + { + KeyPrefix prefix; + const char* p = KeyPrefix::decode(start, limit, &prefix); + if (!p) + return 0; + ASSERT(prefix.m_databaseId); + ASSERT(!prefix.m_objectStoreId); + ASSERT(!prefix.m_indexId); + if (p == limit) + return 0; + unsigned char typeByte = *p++; + ASSERT_UNUSED(typeByte, typeByte == kObjectStoreNamesTypeByte); + return decodeStringWithLength(p, limit, result->m_objectStoreName); + } + + static Vector<char> encode(int64_t databaseId, const String& objectStoreName) + { + KeyPrefix prefix(databaseId, 0, 0); + Vector<char> ret = prefix.encode(); + ret.append(encodeByte(kSchemaVersionTypeByte)); + ret.append(encodeByte(kObjectStoreNamesTypeByte)); + ret.append(encodeStringWithLength(objectStoreName)); + return ret; + } + + int compare(const ObjectStoreNamesKey& other) + { + return codePointCompare(m_objectStoreName, other.m_objectStoreName); + } + + String objectStoreName() const { return m_objectStoreName; } + +private: + String m_objectStoreName; // FIXME: Store the encoded string, or just pointers to it. +}; + +class IndexNamesKey { +public: + IndexNamesKey() + : m_objectStoreId(-1) + { + } + + // FIXME: We never use this to look up index ids, because a mapping + // is kept at a higher level. + static const char* decode(const char* start, const char* limit, IndexNamesKey* result) + { + KeyPrefix prefix; + const char* p = KeyPrefix::decode(start, limit, &prefix); + if (!p) + return 0; + ASSERT(prefix.m_databaseId); + ASSERT(!prefix.m_objectStoreId); + ASSERT(!prefix.m_indexId); + if (p == limit) + return 0; + unsigned char typeByte = *p++; + ASSERT_UNUSED(typeByte, typeByte == kIndexNamesKeyTypeByte); + if (p == limit) + return 0; + p = decodeVarInt(p, limit, result->m_objectStoreId); + if (!p) + return 0; + return decodeStringWithLength(p, limit, result->m_indexName); + } + + static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const String& indexName) + { + KeyPrefix prefix(databaseId, 0, 0); + Vector<char> ret = prefix.encode(); + ret.append(encodeByte(kIndexNamesKeyTypeByte)); + ret.append(encodeVarInt(objectStoreId)); + ret.append(encodeStringWithLength(indexName)); + return ret; + } + + int compare(const IndexNamesKey& other) + { + ASSERT(m_objectStoreId >= 0); + if (int x = m_objectStoreId - other.m_objectStoreId) + return x; + return codePointCompare(m_indexName, other.m_indexName); + } + + String indexName() const { return m_indexName; } + +private: + int64_t m_objectStoreId; + String m_indexName; +}; + +class ObjectStoreDataKey { +public: + static const char* decode(const char* start, const char* end, ObjectStoreDataKey* result) + { + KeyPrefix prefix; + const char* p = KeyPrefix::decode(start, end, &prefix); + if (!p) + return 0; + ASSERT(prefix.m_databaseId); + ASSERT(prefix.m_objectStoreId); + ASSERT(prefix.m_indexId == kSpecialIndexNumber); + if (p == end) + return 0; + return extractEncodedIDBKey(p, end, &result->m_encodedUserKey); + } + + static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const Vector<char> encodedUserKey) + { + KeyPrefix prefix(databaseId, objectStoreId, kSpecialIndexNumber); + Vector<char> ret = prefix.encode(); + ret.append(encodedUserKey); + + return ret; + } + + static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const IDBKey& userKey) + { + return encode(databaseId, objectStoreId, encodeIDBKey(userKey)); + } + + int compare(const ObjectStoreDataKey& other) + { + return compareEncodedIDBKeys(m_encodedUserKey, other.m_encodedUserKey); + } + + PassRefPtr<IDBKey> userKey() const + { + RefPtr<IDBKey> key; + decodeIDBKey(m_encodedUserKey.begin(), m_encodedUserKey.end(), key); + return key; + } + + static const int64_t kSpecialIndexNumber = kObjectStoreDataIndexId; + +private: + Vector<char> m_encodedUserKey; +}; + +class ExistsEntryKey { +public: + static const char* decode(const char* start, const char* end, ExistsEntryKey* result) + { + KeyPrefix prefix; + const char* p = KeyPrefix::decode(start, end, &prefix); + if (!p) + return 0; + ASSERT(prefix.m_databaseId); + ASSERT(prefix.m_objectStoreId); + ASSERT(prefix.m_indexId == kSpecialIndexNumber); + if (p == end) + return 0; + return extractEncodedIDBKey(p, end, &result->m_encodedUserKey); + } + + static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const Vector<char>& encodedKey) + { + KeyPrefix prefix(databaseId, objectStoreId, kSpecialIndexNumber); + Vector<char> ret = prefix.encode(); + ret.append(encodedKey); + return ret; + } + + static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const IDBKey& userKey) + { + return encode(databaseId, objectStoreId, encodeIDBKey(userKey)); + } + + int compare(const ExistsEntryKey& other) + { + return compareEncodedIDBKeys(m_encodedUserKey, other.m_encodedUserKey); + } + + PassRefPtr<IDBKey> userKey() const + { + RefPtr<IDBKey> key; + decodeIDBKey(m_encodedUserKey.begin(), m_encodedUserKey.end(), key); + return key; + } + + static const int64_t kSpecialIndexNumber = kExistsEntryIndexId; + +private: + Vector<char> m_encodedUserKey; +}; + +class IndexDataKey { +public: + IndexDataKey() + : m_databaseId(-1) + , m_objectStoreId(-1) + , m_indexId(-1) + , m_sequenceNumber(-1) + { + } + + static const char* decode(const char* start, const char* limit, IndexDataKey* result) + { + KeyPrefix prefix; + const char* p = KeyPrefix::decode(start, limit, &prefix); + if (!p) + return 0; + ASSERT(prefix.m_databaseId); + ASSERT(prefix.m_objectStoreId); + ASSERT(prefix.m_indexId >= kMinimumIndexId); + result->m_databaseId = prefix.m_databaseId; + result->m_objectStoreId = prefix.m_objectStoreId; + result->m_indexId = prefix.m_indexId; + p = extractEncodedIDBKey(p, limit, &result->m_encodedUserKey); + if (!p) + return 0; + if (p == limit) { + result->m_sequenceNumber = -1; // FIXME: We should change it so that all keys have a sequence number. Shouldn't need to handle this case. + return p; + } + return decodeVarInt(p, limit, result->m_sequenceNumber); + } + + static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const Vector<char>& encodedUserKey, int64_t sequenceNumber) + { + KeyPrefix prefix(databaseId, objectStoreId, indexId); + Vector<char> ret = prefix.encode(); + ret.append(encodedUserKey); + ret.append(encodeVarInt(sequenceNumber)); + return ret; + } + + static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& userKey, int64_t sequenceNumber) + { + return encode(databaseId, objectStoreId, indexId, encodeIDBKey(userKey), sequenceNumber); + } + + static Vector<char> encodeMaxKey(int64_t databaseId, int64_t objectStoreId) + { + return encode(databaseId, objectStoreId, INT32_MAX, maxIDBKey(), INT64_MAX); + } + + int compare(const IndexDataKey& other, bool ignoreSequenceNumber) + { + ASSERT(m_databaseId >= 0); + ASSERT(m_objectStoreId >= 0); + ASSERT(m_indexId >= 0); + if (int x = compareEncodedIDBKeys(m_encodedUserKey, other.m_encodedUserKey)) + return x; + if (ignoreSequenceNumber) + return 0; + return m_sequenceNumber - other.m_sequenceNumber; + } + + int64_t databaseId() const + { + ASSERT(m_databaseId >= 0); + return m_databaseId; + } + + int64_t objectStoreId() const + { + ASSERT(m_objectStoreId >= 0); + return m_objectStoreId; + } + + int64_t indexId() const + { + ASSERT(m_indexId >= 0); + return m_indexId; + } + + PassRefPtr<IDBKey> userKey() const + { + RefPtr<IDBKey> key; + decodeIDBKey(m_encodedUserKey.begin(), m_encodedUserKey.end(), key); + return key; + } + +private: + int64_t m_databaseId; + int64_t m_objectStoreId; + int64_t m_indexId; + Vector<char> m_encodedUserKey; + int64_t m_sequenceNumber; +}; + +namespace { +template<typename KeyType> +int decodeAndCompare(const LevelDBSlice& a, const LevelDBSlice& b) +{ + KeyType keyA; + KeyType keyB; + + const char* ptrA = KeyType::decode(a.begin(), a.end(), &keyA); + ASSERT_UNUSED(ptrA, ptrA); + const char* ptrB = KeyType::decode(b.begin(), b.end(), &keyB); + ASSERT_UNUSED(ptrB, ptrB); + + return keyA.compare(keyB); +} +} + +static int realCompare(const LevelDBSlice& a, const LevelDBSlice& b, bool indexKeys = false) +{ + const char* ptrA = a.begin(); + const char* ptrB = b.begin(); + const char* endA = a.end(); + const char* endB = b.end(); + + KeyPrefix prefixA; + KeyPrefix prefixB; + + ptrA = KeyPrefix::decode(ptrA, endA, &prefixA); + ptrB = KeyPrefix::decode(ptrB, endB, &prefixB); + ASSERT(ptrA); + ASSERT(ptrB); + + if (int x = prefixA.compare(prefixB)) + return x; + + if (prefixA.type() == KeyPrefix::kGlobalMetaData) { + ASSERT(ptrA != endA); + ASSERT(ptrB != endB); + + unsigned char typeByteA = *ptrA++; + unsigned char typeByteB = *ptrB++; + + if (int x = typeByteA - typeByteB) + return x; + + if (typeByteA <= 1) + return 0; + if (typeByteA == kDatabaseFreeListTypeByte) + return decodeAndCompare<DatabaseFreeListKey>(a, b); + if (typeByteA == kDatabaseNameTypeByte) + return decodeAndCompare<DatabaseNameKey>(a, b); + } + + if (prefixA.type() == KeyPrefix::kDatabaseMetaData) { + ASSERT(ptrA != endA); + ASSERT(ptrB != endB); + + unsigned char typeByteA = *ptrA++; + unsigned char typeByteB = *ptrB++; + + if (int x = typeByteA - typeByteB) + return x; + + if (typeByteA <= 3) + return 0; + + if (typeByteA == kObjectStoreMetaDataTypeByte) + return decodeAndCompare<ObjectStoreMetaDataKey>(a, b); + if (typeByteA == kIndexMetaDataTypeByte) + return decodeAndCompare<IndexMetaDataKey>(a, b); + if (typeByteA == kObjectStoreFreeListTypeByte) + return decodeAndCompare<ObjectStoreFreeListKey>(a, b); + if (typeByteA == kIndexFreeListTypeByte) + return decodeAndCompare<IndexFreeListKey>(a, b); + if (typeByteA == kObjectStoreNamesTypeByte) + return decodeAndCompare<ObjectStoreNamesKey>(a, b); + if (typeByteA == kIndexNamesKeyTypeByte) + return decodeAndCompare<IndexNamesKey>(a, b); + + return 0; + ASSERT_NOT_REACHED(); + } + + if (prefixA.type() == KeyPrefix::kObjectStoreData) { + if (ptrA == endA && ptrB == endB) + return 0; + if (ptrA == endA) + return -1; + if (ptrB == endB) + return 1; // FIXME: This case of non-existing user keys should not have to be handled this way. + + return decodeAndCompare<ObjectStoreDataKey>(a, b); + } + if (prefixA.type() == KeyPrefix::kExistsEntry) { + if (ptrA == endA && ptrB == endB) + return 0; + if (ptrA == endA) + return -1; + if (ptrB == endB) + return 1; // FIXME: This case of non-existing user keys should not have to be handled this way. + + return decodeAndCompare<ExistsEntryKey>(a, b); + } + if (prefixA.type() == KeyPrefix::kIndexData) { + if (ptrA == endA && ptrB == endB) + return 0; + if (ptrA == endA) + return -1; + if (ptrB == endB) + return 1; // FIXME: This case of non-existing user keys should not have to be handled this way. + + IndexDataKey indexDataKeyA; + IndexDataKey indexDataKeyB; + + ptrA = IndexDataKey::decode(a.begin(), endA, &indexDataKeyA); + ptrB = IndexDataKey::decode(b.begin(), endB, &indexDataKeyB); + ASSERT(ptrA); + ASSERT(ptrB); + + bool ignoreSequenceNumber = indexKeys; + return indexDataKeyA.compare(indexDataKeyB, ignoreSequenceNumber); + } + + ASSERT_NOT_REACHED(); + return 0; +} + +static int compareKeys(const LevelDBSlice& a, const LevelDBSlice& b) +{ + return realCompare(a, b); +} + +static int compareIndexKeys(const LevelDBSlice& a, const LevelDBSlice& b) +{ + return realCompare(a, b, true); +} + +class Comparator : public LevelDBComparator { +public: + virtual int compare(const LevelDBSlice& a, const LevelDBSlice& b) const { return realCompare(a, b); } + virtual const char* name() const { return "idb_cmp1"; } +}; +} + +static bool setUpMetadata(LevelDBDatabase* db) +{ + const Vector<char> metaDataKey = SchemaVersionKey::encode(); + + int64_t schemaVersion; + if (!getInt(db, metaDataKey, schemaVersion)) { + schemaVersion = 0; + if (!putInt(db, metaDataKey, schemaVersion)) + return false; + } + + // FIXME: Eventually, we'll need to be able to transition between schemas. + if (schemaVersion) + return false; // Don't know what to do with this version. + + return true; +} + +IDBLevelDBBackingStore::IDBLevelDBBackingStore(String identifier, IDBFactoryBackendImpl* factory, LevelDBDatabase* db) + : m_identifier(identifier) + , m_factory(factory) + , m_db(db) +{ + m_factory->addIDBBackingStore(identifier, this); +} + +IDBLevelDBBackingStore::~IDBLevelDBBackingStore() +{ + m_factory->removeIDBBackingStore(m_identifier); +} + +PassRefPtr<IDBBackingStore> IDBLevelDBBackingStore::open(SecurityOrigin* securityOrigin, const String& pathBaseArg, int64_t maximumSize, const String& fileIdentifier, IDBFactoryBackendImpl* factory) +{ + String pathBase = pathBaseArg; + + if (pathBase.isEmpty()) { + ASSERT_NOT_REACHED(); // FIXME: We need to handle this case for incognito and DumpRenderTree. + return 0; + } + + if (!makeAllDirectories(pathBase)) { + LOG_ERROR("Unable to create IndexedDB database path %s", pathBase.utf8().data()); + return 0; + } + // FIXME: We should eventually use the same LevelDB database for all origins. + String path = pathByAppendingComponent(pathBase, securityOrigin->databaseIdentifier() + ".indexeddb.leveldb"); + + OwnPtr<LevelDBComparator> comparator(new Comparator()); + LevelDBDatabase* db = LevelDBDatabase::open(path, comparator.get()); + if (!db) + return 0; + + // FIXME: Handle comparator name changes. + + RefPtr<IDBLevelDBBackingStore> backingStore(adoptRef(new IDBLevelDBBackingStore(fileIdentifier, factory, db))); + backingStore->m_comparator = comparator.release(); + + if (!setUpMetadata(backingStore->m_db.get())) + return 0; + + return backingStore.release(); +} + +bool IDBLevelDBBackingStore::extractIDBDatabaseMetaData(const String& name, String& foundVersion, int64_t& foundId) +{ + const Vector<char> key = DatabaseNameKey::encode(m_identifier, name); + + bool ok = getInt(m_db.get(), key, foundId); + if (!ok) + return false; + + ok = getString(m_db.get(), DatabaseMetaDataKey::encode(foundId, DatabaseMetaDataKey::kUserVersion), foundVersion); + if (!ok) + return false; + + return true; +} + +static int64_t getNewDatabaseId(LevelDBDatabase* db) +{ + const Vector<char> freeListStartKey = DatabaseFreeListKey::encode(0); + const Vector<char> freeListStopKey = DatabaseFreeListKey::encode(INT64_MAX); + + OwnPtr<LevelDBIterator> it(db->newIterator()); + for (it->seek(freeListStartKey); it->isValid() && compareKeys(it->key(), freeListStopKey) < 0; it->next()) { + const char *p = it->key().begin(); + const char *limit = it->key().end(); + + DatabaseFreeListKey freeListKey; + p = DatabaseFreeListKey::decode(p, limit, &freeListKey); + ASSERT(p); + + bool ok = db->remove(it->key()); + ASSERT_UNUSED(ok, ok); + + return freeListKey.databaseId(); + } + + // If we got here, there was no free-list. + int64_t maxDatabaseId = -1; + if (!getInt(db, MaxDatabaseIdKey::encode(), maxDatabaseId)) + maxDatabaseId = 0; + + ASSERT(maxDatabaseId >= 0); + + int64_t databaseId = maxDatabaseId + 1; + bool ok = putInt(db, MaxDatabaseIdKey::encode(), databaseId); + ASSERT_UNUSED(ok, ok); + + return databaseId; + +} + +bool IDBLevelDBBackingStore::setIDBDatabaseMetaData(const String& name, const String& version, int64_t& rowId, bool invalidRowId) +{ + if (invalidRowId) { + rowId = getNewDatabaseId(m_db.get()); + + const Vector<char> key = DatabaseNameKey::encode(m_identifier, name); + if (!putInt(m_db.get(), key, rowId)) + return false; + } + + if (!putString(m_db.get(), DatabaseMetaDataKey::encode(rowId, DatabaseMetaDataKey::kUserVersion), version)) + return false; + + return true; +} + +void IDBLevelDBBackingStore::getObjectStores(int64_t databaseId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundAutoIncrementFlags) +{ + const Vector<char> startKey = ObjectStoreMetaDataKey::encode(databaseId, 1, 0); + const Vector<char> stopKey = ObjectStoreMetaDataKey::encode(databaseId, INT64_MAX, 0); + + OwnPtr<LevelDBIterator> it(m_db->newIterator()); + for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) { + const char *p = it->key().begin(); + const char *limit = it->key().end(); + + ObjectStoreMetaDataKey metaDataKey; + p = ObjectStoreMetaDataKey::decode(p, limit, &metaDataKey); + ASSERT(p); + + int64_t objectStoreId = metaDataKey.objectStoreId(); + + String objectStoreName = decodeString(it->value().begin(), it->value().end()); + + it->next(); + if (!it->isValid()) { + LOG_ERROR("Internal Indexed DB error."); + return; + } + String keyPath = decodeString(it->value().begin(), it->value().end()); + + it->next(); + if (!it->isValid()) { + LOG_ERROR("Internal Indexed DB error."); + return; + } + bool autoIncrement = *it->value().begin(); + + it->next(); // Is evicatble. + if (!it->isValid()) { + LOG_ERROR("Internal Indexed DB error."); + return; + } + + it->next(); // Last version. + if (!it->isValid()) { + LOG_ERROR("Internal Indexed DB error."); + return; + } + + it->next(); // Maxium index id allocated. + if (!it->isValid()) { + LOG_ERROR("Internal Indexed DB error."); + return; + } + + foundIds.append(objectStoreId); + foundNames.append(objectStoreName); + foundKeyPaths.append(keyPath); + foundAutoIncrementFlags.append(autoIncrement); + } +} + +static int64_t getNewObjectStoreId(LevelDBDatabase* db, int64_t databaseId) +{ + const Vector<char> freeListStartKey = ObjectStoreFreeListKey::encode(databaseId, 0); + const Vector<char> freeListStopKey = ObjectStoreFreeListKey::encode(databaseId, INT64_MAX); + + OwnPtr<LevelDBIterator> it(db->newIterator()); + for (it->seek(freeListStartKey); it->isValid() && compareKeys(it->key(), freeListStopKey) < 0; it->next()) { + const char* p = it->key().begin(); + const char* limit = it->key().end(); + + ObjectStoreFreeListKey freeListKey; + p = ObjectStoreFreeListKey::decode(p, limit, &freeListKey); + ASSERT(p); + + bool ok = db->remove(it->key()); + ASSERT_UNUSED(ok, ok); + + return freeListKey.objectStoreId(); + } + + int64_t maxObjectStoreId; + const Vector<char> maxObjectStoreIdKey = DatabaseMetaDataKey::encode(databaseId, DatabaseMetaDataKey::kMaxObjectStoreId); + if (!getInt(db, maxObjectStoreIdKey, maxObjectStoreId)) + maxObjectStoreId = 0; + + int64_t objectStoreId = maxObjectStoreId + 1; + bool ok = putInt(db, maxObjectStoreIdKey, objectStoreId); + ASSERT_UNUSED(ok, ok); + + return objectStoreId; +} + +bool IDBLevelDBBackingStore::createObjectStore(int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement, int64_t& assignedObjectStoreId) +{ + int64_t objectStoreId = getNewObjectStoreId(m_db.get(), databaseId); + + const Vector<char> nameKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 0); + const Vector<char> keyPathKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 1); + const Vector<char> autoIncrementKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 2); + const Vector<char> evictableKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 3); + const Vector<char> lastVersionKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 4); + const Vector<char> maxIndexIdKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 5); + const Vector<char> namesKey = ObjectStoreNamesKey::encode(databaseId, name); + + bool ok = putString(m_db.get(), nameKey, name); + if (!ok) { + LOG_ERROR("Internal Indexed DB error."); + return false; + } + + ok = putString(m_db.get(), keyPathKey, keyPath); + if (!ok) { + LOG_ERROR("Internal Indexed DB error."); + return false; + } + + ok = putInt(m_db.get(), autoIncrementKey, autoIncrement); + if (!ok) { + LOG_ERROR("Internal Indexed DB error."); + return false; + } + + ok = putInt(m_db.get(), evictableKey, false); + if (!ok) { + LOG_ERROR("Internal Indexed DB error."); + return false; + } + + ok = putInt(m_db.get(), lastVersionKey, 1); + if (!ok) { + LOG_ERROR("Internal Indexed DB error."); + return false; + } + + ok = putInt(m_db.get(), maxIndexIdKey, kMinimumIndexId); + if (!ok) { + LOG_ERROR("Internal Indexed DB error."); + return false; + } + + ok = putInt(m_db.get(), namesKey, objectStoreId); + if (!ok) { + LOG_ERROR("Internal Indexed DB error."); + return false; + } + + assignedObjectStoreId = objectStoreId; + + return true; +} + +static bool deleteRange(LevelDBDatabase* db, const Vector<char>& begin, const Vector<char>& end) +{ + // FIXME: LevelDB may be able to provide a bulk operation that we can do first. + OwnPtr<LevelDBIterator> it(db->newIterator()); + for (it->seek(begin); it->isValid() && compareKeys(it->key(), end) < 0; it->next()) { + if (!db->remove(it->key())) + return false; + } + + return true; +} + +void IDBLevelDBBackingStore::deleteObjectStore(int64_t databaseId, int64_t objectStoreId) +{ + String objectStoreName; + getString(m_db.get(), ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 0), objectStoreName); + + if (!deleteRange(m_db.get(), ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 0), ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 6))) + return; // FIXME: Report error. + + putString(m_db.get(), ObjectStoreFreeListKey::encode(databaseId, objectStoreId), ""); + m_db->remove(ObjectStoreNamesKey::encode(databaseId, objectStoreName)); + + if (!deleteRange(m_db.get(), IndexFreeListKey::encode(databaseId, objectStoreId, 0), IndexFreeListKey::encode(databaseId, objectStoreId, INT64_MAX))) + return; // FIXME: Report error. + if (!deleteRange(m_db.get(), IndexMetaDataKey::encode(databaseId, objectStoreId, 0, 0), IndexMetaDataKey::encode(databaseId, objectStoreId, INT64_MAX, 0))) + return; // FIXME: Report error. + + clearObjectStore(databaseId, objectStoreId); +} + +String IDBLevelDBBackingStore::getObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey& key) +{ + const Vector<char> leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key); + Vector<char> data; + + if (!m_db->get(leveldbKey, data)) + return String(); + + int64_t version; + const char* p = decodeVarInt(data.begin(), data.end(), version); + if (!p) + return String(); + (void) version; + + return decodeString(p, data.end()); +} + +namespace { +class LevelDBRecordIdentifier : public IDBBackingStore::ObjectStoreRecordIdentifier { +public: + static PassRefPtr<LevelDBRecordIdentifier> create(const Vector<char>& primaryKey, int64_t version) { return adoptRef(new LevelDBRecordIdentifier(primaryKey, version)); } + static PassRefPtr<LevelDBRecordIdentifier> create() { return adoptRef(new LevelDBRecordIdentifier()); } + + virtual bool isValid() const { return m_primaryKey.isEmpty(); } + Vector<char> primaryKey() const { return m_primaryKey; } + void setPrimaryKey(const Vector<char>& primaryKey) { m_primaryKey = primaryKey; } + int64_t version() const { return m_version; } + void setVersion(int64_t version) { m_version = version; } + +private: + LevelDBRecordIdentifier(const Vector<char>& primaryKey, int64_t version) : m_primaryKey(primaryKey), m_version(version) { ASSERT(!primaryKey.isEmpty()); } + LevelDBRecordIdentifier() : m_primaryKey(), m_version(-1) {} + + Vector<char> m_primaryKey; // FIXME: Make it more clear that this is the *encoded* version of the key. + int64_t m_version; +}; +} + +static int64_t getNewVersionNumber(LevelDBDatabase* db, int64_t databaseId, int64_t objectStoreId) +{ + const Vector<char> lastVersionKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 4); + + int64_t lastVersion = -1; + if (!getInt(db, lastVersionKey, lastVersion)) + lastVersion = 0; + + ASSERT(lastVersion >= 0); + + int64_t version = lastVersion + 1; + bool ok = putInt(db, lastVersionKey, version); + ASSERT_UNUSED(ok, ok); + + ASSERT(version > lastVersion); // FIXME: Think about how we want to handle the overflow scenario. + + return version; +} + +bool IDBLevelDBBackingStore::putObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey& key, const String& value, ObjectStoreRecordIdentifier* recordIdentifier) +{ + int64_t version = getNewVersionNumber(m_db.get(), databaseId, objectStoreId); + const Vector<char> objectStoredataKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key); + + Vector<char> v; + v.append(encodeVarInt(version)); + v.append(encodeString(value)); + + if (!m_db->put(objectStoredataKey, v)) + return false; + + const Vector<char> existsEntryKey = ExistsEntryKey::encode(databaseId, objectStoreId, key); + if (!m_db->put(existsEntryKey, encodeInt(version))) + return false; + + LevelDBRecordIdentifier* levelDBRecordIdentifier = static_cast<LevelDBRecordIdentifier*>(recordIdentifier); + levelDBRecordIdentifier->setPrimaryKey(encodeIDBKey(key)); + levelDBRecordIdentifier->setVersion(version); + return true; +} + +void IDBLevelDBBackingStore::clearObjectStore(int64_t databaseId, int64_t objectStoreId) +{ + const Vector<char> startKey = KeyPrefix(databaseId, objectStoreId, 0).encode(); + const Vector<char> stopKey = KeyPrefix(databaseId, objectStoreId + 1, 0).encode(); + + deleteRange(m_db.get(), startKey, stopKey); +} + +PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> IDBLevelDBBackingStore::createInvalidRecordIdentifier() +{ + return LevelDBRecordIdentifier::create(); +} + +void IDBLevelDBBackingStore::deleteObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const ObjectStoreRecordIdentifier* recordIdentifier) +{ + const LevelDBRecordIdentifier* levelDBRecordIdentifier = static_cast<const LevelDBRecordIdentifier*>(recordIdentifier); + const Vector<char> key = ObjectStoreDataKey::encode(databaseId, objectStoreId, levelDBRecordIdentifier->primaryKey()); + m_db->remove(key); +} + +double IDBLevelDBBackingStore::nextAutoIncrementNumber(int64_t databaseId, int64_t objectStoreId) +{ + const Vector<char> startKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, minIDBKey()); + const Vector<char> stopKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, maxIDBKey()); + + OwnPtr<LevelDBIterator> it(m_db->newIterator()); + + int maxNumericKey = 0; + + // FIXME: Be more efficient: seek to something after the object store data, then reverse. + + for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) { + const char *p = it->key().begin(); + const char *limit = it->key().end(); + + ObjectStoreDataKey dataKey; + p = ObjectStoreDataKey::decode(p, limit, &dataKey); + ASSERT(p); + + if (dataKey.userKey()->type() == IDBKey::NumberType) { + int64_t n = static_cast<int64_t>(dataKey.userKey()->number()); + if (n > maxNumericKey) + maxNumericKey = n; + } + } + + return maxNumericKey + 1; +} + +bool IDBLevelDBBackingStore::keyExistsInObjectStore(int64_t databaseId, int64_t objectStoreId, const IDBKey& key, ObjectStoreRecordIdentifier* foundRecordIdentifier) +{ + const Vector<char> leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key); + Vector<char> data; + + if (!m_db->get(leveldbKey, data)) + return false; + + int64_t version; + if (!decodeVarInt(data.begin(), data.end(), version)) + return false; + + LevelDBRecordIdentifier* levelDBRecordIdentifier = static_cast<LevelDBRecordIdentifier*>(foundRecordIdentifier); + levelDBRecordIdentifier->setPrimaryKey(encodeIDBKey(key)); + levelDBRecordIdentifier->setVersion(version); + return true; +} + +bool IDBLevelDBBackingStore::forEachObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, ObjectStoreRecordCallback& callback) +{ + const Vector<char> startKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, minIDBKey()); + const Vector<char> stopKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, maxIDBKey()); + + OwnPtr<LevelDBIterator> it(m_db->newIterator()); + for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) { + const char *p = it->key().begin(); + const char *limit = it->key().end(); + ObjectStoreDataKey dataKey; + p = ObjectStoreDataKey::decode(p, limit, &dataKey); + ASSERT(p); + + RefPtr<IDBKey> primaryKey = dataKey.userKey(); + + int64_t version; + const char* q = decodeVarInt(it->value().begin(), it->value().end(), version); + if (!q) + return false; + + RefPtr<LevelDBRecordIdentifier> ri = LevelDBRecordIdentifier::create(encodeIDBKey(*primaryKey), version); + String idbValue = decodeString(q, it->value().end()); + + callback.callback(ri.get(), idbValue); + } + + return true; +} + +void IDBLevelDBBackingStore::getIndexes(int64_t databaseId, int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags) +{ + const Vector<char> startKey = IndexMetaDataKey::encode(databaseId, objectStoreId, 0, 0); + const Vector<char> stopKey = IndexMetaDataKey::encode(databaseId, objectStoreId + 1, 0, 0); + + OwnPtr<LevelDBIterator> it(m_db->newIterator()); + for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) { + const char* p = it->key().begin(); + const char* limit = it->key().end(); + + IndexMetaDataKey metaDataKey; + p = IndexMetaDataKey::decode(p, limit, &metaDataKey); + ASSERT(p); + + int64_t indexId = metaDataKey.indexId(); + ASSERT(!metaDataKey.metaDataType()); + + String indexName = decodeString(it->value().begin(), it->value().end()); + it->next(); + if (!it->isValid()) { + LOG_ERROR("Internal Indexed DB error."); + return; + } + + bool indexUnique = *it->value().begin(); + it->next(); + if (!it->isValid()) { + LOG_ERROR("Internal Indexed DB error."); + return; + } + + String keyPath = decodeString(it->value().begin(), it->value().end()); + + foundIds.append(indexId); + foundNames.append(indexName); + foundKeyPaths.append(keyPath); + foundUniqueFlags.append(indexUnique); + } +} + +static int64_t getNewIndexId(LevelDBDatabase* db, int64_t databaseId, int64_t objectStoreId) +{ + const Vector<char> startKey = IndexFreeListKey::encode(databaseId, objectStoreId, 0); + const Vector<char> stopKey = IndexFreeListKey::encode(databaseId, objectStoreId, INT64_MAX); + + OwnPtr<LevelDBIterator> it(db->newIterator()); + for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) { + const char* p = it->key().begin(); + const char* limit = it->key().end(); + + IndexFreeListKey freeListKey; + p = IndexFreeListKey::decode(p, limit, &freeListKey); + ASSERT(p); + + bool ok = db->remove(it->key()); + ASSERT_UNUSED(ok, ok); + + ASSERT(freeListKey.indexId() >= kMinimumIndexId); + return freeListKey.indexId(); + } + + int64_t maxIndexId; + const Vector<char> maxIndexIdKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 5); + if (!getInt(db, maxIndexIdKey, maxIndexId)) + maxIndexId = kMinimumIndexId; + + int64_t indexId = maxIndexId + 1; + bool ok = putInt(db, maxIndexIdKey, indexId); + if (!ok) + return false; + + return indexId; +} + +bool IDBLevelDBBackingStore::createIndex(int64_t databaseId, int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId) +{ + indexId = getNewIndexId(m_db.get(), databaseId, objectStoreId); + + const Vector<char> nameKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, 0); + const Vector<char> uniqueKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, 1); + const Vector<char> keyPathKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, 2); + + bool ok = putString(m_db.get(), nameKey, name); + if (!ok) { + LOG_ERROR("Internal Indexed DB error."); + return false; + } + + ok = putInt(m_db.get(), uniqueKey, isUnique); + if (!ok) { + LOG_ERROR("Internal Indexed DB error."); + return false; + } + + ok = putString(m_db.get(), keyPathKey, keyPath); + if (!ok) { + LOG_ERROR("Internal Indexed DB error."); + return false; + } + + return true; +} + +void IDBLevelDBBackingStore::deleteIndex(int64_t, int64_t, int64_t) +{ + ASSERT_NOT_REACHED(); // FIXME: Implement and add layout test. + return; +} + +bool IDBLevelDBBackingStore::putIndexDataForRecord(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, const ObjectStoreRecordIdentifier* recordIdentifier) +{ + ASSERT(indexId >= kMinimumIndexId); + const LevelDBRecordIdentifier* levelDBRecordIdentifier = static_cast<const LevelDBRecordIdentifier*>(recordIdentifier); + + const int64_t globalSequenceNumber = getNewVersionNumber(m_db.get(), databaseId, objectStoreId); + const Vector<char> indexDataKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, key, globalSequenceNumber); + + Vector<char> data; + data.append(encodeVarInt(levelDBRecordIdentifier->version())); + data.append(levelDBRecordIdentifier->primaryKey()); + + return m_db->put(indexDataKey, data); +} + +static bool findGreatestKeyLessThan(LevelDBDatabase* db, const Vector<char>& target, Vector<char>& foundKey) +{ + OwnPtr<LevelDBIterator> it(db->newIterator()); + it->seek(target); + + if (!it->isValid()) { + it->seekToLast(); + if (!it->isValid()) + return false; + } + + while (compareIndexKeys(it->key(), target) >= 0) { + it->prev(); + if (!it->isValid()) + return false; + } + + foundKey.clear(); + foundKey.append(it->key().begin(), it->key().end() - it->key().begin()); + return true; +} + +bool IDBLevelDBBackingStore::deleteIndexDataForRecord(int64_t, int64_t, int64_t, const ObjectStoreRecordIdentifier*) +{ + // FIXME: This isn't needed since we invalidate index data via the version number mechanism. + return true; +} + +String IDBLevelDBBackingStore::getObjectViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key) +{ + RefPtr<IDBKey> primaryKey = getPrimaryKeyViaIndex(databaseId, objectStoreId, indexId, key); + if (!primaryKey) + return String(); + + return getObjectStoreRecord(databaseId, objectStoreId, *primaryKey); +} + +static bool versionExists(LevelDBDatabase* db, int64_t databaseId, int64_t objectStoreId, int64_t version, const Vector<char>& encodedPrimaryKey) +{ + const Vector<char> key = ExistsEntryKey::encode(databaseId, objectStoreId, encodedPrimaryKey); + Vector<char> data; + + if (!db->get(key, data)) + return false; + + return decodeInt(data.begin(), data.end()) == version; +} + +PassRefPtr<IDBKey> IDBLevelDBBackingStore::getPrimaryKeyViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key) +{ + const Vector<char> leveldbKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, key, 0); + OwnPtr<LevelDBIterator> it(m_db->newIterator()); + it->seek(leveldbKey); + + for (;;) { + if (!it->isValid()) + return 0; + if (compareIndexKeys(it->key(), leveldbKey) > 0) + return 0; + + int64_t version; + const char* p = decodeVarInt(it->value().begin(), it->value().end(), version); + if (!p) + return 0; + Vector<char> encodedPrimaryKey; + encodedPrimaryKey.append(p, it->value().end() - p); + + if (!versionExists(m_db.get(), databaseId, objectStoreId, version, encodedPrimaryKey)) { + // Delete stale index data entry and continue. + m_db->remove(it->key()); + it->next(); + continue; + } + + RefPtr<IDBKey> primaryKey; + decodeIDBKey(encodedPrimaryKey.begin(), encodedPrimaryKey.end(), primaryKey); + return primaryKey.release(); + } +} + +bool IDBLevelDBBackingStore::keyExistsInIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key) +{ + const Vector<char> levelDBKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, key, 0); + OwnPtr<LevelDBIterator> it(m_db->newIterator()); + + bool found = false; + + it->seek(levelDBKey); + if (it->isValid() && !compareIndexKeys(it->key(), levelDBKey)) + found = true; + + return found; +} + +namespace { +class CursorImplCommon : public IDBBackingStore::Cursor { +public: + // IDBBackingStore::Cursor + virtual bool continueFunction(const IDBKey*); + virtual PassRefPtr<IDBKey> key() { return m_currentKey; } + virtual PassRefPtr<IDBKey> primaryKey() { return m_currentKey; } + virtual String value() = 0; + virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() = 0; // FIXME: I don't think this is actually used, so drop it. + virtual int64_t indexDataId() = 0; + + virtual bool loadCurrentRow() = 0; + bool firstSeek(); + +protected: + CursorImplCommon(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward) + : m_db(db) + , m_lowKey(lowKey) + , m_lowOpen(lowOpen) + , m_highKey(highKey) + , m_highOpen(highOpen) + , m_forward(forward) + { + } + virtual ~CursorImplCommon() {} + + LevelDBDatabase* m_db; + OwnPtr<LevelDBIterator> m_iterator; + Vector<char> m_lowKey; + bool m_lowOpen; + Vector<char> m_highKey; + bool m_highOpen; + bool m_forward; + RefPtr<IDBKey> m_currentKey; +}; + +bool CursorImplCommon::firstSeek() +{ + m_iterator = m_db->newIterator(); + + if (m_forward) + m_iterator->seek(m_lowKey); + else + m_iterator->seek(m_highKey); + + for (;;) { + if (!m_iterator->isValid()) + return false; + + if (m_forward && m_highOpen && compareIndexKeys(m_iterator->key(), m_highKey) >= 0) + return false; + if (m_forward && !m_highOpen && compareIndexKeys(m_iterator->key(), m_highKey) > 0) + return false; + if (!m_forward && m_lowOpen && compareIndexKeys(m_iterator->key(), m_lowKey) <= 0) + return false; + if (!m_forward && !m_lowOpen && compareIndexKeys(m_iterator->key(), m_lowKey) < 0) + return false; + + if (m_forward && m_lowOpen) { + // lowKey not included in the range. + if (compareIndexKeys(m_iterator->key(), m_lowKey) <= 0) { + m_iterator->next(); + continue; + } + } + if (!m_forward && m_highOpen) { + // highKey not included in the range. + if (compareIndexKeys(m_iterator->key(), m_highKey) >= 0) { + m_iterator->prev(); + continue; + } + } + + if (!loadCurrentRow()) { + if (m_forward) + m_iterator->next(); + else + m_iterator->prev(); + continue; + } + + return true; + } +} + +bool CursorImplCommon::continueFunction(const IDBKey* key) +{ + // FIXME: This shares a lot of code with firstSeek. + + for (;;) { + if (m_forward) + m_iterator->next(); + else + m_iterator->prev(); + + if (!m_iterator->isValid()) + return false; + + Vector<char> trash; + if (!m_db->get(m_iterator->key(), trash)) + continue; + + if (m_forward && m_highOpen && compareIndexKeys(m_iterator->key(), m_highKey) >= 0) // high key not included in range + return false; + if (m_forward && !m_highOpen && compareIndexKeys(m_iterator->key(), m_highKey) > 0) + return false; + if (!m_forward && m_lowOpen && compareIndexKeys(m_iterator->key(), m_lowKey) <= 0) // low key not included in range + return false; + if (!m_forward && !m_lowOpen && compareIndexKeys(m_iterator->key(), m_lowKey) < 0) + return false; + + if (!loadCurrentRow()) + continue; + + if (key) { + if (m_forward) { + if (m_currentKey->isLessThan(key)) + continue; + } else { + if (key->isLessThan(m_currentKey.get())) + continue; + } + } + + // FIXME: Obey the uniqueness constraint (and test for it!) + + break; + } + + return true; +} + +class ObjectStoreCursorImpl : public CursorImplCommon { +public: + static PassRefPtr<ObjectStoreCursorImpl> create(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward) + { + return adoptRef(new ObjectStoreCursorImpl(db, lowKey, lowOpen, highKey, highOpen, forward)); + } + + // CursorImplCommon + virtual String value() { return m_currentValue; } + virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() { ASSERT_NOT_REACHED(); return 0; } + virtual int64_t indexDataId() { ASSERT_NOT_REACHED(); return 0; } + virtual bool loadCurrentRow(); + +private: + ObjectStoreCursorImpl(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward) + : CursorImplCommon(db, lowKey, lowOpen, highKey, highOpen, forward) + { + } + + String m_currentValue; +}; + +bool ObjectStoreCursorImpl::loadCurrentRow() +{ + const char* p = m_iterator->key().begin(); + const char* keyLimit = m_iterator->key().end(); + + ObjectStoreDataKey objectStoreDataKey; + p = ObjectStoreDataKey::decode(p, keyLimit, &objectStoreDataKey); + ASSERT(p); + if (!p) + return false; + + m_currentKey = objectStoreDataKey.userKey(); + + int64_t version; + const char* q = decodeVarInt(m_iterator->value().begin(), m_iterator->value().end(), version); + ASSERT(q); + if (!q) + return false; + (void) version; + + m_currentValue = decodeString(q, m_iterator->value().end()); + + return true; +} + +class IndexKeyCursorImpl : public CursorImplCommon { +public: + static PassRefPtr<IndexKeyCursorImpl> create(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward) + { + return adoptRef(new IndexKeyCursorImpl(db, lowKey, lowOpen, highKey, highOpen, forward)); + } + + // CursorImplCommon + virtual String value() { ASSERT_NOT_REACHED(); return String(); } + virtual PassRefPtr<IDBKey> primaryKey() { return m_primaryKey; } + virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() { ASSERT_NOT_REACHED(); return 0; } + virtual int64_t indexDataId() { ASSERT_NOT_REACHED(); return 0; } + virtual bool loadCurrentRow(); + +private: + IndexKeyCursorImpl(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward) + : CursorImplCommon(db, lowKey, lowOpen, highKey, highOpen, forward) + { + } + + RefPtr<IDBKey> m_primaryKey; +}; + +bool IndexKeyCursorImpl::loadCurrentRow() +{ + const char* p = m_iterator->key().begin(); + const char* keyLimit = m_iterator->key().end(); + IndexDataKey indexDataKey; + p = IndexDataKey::decode(p, keyLimit, &indexDataKey); + + m_currentKey = indexDataKey.userKey(); + + int64_t indexDataVersion; + const char* q = decodeVarInt(m_iterator->value().begin(), m_iterator->value().end(), indexDataVersion); + ASSERT(q); + if (!q) + return false; + + q = decodeIDBKey(q, m_iterator->value().end(), m_primaryKey); + ASSERT(q); + if (!q) + return false; + + Vector<char> primaryLevelDBKey = ObjectStoreDataKey::encode(indexDataKey.databaseId(), indexDataKey.objectStoreId(), *m_primaryKey); + + Vector<char> result; + if (!m_db->get(primaryLevelDBKey, result)) + return false; + + int64_t objectStoreDataVersion; + const char* t = decodeVarInt(result.begin(), result.end(), objectStoreDataVersion); + ASSERT(t); + if (!t) + return false; + + if (objectStoreDataVersion != indexDataVersion) { // FIXME: This is probably not very well covered by the layout tests. + m_db->remove(m_iterator->key()); + return false; + } + + return true; +} + +class IndexCursorImpl : public CursorImplCommon { +public: + static PassRefPtr<IndexCursorImpl> create(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward) + { + return adoptRef(new IndexCursorImpl(db, lowKey, lowOpen, highKey, highOpen, forward)); + } + + // CursorImplCommon + virtual String value() { return m_value; } + virtual PassRefPtr<IDBKey> primaryKey() { return m_primaryKey; } + virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() { ASSERT_NOT_REACHED(); return 0; } + virtual int64_t indexDataId() { ASSERT_NOT_REACHED(); return 0; } + bool loadCurrentRow(); + +private: + IndexCursorImpl(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward) + : CursorImplCommon(db, lowKey, lowOpen, highKey, highOpen, forward) + { + } + + RefPtr<IDBKey> m_primaryKey; + String m_value; + Vector<char> m_primaryLevelDBKey; +}; + +bool IndexCursorImpl::loadCurrentRow() +{ + const char *p = m_iterator->key().begin(); + const char *limit = m_iterator->key().end(); + + IndexDataKey indexDataKey; + p = IndexDataKey::decode(p, limit, &indexDataKey); + + m_currentKey = indexDataKey.userKey(); + + const char *q = m_iterator->value().begin(); + const char *valueLimit = m_iterator->value().end(); + + int64_t indexDataVersion; + q = decodeVarInt(q, valueLimit, indexDataVersion); + ASSERT(q); + if (!q) + return false; + q = decodeIDBKey(q, valueLimit, m_primaryKey); + ASSERT(q); + if (!q) + return false; + + m_primaryLevelDBKey = ObjectStoreDataKey::encode(indexDataKey.databaseId(), indexDataKey.objectStoreId(), *m_primaryKey); + + Vector<char> result; + if (!m_db->get(m_primaryLevelDBKey, result)) + return false; + + int64_t objectStoreDataVersion; + const char* t = decodeVarInt(result.begin(), result.end(), objectStoreDataVersion); + ASSERT(t); + if (!t) + return false; + + if (objectStoreDataVersion != indexDataVersion) { + m_db->remove(m_iterator->key()); + return false; + } + + m_value = decodeString(t, result.end()); + return true; +} + +} + +static bool findLastIndexKeyEqualTo(LevelDBDatabase* db, const Vector<char>& target, Vector<char>& foundKey) +{ + OwnPtr<LevelDBIterator> it(db->newIterator()); + it->seek(target); + + if (!it->isValid()) + return false; + + while (it->isValid() && !compareIndexKeys(it->key(), target)) { + foundKey.clear(); + foundKey.append(it->key().begin(), it->key().end() - it->key().begin()); + it->next(); + } + + return true; +} + +PassRefPtr<IDBBackingStore::Cursor> IDBLevelDBBackingStore::openObjectStoreCursor(int64_t databaseId, int64_t objectStoreId, const IDBKeyRange* range, IDBCursor::Direction direction) +{ + bool lowerBound = range && range->lower(); + bool upperBound = range && range->upper(); + bool forward = (direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::NEXT); + + bool lowerOpen, upperOpen; + Vector<char> startKey, stopKey; + + if (!lowerBound) { + startKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, minIDBKey()); + lowerOpen = true; // Not included. + } else { + startKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, *range->lower()); + lowerOpen = range->lowerOpen(); + } + + if (!upperBound) { + stopKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, maxIDBKey()); + upperOpen = true; // Not included. + + if (!forward) { // We need a key that exists. + if (!findGreatestKeyLessThan(m_db.get(), stopKey, stopKey)) + return 0; + upperOpen = false; + } + } else { + stopKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, *range->upper()); + upperOpen = range->upperOpen(); + } + + RefPtr<ObjectStoreCursorImpl> cursor = ObjectStoreCursorImpl::create(m_db.get(), startKey, lowerOpen, stopKey, upperOpen, forward); + if (!cursor->firstSeek()) + return 0; + + return cursor.release(); +} + +PassRefPtr<IDBBackingStore::Cursor> IDBLevelDBBackingStore::openIndexKeyCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction) +{ + bool lowerBound = range && range->lower(); + bool upperBound = range && range->upper(); + bool forward = (direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::NEXT); + + bool lowerOpen, upperOpen; + Vector<char> startKey, stopKey; + + if (!lowerBound) { + startKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, minIDBKey(), 0); + lowerOpen = false; // Included. + } else { + startKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->lower(), 0); + lowerOpen = range->lowerOpen(); + } + + if (!upperBound) { + stopKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, maxIDBKey(), 0); + upperOpen = false; // Included. + + if (!forward) { // We need a key that exists. + if (!findGreatestKeyLessThan(m_db.get(), stopKey, stopKey)) + return 0; + upperOpen = false; + } + } else { + stopKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->upper(), 0); + if (!findLastIndexKeyEqualTo(m_db.get(), stopKey, stopKey)) // Seek to the *last* key in the set of non-unique keys. + return 0; + upperOpen = range->upperOpen(); + } + + RefPtr<IndexKeyCursorImpl> cursor = IndexKeyCursorImpl::create(m_db.get(), startKey, lowerOpen, stopKey, upperOpen, forward); + if (!cursor->firstSeek()) + return 0; + + return cursor.release(); +} + +PassRefPtr<IDBBackingStore::Cursor> IDBLevelDBBackingStore::openIndexCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction) +{ + bool lowerBound = range && range->lower(); + bool upperBound = range && range->upper(); + bool forward = (direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::NEXT); + + bool lowerOpen, upperOpen; + Vector<char> startKey, stopKey; + + if (!lowerBound) { + startKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, minIDBKey(), 0); + lowerOpen = false; // Included. + } else { + startKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->lower(), 0); + lowerOpen = range->lowerOpen(); + } + + if (!upperBound) { + stopKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, maxIDBKey(), 0); + upperOpen = false; // Included. + + if (!forward) { // We need a key that exists. + if (!findGreatestKeyLessThan(m_db.get(), stopKey, stopKey)) + return 0; + upperOpen = false; + } + } else { + stopKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->upper(), 0); + if (!findLastIndexKeyEqualTo(m_db.get(), stopKey, stopKey)) // Seek to the *last* key in the set of non-unique keys. + return 0; + upperOpen = range->upperOpen(); + } + + RefPtr<IndexCursorImpl> cursor = IndexCursorImpl::create(m_db.get(), startKey, lowerOpen, stopKey, upperOpen, forward); + if (!cursor->firstSeek()) + return 0; + + return cursor.release(); +} + +namespace { +class DummyTransaction : public IDBBackingStore::Transaction { +public: + virtual void begin() {} + virtual void commit() {} + virtual void rollback() {} +}; +} + +PassRefPtr<IDBBackingStore::Transaction> IDBLevelDBBackingStore::createTransaction() +{ + // FIXME: We need to implement a transaction abstraction that allows for roll-backs, and write tests for it. + return adoptRef(new DummyTransaction()); +} + +// FIXME: deleteDatabase should be part of IDBBackingStore. + +} // namespace WebCore + +#endif // ENABLE(LEVELDB) +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBLevelDBBackingStore.h b/Source/WebCore/storage/IDBLevelDBBackingStore.h new file mode 100644 index 0000000..a8647da --- /dev/null +++ b/Source/WebCore/storage/IDBLevelDBBackingStore.h @@ -0,0 +1,91 @@ +/* + * 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. + */ + +#ifndef IDBLevelDBBackingStore_h +#define IDBLevelDBBackingStore_h + +#if ENABLE(INDEXED_DATABASE) +#if ENABLE(LEVELDB) + +#include "IDBBackingStore.h" +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class LevelDBComparator; +class LevelDBDatabase; + +class IDBLevelDBBackingStore : public IDBBackingStore { +public: + static PassRefPtr<IDBBackingStore> open(SecurityOrigin*, const String& pathBase, int64_t maximumSize, const String& fileIdentifier, IDBFactoryBackendImpl*); + virtual ~IDBLevelDBBackingStore(); + + virtual bool extractIDBDatabaseMetaData(const String& name, String& foundVersion, int64_t& foundId); + virtual bool setIDBDatabaseMetaData(const String& name, const String& version, int64_t& rowId, bool invalidRowId); + + virtual void getObjectStores(int64_t databaseId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundAutoIncrementFlags); + virtual bool createObjectStore(int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement, int64_t& assignedObjectStoreId); + virtual void deleteObjectStore(int64_t databaseId, int64_t objectStoreId); + virtual PassRefPtr<ObjectStoreRecordIdentifier> createInvalidRecordIdentifier(); + virtual String getObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey&); + virtual bool putObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey&, const String& value, ObjectStoreRecordIdentifier*); + virtual void clearObjectStore(int64_t databaseId, int64_t objectStoreId); + virtual void deleteObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const ObjectStoreRecordIdentifier*); + virtual double nextAutoIncrementNumber(int64_t databaseId, int64_t objectStoreId); + virtual bool keyExistsInObjectStore(int64_t databaseId, int64_t objectStoreId, const IDBKey&, ObjectStoreRecordIdentifier* foundRecordIdentifier); + + virtual bool forEachObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, ObjectStoreRecordCallback&); + + virtual void getIndexes(int64_t databaseId, int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags); + virtual bool createIndex(int64_t databaseId, int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId); + virtual void deleteIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId); + virtual bool putIndexDataForRecord(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, const ObjectStoreRecordIdentifier*); + virtual bool deleteIndexDataForRecord(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const ObjectStoreRecordIdentifier*); + virtual String getObjectViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&); + virtual PassRefPtr<IDBKey> getPrimaryKeyViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&); + virtual bool keyExistsInIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&); + + virtual PassRefPtr<Cursor> openObjectStoreCursor(int64_t databaseId, int64_t objectStoreId, const IDBKeyRange*, IDBCursor::Direction); + virtual PassRefPtr<Cursor> openIndexKeyCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IDBCursor::Direction); + virtual PassRefPtr<Cursor> openIndexCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IDBCursor::Direction); + + virtual PassRefPtr<Transaction> createTransaction(); + +private: + IDBLevelDBBackingStore(String identifier, IDBFactoryBackendImpl*, LevelDBDatabase*); + + String m_identifier; + RefPtr<IDBFactoryBackendImpl> m_factory; + OwnPtr<LevelDBDatabase> m_db; + OwnPtr<LevelDBComparator> m_comparator; +}; + +} // namespace WebCore + + +#endif // ENABLE(LEVELDB) +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBLevelDBBackingStore_h diff --git a/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp b/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp index 0433ed7..cb6b0da 100644 --- a/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp +++ b/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * 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 @@ -50,8 +50,9 @@ IDBObjectStoreBackendImpl::~IDBObjectStoreBackendImpl() { } -IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBBackingStore* backingStore, int64_t id, const String& name, const String& keyPath, bool autoIncrement) +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) @@ -61,8 +62,9 @@ IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBBackingStore* backingSto loadIndexes(); } -IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBBackingStore* backingStore, const String& name, const String& keyPath, bool autoIncrement) +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) @@ -90,7 +92,7 @@ void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCal void IDBObjectStoreBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks) { - String wireData = objectStore->m_backingStore->getObjectStoreRecord(objectStore->id(), *key); + String wireData = objectStore->m_backingStore->getObjectStoreRecord(objectStore->m_databaseId, objectStore->id(), *key); if (wireData.isNull()) { callbacks->onSuccess(SerializedScriptValue::undefinedValue()); return; @@ -227,8 +229,8 @@ void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr< indexKeys.append(key.release()); } - int64_t dataRowId = InvalidId; - bool isExistingValue = objectStore->m_backingStore->keyExistsInObjectStore(objectStore->id(), *key, dataRowId); + RefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> recordIdentifier = objectStore->m_backingStore->createInvalidRecordIdentifier(); + bool isExistingValue = objectStore->m_backingStore->keyExistsInObjectStore(objectStore->m_databaseId, objectStore->id(), *key, recordIdentifier.get()); if (putMode == AddOnly && isExistingValue) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "Key already exists in the object store.")); @@ -237,14 +239,7 @@ void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr< // Before this point, don't do any mutation. After this point, rollback the transaction in case of error. - if (!objectStore->m_backingStore->putObjectStoreRecord(objectStore->id(), *key, value->toWireString(), dataRowId, dataRowId == InvalidId)) { - // 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 (!objectStore->m_backingStore->deleteIndexDataForRecord(dataRowId)) { + 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(); @@ -255,7 +250,15 @@ void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr< for (IndexMap::iterator it = objectStore->m_indexes.begin(); it != objectStore->m_indexes.end(); ++it, ++i) { if (!it->second->hasValidId()) continue; // The index object has been created, but does not exist in the database yet. - if (!objectStore->m_backingStore->putIndexDataForRecord(it->second->id(), *indexKeys[i], dataRowId)) { + + if (!objectStore->m_backingStore->deleteIndexDataForRecord(objectStore->m_databaseId, objectStore->id(), it->second->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 (!objectStore->m_backingStore->putIndexDataForRecord(objectStore->m_databaseId, objectStore->id(), it->second->id(), *indexKeys[i], 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(); @@ -283,13 +286,21 @@ void IDBObjectStoreBackendImpl::deleteFunction(PassRefPtr<IDBKey> prpKey, PassRe void IDBObjectStoreBackendImpl::deleteInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks) { - int64_t id; - if (!objectStore->m_backingStore->keyExistsInObjectStore(objectStore->id(), *key, id)) { + RefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> recordIdentifier = objectStore->m_backingStore->createInvalidRecordIdentifier(); + if (!objectStore->m_backingStore->keyExistsInObjectStore(objectStore->m_databaseId, objectStore->id(), *key, recordIdentifier.get())) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the object store.")); return; } - objectStore->m_backingStore->deleteObjectStoreRecord(objectStore->id(), id); + 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. + + if (!objectStore->m_backingStore->deleteIndexDataForRecord(objectStore->m_databaseId, objectStore->id(), it->second->id(), recordIdentifier.get())) + ASSERT_NOT_REACHED(); + } + + objectStore->m_backingStore->deleteObjectStoreRecord(objectStore->m_databaseId, objectStore->id(), recordIdentifier.get()); callbacks->onSuccess(SerializedScriptValue::nullValue()); } @@ -309,26 +320,28 @@ void IDBObjectStoreBackendImpl::clear(PassRefPtr<IDBCallbacks> prpCallbacks, IDB void IDBObjectStoreBackendImpl::clearInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBCallbacks> callbacks) { - objectStore->m_backingStore->clearObjectStore(objectStore->id()); + objectStore->m_backingStore->clearObjectStore(objectStore->m_databaseId, objectStore->id()); callbacks->onSuccess(SerializedScriptValue::undefinedValue()); } namespace { class PopulateIndexCallback : public IDBBackingStore::ObjectStoreRecordCallback { public: - PopulateIndexCallback(IDBBackingStore& backingStore, const String& indexKeyPath, int64_t indexId) + PopulateIndexCallback(IDBBackingStore& backingStore, const String& indexKeyPath, int64_t databaseId, int64_t objectStoreId, int64_t indexId) : m_backingStore(backingStore) , m_indexKeyPath(indexKeyPath) + , m_databaseId(databaseId) + , m_objectStoreId(objectStoreId) , m_indexId(indexId) { } - virtual bool callback(int64_t objectStoreDataId, const String& value) + virtual bool callback(const IDBBackingStore::ObjectStoreRecordIdentifier* recordIdentifier, const String& value) { RefPtr<SerializedScriptValue> objectValue = SerializedScriptValue::createFromWire(value); RefPtr<IDBKey> indexKey = fetchKeyFromKeyPath(objectValue.get(), m_indexKeyPath); - if (!m_backingStore.putIndexDataForRecord(m_indexId, *indexKey, objectStoreDataId)) + if (!m_backingStore.putIndexDataForRecord(m_databaseId, m_objectStoreId, m_indexId, *indexKey, recordIdentifier)) return false; return true; @@ -337,14 +350,16 @@ public: private: IDBBackingStore& m_backingStore; const String& m_indexKeyPath; + int64_t m_databaseId; + int64_t m_objectStoreId; int64_t m_indexId; }; } -static bool populateIndex(IDBBackingStore& backingStore, int64_t objectStoreId, int64_t indexId, const String& indexKeyPath) +static bool populateIndex(IDBBackingStore& backingStore, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const String& indexKeyPath) { - PopulateIndexCallback callback(backingStore, indexKeyPath, indexId); - if (!backingStore.forEachObjectStoreRecord(objectStoreId, callback)) + PopulateIndexCallback callback(backingStore, indexKeyPath, databaseId, objectStoreId, indexId); + if (!backingStore.forEachObjectStoreRecord(databaseId, objectStoreId, callback)) return false; return true; } @@ -360,7 +375,7 @@ PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(cons return 0; } - RefPtr<IDBIndexBackendImpl> index = IDBIndexBackendImpl::create(m_backingStore.get(), name, m_name, keyPath, unique); + RefPtr<IDBIndexBackendImpl> index = IDBIndexBackendImpl::create(m_backingStore.get(), m_databaseId, this, name, m_name, keyPath, unique); ASSERT(index->name() == name); RefPtr<IDBObjectStoreBackendImpl> objectStore = this; @@ -378,14 +393,14 @@ PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(cons void IDBObjectStoreBackendImpl::createIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBTransactionBackendInterface> transaction) { int64_t id; - if (!objectStore->m_backingStore->createIndex(objectStore->m_id, index->name(), index->keyPath(), index->unique(), id)) { + if (!objectStore->m_backingStore->createIndex(objectStore->m_databaseId, objectStore->id(), index->name(), index->keyPath(), index->unique(), id)) { transaction->abort(); return; } index->setId(id); - if (!populateIndex(*objectStore->m_backingStore, objectStore->m_id, id, index->keyPath())) { + if (!populateIndex(*objectStore->m_backingStore, objectStore->m_databaseId, objectStore->m_id, id, index->keyPath())) { transaction->abort(); return; } @@ -428,7 +443,7 @@ void IDBObjectStoreBackendImpl::deleteIndex(const String& name, IDBTransactionBa void IDBObjectStoreBackendImpl::deleteIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBTransactionBackendInterface> transaction) { - objectStore->m_backingStore->deleteIndex(index->id()); + objectStore->m_backingStore->deleteIndex(objectStore->m_databaseId, objectStore->id(), index->id()); transaction->didCompleteTaskEvents(); } @@ -446,7 +461,7 @@ void IDBObjectStoreBackendImpl::openCursorInternal(ScriptExecutionContext*, Pass { IDBCursor::Direction direction = static_cast<IDBCursor::Direction>(tmpDirection); - RefPtr<IDBBackingStore::Cursor> backingStoreCursor = objectStore->m_backingStore->openObjectStoreCursor(objectStore->id(), range.get(), direction); + RefPtr<IDBBackingStore::Cursor> backingStoreCursor = objectStore->m_backingStore->openObjectStoreCursor(objectStore->m_databaseId, objectStore->id(), range.get(), direction); if (!backingStoreCursor) { callbacks->onSuccess(SerializedScriptValue::nullValue()); return; @@ -462,14 +477,14 @@ void IDBObjectStoreBackendImpl::loadIndexes() Vector<String> names; Vector<String> keyPaths; Vector<bool> uniqueFlags; - m_backingStore->getIndexes(m_id, ids, names, keyPaths, uniqueFlags); + m_backingStore->getIndexes(m_databaseId, m_id, ids, names, keyPaths, uniqueFlags); ASSERT(names.size() == ids.size()); ASSERT(keyPaths.size() == ids.size()); ASSERT(uniqueFlags.size() == ids.size()); for (size_t i = 0; i < ids.size(); i++) - m_indexes.set(names[i], IDBIndexBackendImpl::create(m_backingStore.get(), ids[i], names[i], m_name, keyPaths[i], uniqueFlags[i])); + m_indexes.set(names[i], IDBIndexBackendImpl::create(m_backingStore.get(), m_databaseId, this, ids[i], names[i], m_name, keyPaths[i], uniqueFlags[i])); } void IDBObjectStoreBackendImpl::removeIndexFromMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index) @@ -490,7 +505,7 @@ PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::genAutoIncrementKey() if (m_autoIncrementNumber > 0) return IDBKey::createNumber(m_autoIncrementNumber++); - m_autoIncrementNumber = static_cast<int>(m_backingStore->nextAutoIncrementNumber(id())); + m_autoIncrementNumber = static_cast<int>(m_backingStore->nextAutoIncrementNumber(m_databaseId, id())); return IDBKey::createNumber(m_autoIncrementNumber++); } diff --git a/Source/WebCore/storage/IDBObjectStoreBackendImpl.h b/Source/WebCore/storage/IDBObjectStoreBackendImpl.h index 4479d1e..6bf95cd 100644 --- a/Source/WebCore/storage/IDBObjectStoreBackendImpl.h +++ b/Source/WebCore/storage/IDBObjectStoreBackendImpl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * 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 @@ -42,13 +42,13 @@ class ScriptExecutionContext; class IDBObjectStoreBackendImpl : public IDBObjectStoreBackendInterface { public: - static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBBackingStore* backingStore, int64_t id, const String& name, const String& keyPath, bool autoIncrement) + static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBBackingStore* backingStore, int64_t databaseId, int64_t id, const String& name, const String& keyPath, bool autoIncrement) { - return adoptRef(new IDBObjectStoreBackendImpl(backingStore, id, name, keyPath, autoIncrement)); + return adoptRef(new IDBObjectStoreBackendImpl(backingStore, databaseId, id, name, keyPath, autoIncrement)); } - static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBBackingStore* backingStore, const String& name, const String& keyPath, bool autoIncrement) + static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBBackingStore* backingStore, int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement) { - return adoptRef(new IDBObjectStoreBackendImpl(backingStore, name, keyPath, autoIncrement)); + return adoptRef(new IDBObjectStoreBackendImpl(backingStore, databaseId, name, keyPath, autoIncrement)); } virtual ~IDBObjectStoreBackendImpl(); @@ -59,6 +59,7 @@ public: return m_id; } void setId(int64_t id) { m_id = id; } + int64_t databaseId() const { return m_databaseId; } virtual String name() const { return m_name; } virtual String keyPath() const { return m_keyPath; } @@ -77,8 +78,8 @@ public: virtual void openCursor(PassRefPtr<IDBKeyRange> range, unsigned short direction, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); private: - IDBObjectStoreBackendImpl(IDBBackingStore*, int64_t id, const String& name, const String& keyPath, bool autoIncrement); - IDBObjectStoreBackendImpl(IDBBackingStore*, const String& name, const String& keyPath, bool autoIncrement); + IDBObjectStoreBackendImpl(IDBBackingStore*, int64_t databaseId, int64_t id, const String& name, const String& keyPath, bool autoIncrement); + IDBObjectStoreBackendImpl(IDBBackingStore*, int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement); void loadIndexes(); PassRefPtr<IDBKey> genAutoIncrementKey(); @@ -99,6 +100,7 @@ private: RefPtr<IDBBackingStore> m_backingStore; + int64_t m_databaseId; int64_t m_id; String m_name; String m_keyPath; diff --git a/Source/WebCore/storage/IDBSQLiteBackingStore.cpp b/Source/WebCore/storage/IDBSQLiteBackingStore.cpp index e43b7a3..f42a1e1 100644 --- a/Source/WebCore/storage/IDBSQLiteBackingStore.cpp +++ b/Source/WebCore/storage/IDBSQLiteBackingStore.cpp @@ -173,7 +173,7 @@ PassRefPtr<IDBBackingStore> IDBSQLiteBackingStore::open(SecurityOrigin* security if (!pathBase.isEmpty()) { if (!makeAllDirectories(pathBase)) { // FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out. - LOG_ERROR("Unabled to create LocalStorage database path %s", pathBase.utf8().data()); + LOG_ERROR("Unable to create Indexed DB database path %s", pathBase.utf8().data()); return 0; } path = pathByAppendingComponent(pathBase, securityOrigin->databaseIdentifier() + ".indexeddb"); @@ -263,7 +263,7 @@ void IDBSQLiteBackingStore::getObjectStores(int64_t databaseId, Vector<int64_t>& } } -bool IDBSQLiteBackingStore::createObjectStore(const String& name, const String& keyPath, bool autoIncrement, int64_t databaseId, int64_t& assignedObjectStoreId) +bool IDBSQLiteBackingStore::createObjectStore(int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement, int64_t& assignedObjectStoreId) { SQLiteStatement query(m_db, "INSERT INTO ObjectStores (name, keyPath, doAutoIncrement, databaseId) VALUES (?, ?, ?, ?)"); if (query.prepare() != SQLResultOk) @@ -291,7 +291,7 @@ static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id) ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. } -void IDBSQLiteBackingStore::deleteObjectStore(int64_t objectStoreId) +void IDBSQLiteBackingStore::deleteObjectStore(int64_t, int64_t objectStoreId) { doDelete(m_db, "DELETE FROM ObjectStores WHERE id = ?", objectStoreId); doDelete(m_db, "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStoreId); @@ -299,6 +299,26 @@ void IDBSQLiteBackingStore::deleteObjectStore(int64_t objectStoreId) doDelete(m_db, "DELETE FROM Indexes WHERE objectStoreId = ?", objectStoreId); } +namespace { +class SQLiteRecordIdentifier : public IDBBackingStore::ObjectStoreRecordIdentifier { +public: + static PassRefPtr<SQLiteRecordIdentifier> create() { return adoptRef(new SQLiteRecordIdentifier()); } + static PassRefPtr<SQLiteRecordIdentifier> create(int64_t id) { return adoptRef(new SQLiteRecordIdentifier(id)); } + virtual bool isValid() const { return m_id != -1; } + int64_t id() const { return m_id; } + void setId(int64_t id) { m_id = id; } +private: + SQLiteRecordIdentifier() : m_id(-1) { } + SQLiteRecordIdentifier(int64_t id) : m_id(id) { } + int64_t m_id; +}; +} + +PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> IDBSQLiteBackingStore::createInvalidRecordIdentifier() +{ + return SQLiteRecordIdentifier::create(); +} + static String whereSyntaxForKey(const IDBKey& key, String qualifiedTableName = "") { switch (key.type()) { @@ -373,7 +393,7 @@ static String upperCursorWhereFragment(const IDBKey& key, String comparisonOpera return ""; } -String IDBSQLiteBackingStore::getObjectStoreRecord(int64_t objectStoreId, const IDBKey& key) +String IDBSQLiteBackingStore::getObjectStoreRecord(int64_t, int64_t objectStoreId, const IDBKey& key) { SQLiteStatement query(m_db, "SELECT keyString, keyDate, keyNumber, value FROM ObjectStoreData WHERE objectStoreId = ? AND " + whereSyntaxForKey(key)); bool ok = query.prepare() == SQLResultOk; @@ -422,58 +442,54 @@ static void bindKeyToQueryWithNulls(SQLiteStatement& query, int baseColumn, cons } } -bool IDBSQLiteBackingStore::putObjectStoreRecord(int64_t objectStoreId, const IDBKey& key, const String& value, int64_t& rowId, bool invalidRowId) +bool IDBSQLiteBackingStore::putObjectStoreRecord(int64_t, int64_t objectStoreId, const IDBKey& key, const String& value, ObjectStoreRecordIdentifier* recordIdentifier) { - String sql = !invalidRowId ? "UPDATE ObjectStoreData SET keyString = ?, keyDate = ?, keyNumber = ?, value = ? WHERE id = ?" - : "INSERT INTO ObjectStoreData (keyString, keyDate, keyNumber, value, objectStoreId) VALUES (?, ?, ?, ?, ?)"; + SQLiteRecordIdentifier* sqliteRecordIdentifier = static_cast<SQLiteRecordIdentifier*>(recordIdentifier); + + String sql = sqliteRecordIdentifier->isValid() ? "UPDATE ObjectStoreData SET keyString = ?, keyDate = ?, keyNumber = ?, value = ? WHERE id = ?" + : "INSERT INTO ObjectStoreData (keyString, keyDate, keyNumber, value, objectStoreId) VALUES (?, ?, ?, ?, ?)"; SQLiteStatement query(m_db, sql); if (query.prepare() != SQLResultOk) return false; bindKeyToQueryWithNulls(query, 1, key); query.bindBlob(4, value); - if (!invalidRowId) - query.bindInt64(5, rowId); + if (sqliteRecordIdentifier->isValid()) + query.bindInt64(5, sqliteRecordIdentifier->id()); else query.bindInt64(5, objectStoreId); if (query.step() != SQLResultDone) return false; - if (invalidRowId) - rowId = m_db.lastInsertRowID(); + if (!sqliteRecordIdentifier->isValid()) + sqliteRecordIdentifier->setId(m_db.lastInsertRowID()); return true; } -void IDBSQLiteBackingStore::clearObjectStore(int64_t objectStoreId) +void IDBSQLiteBackingStore::clearObjectStore(int64_t, int64_t objectStoreId) { doDelete(m_db, "DELETE FROM IndexData WHERE objectStoreDataId IN (SELECT id FROM ObjectStoreData WHERE objectStoreId = ?)", objectStoreId); doDelete(m_db, "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStoreId); } -void IDBSQLiteBackingStore::deleteObjectStoreRecord(int64_t, int64_t objectStoreDataId) +void IDBSQLiteBackingStore::deleteObjectStoreRecord(int64_t, int64_t objectStoreId, const ObjectStoreRecordIdentifier* recordIdentifier) { + const SQLiteRecordIdentifier* sqliteRecordIdentifier = static_cast<const SQLiteRecordIdentifier*>(recordIdentifier); + ASSERT(sqliteRecordIdentifier->isValid()); + SQLiteStatement osQuery(m_db, "DELETE FROM ObjectStoreData WHERE id = ?"); bool ok = osQuery.prepare() == SQLResultOk; ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? - osQuery.bindInt64(1, objectStoreDataId); + osQuery.bindInt64(1, sqliteRecordIdentifier->id()); ok = osQuery.step() == SQLResultDone; ASSERT_UNUSED(ok, ok); - - SQLiteStatement indexQuery(m_db, "DELETE FROM IndexData WHERE objectStoreDataId = ?"); - ok = indexQuery.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? - - indexQuery.bindInt64(1, objectStoreDataId); - - ok = indexQuery.step() == SQLResultDone; - ASSERT_UNUSED(ok, ok); } -double IDBSQLiteBackingStore::nextAutoIncrementNumber(int64_t objectStoreId) +double IDBSQLiteBackingStore::nextAutoIncrementNumber(int64_t, int64_t objectStoreId) { SQLiteStatement query(m_db, "SELECT max(keyNumber) + 1 FROM ObjectStoreData WHERE objectStoreId = ? AND keyString IS NULL AND keyDate IS NULL"); bool ok = query.prepare() == SQLResultOk; @@ -487,8 +503,10 @@ double IDBSQLiteBackingStore::nextAutoIncrementNumber(int64_t objectStoreId) return query.getColumnDouble(0); } -bool IDBSQLiteBackingStore::keyExistsInObjectStore(int64_t objectStoreId, const IDBKey& key, int64_t& foundObjectStoreDataId) +bool IDBSQLiteBackingStore::keyExistsInObjectStore(int64_t, int64_t objectStoreId, const IDBKey& key, ObjectStoreRecordIdentifier* foundRecordIdentifier) { + SQLiteRecordIdentifier* sqliteRecordIdentifier = static_cast<SQLiteRecordIdentifier*>(foundRecordIdentifier); + String sql = String("SELECT id FROM ObjectStoreData WHERE objectStoreId = ? AND ") + whereSyntaxForKey(key); SQLiteStatement query(m_db, sql); bool ok = query.prepare() == SQLResultOk; @@ -500,11 +518,11 @@ bool IDBSQLiteBackingStore::keyExistsInObjectStore(int64_t objectStoreId, const if (query.step() != SQLResultRow) return false; - foundObjectStoreDataId = query.getColumnInt64(0); + sqliteRecordIdentifier->setId(query.getColumnInt64(0)); return true; } -bool IDBSQLiteBackingStore::forEachObjectStoreRecord(int64_t objectStoreId, ObjectStoreRecordCallback& callback) +bool IDBSQLiteBackingStore::forEachObjectStoreRecord(int64_t, int64_t objectStoreId, ObjectStoreRecordCallback& callback) { SQLiteStatement query(m_db, "SELECT id, value FROM ObjectStoreData WHERE objectStoreId = ?"); if (query.prepare() != SQLResultOk) @@ -515,14 +533,15 @@ bool IDBSQLiteBackingStore::forEachObjectStoreRecord(int64_t objectStoreId, Obje while (query.step() == SQLResultRow) { int64_t objectStoreDataId = query.getColumnInt64(0); String value = query.getColumnBlobAsString(1); - if (!callback.callback(objectStoreDataId, value)) + RefPtr<SQLiteRecordIdentifier> recordIdentifier = SQLiteRecordIdentifier::create(objectStoreDataId); + if (!callback.callback(recordIdentifier.get(), value)) return false; } return true; } -void IDBSQLiteBackingStore::getIndexes(int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags) +void IDBSQLiteBackingStore::getIndexes(int64_t, int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags) { SQLiteStatement query(m_db, "SELECT id, name, keyPath, isUnique FROM Indexes WHERE objectStoreId = ?"); bool ok = query.prepare() == SQLResultOk; @@ -543,7 +562,7 @@ void IDBSQLiteBackingStore::getIndexes(int64_t objectStoreId, Vector<int64_t>& f } } -bool IDBSQLiteBackingStore::createIndex(int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId) +bool IDBSQLiteBackingStore::createIndex(int64_t, int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId) { SQLiteStatement query(m_db, "INSERT INTO Indexes (objectStoreId, name, keyPath, isUnique) VALUES (?, ?, ?, ?)"); if (query.prepare() != SQLResultOk) @@ -561,36 +580,41 @@ bool IDBSQLiteBackingStore::createIndex(int64_t objectStoreId, const String& nam return true; } -void IDBSQLiteBackingStore::deleteIndex(int64_t indexId) +void IDBSQLiteBackingStore::deleteIndex(int64_t, int64_t, int64_t indexId) { doDelete(m_db, "DELETE FROM Indexes WHERE id = ?", indexId); doDelete(m_db, "DELETE FROM IndexData WHERE indexId = ?", indexId); } -bool IDBSQLiteBackingStore::putIndexDataForRecord(int64_t indexId, const IDBKey& key, int64_t objectStoreDataId) +bool IDBSQLiteBackingStore::putIndexDataForRecord(int64_t, int64_t, int64_t indexId, const IDBKey& key, const ObjectStoreRecordIdentifier* recordIdentifier) { + const SQLiteRecordIdentifier* sqliteRecordIdentifier = static_cast<const SQLiteRecordIdentifier*>(recordIdentifier); + SQLiteStatement query(m_db, "INSERT INTO IndexData (keyString, keyDate, keyNumber, indexId, objectStoreDataId) VALUES (?, ?, ?, ?, ?)"); if (query.prepare() != SQLResultOk) return false; bindKeyToQueryWithNulls(query, 1, key); query.bindInt64(4, indexId); - query.bindInt64(5, objectStoreDataId); + query.bindInt64(5, sqliteRecordIdentifier->id()); return query.step() == SQLResultDone; } -bool IDBSQLiteBackingStore::deleteIndexDataForRecord(int64_t objectStoreDataId) +bool IDBSQLiteBackingStore::deleteIndexDataForRecord(int64_t, int64_t, int64_t indexId, const ObjectStoreRecordIdentifier* recordIdentifier) { - SQLiteStatement query(m_db, "DELETE FROM IndexData WHERE objectStoreDataId = ?"); + const SQLiteRecordIdentifier* sqliteRecordIdentifier = static_cast<const SQLiteRecordIdentifier*>(recordIdentifier); + + SQLiteStatement query(m_db, "DELETE FROM IndexData WHERE objectStoreDataId = ? AND indexId = ?"); if (query.prepare() != SQLResultOk) return false; - query.bindInt64(1, objectStoreDataId); + query.bindInt64(1, sqliteRecordIdentifier->id()); + query.bindInt64(2, indexId); return query.step() == SQLResultDone; } -String IDBSQLiteBackingStore::getObjectViaIndex(int64_t indexId, const IDBKey& key) +String IDBSQLiteBackingStore::getObjectViaIndex(int64_t, int64_t, int64_t indexId, const IDBKey& key) { String sql = String("SELECT ") + "ObjectStoreData.value " @@ -629,7 +653,7 @@ static PassRefPtr<IDBKey> keyFromQuery(SQLiteStatement& query, int baseColumn) return IDBKey::createNull(); } -PassRefPtr<IDBKey> IDBSQLiteBackingStore::getPrimaryKeyViaIndex(int64_t indexId, const IDBKey& key) +PassRefPtr<IDBKey> IDBSQLiteBackingStore::getPrimaryKeyViaIndex(int64_t, int64_t, int64_t indexId, const IDBKey& key) { String sql = String("SELECT ") + "ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber " @@ -651,7 +675,7 @@ PassRefPtr<IDBKey> IDBSQLiteBackingStore::getPrimaryKeyViaIndex(int64_t indexId, return foundKey.release(); } -bool IDBSQLiteBackingStore::keyExistsInIndex(int64_t indexId, const IDBKey& key) +bool IDBSQLiteBackingStore::keyExistsInIndex(int64_t, int64_t, int64_t indexId, const IDBKey& key) { String sql = String("SELECT id FROM IndexData WHERE indexId = ? AND ") + whereSyntaxForKey(key); SQLiteStatement query(m_db, sql); @@ -682,7 +706,7 @@ public: virtual PassRefPtr<IDBKey> key() { return m_currentKey; } virtual PassRefPtr<IDBKey> primaryKey() { return m_currentKey; } virtual String value() = 0; - virtual int64_t objectStoreDataId() = 0; + virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() = 0; virtual int64_t indexDataId() = 0; virtual void loadCurrentRow() = 0; @@ -741,7 +765,7 @@ public: // CursorImplCommon. virtual String value() { return m_currentValue; } - virtual int64_t objectStoreDataId() { return m_currentId; } + virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() { return SQLiteRecordIdentifier::create(m_currentId); } virtual int64_t indexDataId() { ASSERT_NOT_REACHED(); return 0; } virtual void loadCurrentRow(); virtual bool currentRowExists(); @@ -779,7 +803,7 @@ public: // CursorImplCommon virtual PassRefPtr<IDBKey> primaryKey() { return m_currentPrimaryKey; } virtual String value() { ASSERT_NOT_REACHED(); return String(); } - virtual int64_t objectStoreDataId() { ASSERT_NOT_REACHED(); return 0; } + virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() { ASSERT_NOT_REACHED(); return 0; } virtual int64_t indexDataId() { return m_currentId; } virtual void loadCurrentRow(); virtual bool currentRowExists(); @@ -817,7 +841,7 @@ public: // CursorImplCommon virtual PassRefPtr<IDBKey> primaryKey() { return m_currentPrimaryKey; } virtual String value() { return m_currentValue; } - virtual int64_t objectStoreDataId() { ASSERT_NOT_REACHED(); return 0; } + virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() { ASSERT_NOT_REACHED(); return 0; } virtual int64_t indexDataId() { return m_currentId; } virtual void loadCurrentRow(); virtual bool currentRowExists(); @@ -849,7 +873,7 @@ bool IndexCursorImpl::currentRowExists() } // namespace -PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openObjectStoreCursor(int64_t objectStoreId, const IDBKeyRange* range, IDBCursor::Direction direction) +PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openObjectStoreCursor(int64_t, int64_t objectStoreId, const IDBKeyRange* range, IDBCursor::Direction direction) { bool lowerBound = range && range->lower(); bool upperBound = range && range->upper(); @@ -886,7 +910,7 @@ PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openObjectStoreCursor return cursor.release(); } -PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openIndexKeyCursor(int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction) +PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openIndexKeyCursor(int64_t, int64_t, int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction) { String sql = String("SELECT IndexData.id, IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, ") + ("ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ") @@ -926,7 +950,7 @@ PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openIndexKeyCursor(in return cursor.release(); } -PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openIndexCursor(int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction) +PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openIndexCursor(int64_t, int64_t, int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction) { String sql = String("SELECT IndexData.id, IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, ") + ("ObjectStoreData.value, ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ") diff --git a/Source/WebCore/storage/IDBSQLiteBackingStore.h b/Source/WebCore/storage/IDBSQLiteBackingStore.h index fe0c6c1..9efa060 100644 --- a/Source/WebCore/storage/IDBSQLiteBackingStore.h +++ b/Source/WebCore/storage/IDBSQLiteBackingStore.h @@ -41,29 +41,30 @@ public: virtual bool setIDBDatabaseMetaData(const String& name, const String& version, int64_t& rowId, bool invalidRowId); virtual void getObjectStores(int64_t databaseId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundAutoIncrementFlags); - virtual bool createObjectStore(const String& name, const String& keyPath, bool autoIncrement, int64_t databaseId, int64_t& assignedObjectStoreId); - virtual void deleteObjectStore(int64_t objectStoreId); - virtual String getObjectStoreRecord(int64_t objectStoreId, const IDBKey&); - virtual bool putObjectStoreRecord(int64_t objectStoreId, const IDBKey&, const String& value, int64_t& rowId, bool invalidRowId); - virtual void clearObjectStore(int64_t objectStoreId); - virtual void deleteObjectStoreRecord(int64_t objectStoreId, int64_t objectStoreDataId); - virtual double nextAutoIncrementNumber(int64_t objectStoreId); - virtual bool keyExistsInObjectStore(int64_t objectStoreId, const IDBKey&, int64_t& foundObjectStoreDataId); - - virtual bool forEachObjectStoreRecord(int64_t objectStoreId, ObjectStoreRecordCallback&); - - virtual void getIndexes(int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags); - virtual bool createIndex(int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId); - virtual void deleteIndex(int64_t indexId); - virtual bool putIndexDataForRecord(int64_t indexId, const IDBKey&, int64_t objectStoreDataId); - virtual bool deleteIndexDataForRecord(int64_t objectStoreDataId); - virtual String getObjectViaIndex(int64_t indexId, const IDBKey&); - virtual PassRefPtr<IDBKey> getPrimaryKeyViaIndex(int64_t indexId, const IDBKey&); - virtual bool keyExistsInIndex(int64_t indexId, const IDBKey&); - - virtual PassRefPtr<Cursor> openObjectStoreCursor(int64_t objectStoreId, const IDBKeyRange*, IDBCursor::Direction); - virtual PassRefPtr<Cursor> openIndexKeyCursor(int64_t indexId, const IDBKeyRange*, IDBCursor::Direction); - virtual PassRefPtr<Cursor> openIndexCursor(int64_t indexId, const IDBKeyRange*, IDBCursor::Direction); + virtual bool createObjectStore(int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement, int64_t& assignedObjectStoreId); + virtual void deleteObjectStore(int64_t databaseId, int64_t objectStoreId); + virtual PassRefPtr<ObjectStoreRecordIdentifier> createInvalidRecordIdentifier(); + virtual String getObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey&); + virtual bool putObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey&, const String& value, ObjectStoreRecordIdentifier*); + virtual void clearObjectStore(int64_t databaseId, int64_t objectStoreId); + virtual void deleteObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const ObjectStoreRecordIdentifier*); + virtual double nextAutoIncrementNumber(int64_t databaseId, int64_t objectStoreId); + virtual bool keyExistsInObjectStore(int64_t databaseId, int64_t objectStoreId, const IDBKey&, ObjectStoreRecordIdentifier* foundRecordIdentifier); + + virtual bool forEachObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, ObjectStoreRecordCallback&); + + virtual void getIndexes(int64_t databaseId, int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags); + virtual bool createIndex(int64_t databaseId, int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId); + virtual void deleteIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId); + virtual bool putIndexDataForRecord(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, const ObjectStoreRecordIdentifier*); + virtual bool deleteIndexDataForRecord(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const ObjectStoreRecordIdentifier*); + virtual String getObjectViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&); + virtual PassRefPtr<IDBKey> getPrimaryKeyViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&); + virtual bool keyExistsInIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&); + + virtual PassRefPtr<Cursor> openObjectStoreCursor(int64_t databaseId, int64_t objectStoreId, const IDBKeyRange*, IDBCursor::Direction); + virtual PassRefPtr<Cursor> openIndexKeyCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IDBCursor::Direction); + virtual PassRefPtr<Cursor> openIndexCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IDBCursor::Direction); virtual PassRefPtr<Transaction> createTransaction(); diff --git a/Source/WebCore/storage/StorageInfo.cpp b/Source/WebCore/storage/StorageInfo.cpp new file mode 100644 index 0000000..f3b3d08 --- /dev/null +++ b/Source/WebCore/storage/StorageInfo.cpp @@ -0,0 +1,68 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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" + +#if ENABLE(QUOTA) + +#include "StorageInfo.h" + +#include "NotImplemented.h" +#include "StorageInfoErrorCallback.h" +#include "StorageInfoQuotaCallback.h" +#include "StorageInfoUsageCallback.h" + +namespace WebCore { + +class ScriptExecutionContext; + +StorageInfo::StorageInfo() +{ +} + +StorageInfo::~StorageInfo() +{ +} + +#if !PLATFORM(CHROMIUM) +void StorageInfo::queryUsageAndQuota(ScriptExecutionContext*, int, PassRefPtr<StorageInfoUsageCallback>, PassRefPtr<StorageInfoErrorCallback>) +{ + notImplemented(); +} + +void StorageInfo::requestQuota(ScriptExecutionContext*, int, unsigned long long, PassRefPtr<StorageInfoQuotaCallback>, PassRefPtr<StorageInfoErrorCallback>) +{ + notImplemented(); +} +#endif + +} // namespace WebCore + +#endif // ENABLE(QUOTA) diff --git a/Source/WebCore/storage/StorageInfo.h b/Source/WebCore/storage/StorageInfo.h new file mode 100644 index 0000000..b908bd8 --- /dev/null +++ b/Source/WebCore/storage/StorageInfo.h @@ -0,0 +1,72 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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. + */ + +#ifndef StorageInfo_h +#define StorageInfo_h + +#if ENABLE(QUOTA) + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class ScriptExecutionContext; +class StorageInfoErrorCallback; +class StorageInfoQuotaCallback; +class StorageInfoUsageCallback; + +class StorageInfo : public RefCounted<StorageInfo> { +public: + enum { + TEMPORARY, + PERSISTENT, + }; + + static PassRefPtr<StorageInfo> create() + { + return adoptRef(new StorageInfo()); + } + + void queryUsageAndQuota(ScriptExecutionContext*, int storageType, PassRefPtr<StorageInfoUsageCallback>, PassRefPtr<StorageInfoErrorCallback>); + + void requestQuota(ScriptExecutionContext*, int storageType, unsigned long long newQuotaInBytes, PassRefPtr<StorageInfoQuotaCallback>, PassRefPtr<StorageInfoErrorCallback>); + + ~StorageInfo(); + +private: + StorageInfo(); +}; + +} // namespace WebCore + +#endif // ENABLE(QUOTA) + +#endif // StorageInfo_h diff --git a/Source/WebCore/storage/StorageInfo.idl b/Source/WebCore/storage/StorageInfo.idl new file mode 100644 index 0000000..c75b72b --- /dev/null +++ b/Source/WebCore/storage/StorageInfo.idl @@ -0,0 +1,37 @@ +/* + * 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 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. + */ + +module storage { + interface [ + Conditional=QUOTA, + OmitConstructor, + GenerateNativeConverter + ] StorageInfo { + const unsigned short TEMPORARY = 0; + const unsigned short PERSISTENT = 1; + + [CallWith=ScriptExecutionContext] void queryUsageAndQuota(in unsigned short storageType, in [Callback, Optional] StorageInfoUsageCallback usageCallback, in [Callback, Optional] StorageInfoErrorCallback errorCallback); + }; +} diff --git a/Source/WebCore/storage/StorageInfoErrorCallback.h b/Source/WebCore/storage/StorageInfoErrorCallback.h new file mode 100644 index 0000000..6e3d75e --- /dev/null +++ b/Source/WebCore/storage/StorageInfoErrorCallback.h @@ -0,0 +1,52 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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. + */ + +#ifndef StorageInfoErrorCallback_h +#define StorageInfoErrorCallback_h + +#if ENABLE(QUOTA) + +#include <wtf/RefCounted.h> + +namespace WebCore { + +class DOMCoreException; + +class StorageInfoErrorCallback : public RefCounted<StorageInfoErrorCallback> { +public: + virtual ~StorageInfoErrorCallback() { } + virtual bool handleEvent(DOMCoreException*) = 0; +}; + +} // namespace WebCore + +#endif // ENABLE(QUOTA) + +#endif // StorageInfoErrorCallback_h diff --git a/Source/WebCore/storage/StorageInfoErrorCallback.idl b/Source/WebCore/storage/StorageInfoErrorCallback.idl new file mode 100644 index 0000000..66c3316 --- /dev/null +++ b/Source/WebCore/storage/StorageInfoErrorCallback.idl @@ -0,0 +1,38 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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. + */ + +module storage { + interface [ + Conditional=QUOTA, + Callback + ] StorageInfoErrorCallback { + boolean handleEvent(in DOMCoreException error); + }; +} diff --git a/Source/WebCore/storage/StorageInfoQuotaCallback.h b/Source/WebCore/storage/StorageInfoQuotaCallback.h new file mode 100644 index 0000000..f6fde99 --- /dev/null +++ b/Source/WebCore/storage/StorageInfoQuotaCallback.h @@ -0,0 +1,50 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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. + */ + +#ifndef StorageInfoQuotaCallback_h +#define StorageInfoQuotaCallback_h + +#if ENABLE(QUOTA) + +#include <wtf/RefCounted.h> + +namespace WebCore { + +class StorageInfoQuotaCallback : public RefCounted<StorageInfoQuotaCallback> { +public: + virtual ~StorageInfoQuotaCallback() { } + virtual bool handleEvent(unsigned long long grantedQuotaInBytes) = 0; +}; + +} // namespace + +#endif // ENABLE(QUOTA) + +#endif // StorageInfoQuotaCallback_h diff --git a/Source/WebCore/storage/StorageInfoUsageCallback.h b/Source/WebCore/storage/StorageInfoUsageCallback.h new file mode 100644 index 0000000..8382611 --- /dev/null +++ b/Source/WebCore/storage/StorageInfoUsageCallback.h @@ -0,0 +1,50 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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. + */ + +#ifndef StorageInfoUsageCallback_h +#define StorageInfoUsageCallback_h + +#if ENABLE(QUOTA) + +#include <wtf/RefCounted.h> + +namespace WebCore { + +class StorageInfoUsageCallback : public RefCounted<StorageInfoUsageCallback> { +public: + virtual ~StorageInfoUsageCallback() { } + virtual bool handleEvent(unsigned long long currentUsageInBytes, unsigned long long currentQuotaInBytes) = 0; +}; + +} // namespace + +#endif // ENABLE(QUOTA) + +#endif // StorageInfoUsageCallback_h diff --git a/Source/WebCore/storage/StorageInfoUsageCallback.idl b/Source/WebCore/storage/StorageInfoUsageCallback.idl new file mode 100644 index 0000000..dfa58e6 --- /dev/null +++ b/Source/WebCore/storage/StorageInfoUsageCallback.idl @@ -0,0 +1,39 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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. + */ + +module storage { + interface [ + Conditional=QUOTA, + Callback + ] StorageInfoUsageCallback { + boolean handleEvent(in unsigned long long currentUsageInBytes, in unsigned long long currentQuotaInBytes); + }; +} + diff --git a/Source/WebCore/storage/StorageTracker.cpp b/Source/WebCore/storage/StorageTracker.cpp index e18b4b8..b9ce65f 100644 --- a/Source/WebCore/storage/StorageTracker.cpp +++ b/Source/WebCore/storage/StorageTracker.cpp @@ -232,8 +232,10 @@ void StorageTracker::syncFileSystemAndTrackerDatabase() // Delete stale StorageTracker records. OriginSet::const_iterator setEnd = originSetCopy.end(); for (OriginSet::const_iterator it = originSetCopy.begin(); it != setEnd; ++it) { - if (!foundOrigins.contains(*it)) - syncDeleteOrigin(*it); + if (!foundOrigins.contains(*it)) { + RefPtr<StringImpl> originIdentifier = (*it).threadsafeCopy().impl(); + callOnMainThread(deleteOriginOnMainThread, originIdentifier.release().leakRef()); + } } } @@ -376,10 +378,33 @@ void StorageTracker::syncDeleteAllOrigins() if (m_database.isOpen()) m_database.close(); - SQLiteFileSystem::deleteDatabaseFile(trackerDatabasePath()); + if (!SQLiteFileSystem::deleteDatabaseFile(trackerDatabasePath())) { + // In the case where it is not possible to delete the database file (e.g some other program + // like a virus scanner is accessing it), make sure to remove all entries. + openTrackerDatabase(false); + if (!m_database.isOpen()) + return; + SQLiteStatement deleteStatement(m_database, "DELETE FROM Origins"); + if (deleteStatement.prepare() != SQLResultOk) { + LOG_ERROR("Unable to prepare deletion of all origins"); + return; + } + if (!deleteStatement.executeCommand()) { + LOG_ERROR("Unable to execute deletion of all origins"); + return; + } + } SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_storageDirectoryPath); } +void StorageTracker::deleteOriginOnMainThread(void* originIdentifier) +{ + ASSERT(isMainThread()); + + String identifier = adoptRef(reinterpret_cast<StringImpl*>(originIdentifier)); + tracker().deleteOrigin(identifier); +} + void StorageTracker::deleteOrigin(const String& originIdentifier) { deleteOrigin(SecurityOrigin::createFromDatabaseIdentifier(originIdentifier).get()); diff --git a/Source/WebCore/storage/StorageTracker.h b/Source/WebCore/storage/StorageTracker.h index 04a821b..714576d 100644 --- a/Source/WebCore/storage/StorageTracker.h +++ b/Source/WebCore/storage/StorageTracker.h @@ -86,6 +86,7 @@ private: bool canDeleteOrigin(const String& originIdentifier); void willDeleteOrigin(const String& originIdentifier); void willDeleteAllOrigins(); + static void deleteOriginOnMainThread(void* originIdentifier); void originFilePaths(Vector<String>& paths); |