diff options
Diffstat (limited to 'Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp')
-rw-r--r-- | Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp | 1931 |
1 files changed, 0 insertions, 1931 deletions
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp b/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp deleted file mode 100644 index 0712bdf38..000000000 --- a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp +++ /dev/null @@ -1,1931 +0,0 @@ -/* - * 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 "IDBBackingStoreLevelDB.h" - -#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB) - -#include "FileSystem.h" -#include "HistogramSupport.h" -#include "IDBDatabaseMetadata.h" -#include "IDBIndexWriterLevelDB.h" -#include "IDBKey.h" -#include "IDBKeyPath.h" -#include "IDBKeyRange.h" -#include "IDBLevelDBCoding.h" -#include "IDBTransactionBackend.h" -#include "LevelDBComparator.h" -#include "LevelDBDatabase.h" -#include "LevelDBIterator.h" -#include "LevelDBSlice.h" -#include "LevelDBTransaction.h" -#include "Logging.h" -#include "SecurityOrigin.h" -#include "SharedBuffer.h" -#include <wtf/Assertions.h> - -namespace WebCore { - -using namespace IDBLevelDBCoding; - -const int64_t KeyGeneratorInitialNumber = 1; // From the IndexedDB specification. - -enum IDBBackingStoreLevelDBErrorSource { - // 0 - 2 are no longer used. - FindKeyInIndex = 3, - GetIDBDatabaseMetaData, - GetIndexes, - GetKeyGeneratorCurrentNumber, - GetObjectStores, - GetRecord, - KeyExistsInObjectStore, - LoadCurrentRow, - SetupMetadata, - GetPrimaryKeyViaIndex, - KeyExistsInIndex, - VersionExists, - DeleteObjectStore, - SetMaxObjectStoreId, - SetMaxIndexId, - GetNewDatabaseId, - GetNewVersionNumber, - CreateIDBDatabaseMetaData, - DeleteDatabase, - IDBLevelDBBackingStoreInternalErrorMax, -}; - -static void recordInternalError(const char* type, IDBBackingStoreLevelDBErrorSource location) -{ - String name = String::format("WebCore.IndexedDB.BackingStore.%sError", type); - HistogramSupport::histogramEnumeration(name.utf8().data(), location, IDBLevelDBBackingStoreInternalErrorMax); -} - -// Use to signal conditions that usually indicate developer error, but could be caused by data corruption. -// A macro is used instead of an inline function so that the assert and log report the line number. -#define REPORT_ERROR(type, location) \ - do { \ - LOG_ERROR("IndexedDB %s Error: %s", type, #location); \ - ASSERT_NOT_REACHED(); \ - recordInternalError(type, location); \ - } while (0) - -#define INTERNAL_READ_ERROR(location) REPORT_ERROR("Read", location) -#define INTERNAL_CONSISTENCY_ERROR(location) REPORT_ERROR("Consistency", location) -#define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location) - -static void putBool(LevelDBTransaction* transaction, const LevelDBSlice& key, bool value) -{ - transaction->put(key, encodeBool(value)); -} - -template <typename DBOrTransaction> -static bool getInt(DBOrTransaction* db, const LevelDBSlice& key, int64_t& foundInt, bool& found) -{ - Vector<char> result; - bool ok = db->safeGet(key, result, found); - if (!ok) - return false; - if (!found) - return true; - - foundInt = decodeInt(result.begin(), result.end()); - return true; -} - -static void putInt(LevelDBTransaction* transaction, const LevelDBSlice& key, int64_t value) -{ - ASSERT(value >= 0); - transaction->put(key, encodeInt(value)); -} - -template <typename DBOrTransaction> -WARN_UNUSED_RETURN static bool getVarInt(DBOrTransaction* db, const LevelDBSlice& key, int64_t& foundInt, bool& found) -{ - Vector<char> result; - bool ok = db->safeGet(key, result, found); - if (!ok) - return false; - if (!found) - return true; - - found = decodeVarInt(result.begin(), result.end(), foundInt) == result.end(); - return true; -} - -static void putVarInt(LevelDBTransaction* transaction, const LevelDBSlice& key, int64_t value) -{ - transaction->put(key, encodeVarInt(value)); -} - -template <typename DBOrTransaction> -WARN_UNUSED_RETURN static bool getString(DBOrTransaction* db, const LevelDBSlice& key, String& foundString, bool& found) -{ - Vector<char> result; - found = false; - bool ok = db->safeGet(key, result, found); - if (!ok) - return false; - if (!found) - return true; - - foundString = decodeString(result.begin(), result.end()); - return true; -} - -static void putString(LevelDBTransaction* transaction, const LevelDBSlice& key, const String& value) -{ - transaction->put(key, encodeString(value)); -} - -static void putIDBKeyPath(LevelDBTransaction* transaction, const LevelDBSlice& key, const IDBKeyPath& value) -{ - transaction->put(key, encodeIDBKeyPath(value)); -} - -static int compareKeys(const LevelDBSlice& a, const LevelDBSlice& b) -{ - return compare(a, b); -} - -int IDBBackingStoreLevelDB::compareIndexKeys(const LevelDBSlice& a, const LevelDBSlice& b) -{ - return compare(a, b, true); -} - -class Comparator : public LevelDBComparator { -public: - virtual int compare(const LevelDBSlice& a, const LevelDBSlice& b) const { return IDBLevelDBCoding::compare(a, b); } - virtual const char* name() const { return "idb_cmp1"; } -}; - -// 0 - Initial version. -// 1 - Adds UserIntVersion to DatabaseMetaData. -// 2 - Adds DataVersion to to global metadata. -const int64_t latestKnownSchemaVersion = 2; -WARN_UNUSED_RETURN static bool isSchemaKnown(LevelDBDatabase* db, bool& known) -{ - int64_t dbSchemaVersion = 0; - bool found = false; - bool ok = getInt(db, SchemaVersionKey::encode(), dbSchemaVersion, found); - if (!ok) - return false; - if (!found) { - known = true; - return true; - } - if (dbSchemaVersion > latestKnownSchemaVersion) { - known = false; - return true; - } - - const uint32_t latestKnownDataVersion = SerializedScriptValue::wireFormatVersion(); - int64_t dbDataVersion = 0; - ok = getInt(db, DataVersionKey::encode(), dbDataVersion, found); - if (!ok) - return false; - if (!found) { - known = true; - return true; - } - - if (dbDataVersion > latestKnownDataVersion) { - known = false; - return true; - } - - known = true; - return true; -} - -WARN_UNUSED_RETURN static bool setUpMetadata(LevelDBDatabase* db, const String& origin) -{ - const uint32_t latestKnownDataVersion = SerializedScriptValue::wireFormatVersion(); - const Vector<char> schemaVersionKey = SchemaVersionKey::encode(); - const Vector<char> dataVersionKey = DataVersionKey::encode(); - - RefPtr<LevelDBTransaction> transaction = LevelDBTransaction::create(db); - - int64_t dbSchemaVersion = 0; - int64_t dbDataVersion = 0; - bool found = false; - bool ok = getInt(transaction.get(), schemaVersionKey, dbSchemaVersion, found); - if (!ok) { - INTERNAL_READ_ERROR(SetupMetadata); - return false; - } - if (!found) { - // Initialize new backing store. - dbSchemaVersion = latestKnownSchemaVersion; - putInt(transaction.get(), schemaVersionKey, dbSchemaVersion); - dbDataVersion = latestKnownDataVersion; - putInt(transaction.get(), dataVersionKey, dbDataVersion); - } else { - // Upgrade old backing store. - ASSERT(dbSchemaVersion <= latestKnownSchemaVersion); - if (dbSchemaVersion < 1) { - dbSchemaVersion = 1; - putInt(transaction.get(), schemaVersionKey, dbSchemaVersion); - const Vector<char> startKey = DatabaseNameKey::encodeMinKeyForOrigin(origin); - const Vector<char> stopKey = DatabaseNameKey::encodeStopKeyForOrigin(origin); - OwnPtr<LevelDBIterator> it = db->createIterator(); - for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) { - int64_t databaseId = 0; - found = false; - bool ok = getInt(transaction.get(), it->key(), databaseId, found); - if (!ok) { - INTERNAL_READ_ERROR(SetupMetadata); - return false; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(SetupMetadata); - return false; - } - Vector<char> intVersionKey = DatabaseMetaDataKey::encode(databaseId, DatabaseMetaDataKey::UserIntVersion); - putVarInt(transaction.get(), intVersionKey, IDBDatabaseMetadata::DefaultIntVersion); - } - } - if (dbSchemaVersion < 2) { - dbSchemaVersion = 2; - putInt(transaction.get(), schemaVersionKey, dbSchemaVersion); - dbDataVersion = SerializedScriptValue::wireFormatVersion(); - putInt(transaction.get(), dataVersionKey, dbDataVersion); - } - } - - // All new values will be written using this serialization version. - found = false; - ok = getInt(transaction.get(), dataVersionKey, dbDataVersion, found); - if (!ok) { - INTERNAL_READ_ERROR(SetupMetadata); - return false; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(SetupMetadata); - return false; - } - if (dbDataVersion < latestKnownDataVersion) { - dbDataVersion = latestKnownDataVersion; - putInt(transaction.get(), dataVersionKey, dbDataVersion); - } - - ASSERT(dbSchemaVersion == latestKnownSchemaVersion); - ASSERT(dbDataVersion == latestKnownDataVersion); - - if (!transaction->commit()) { - INTERNAL_WRITE_ERROR(SetupMetadata); - return false; - } - return true; -} - -template <typename DBOrTransaction> -WARN_UNUSED_RETURN static bool getMaxObjectStoreId(DBOrTransaction* db, int64_t databaseId, int64_t& maxObjectStoreId) -{ - const Vector<char> maxObjectStoreIdKey = DatabaseMetaDataKey::encode(databaseId, DatabaseMetaDataKey::MaxObjectStoreId); - bool ok = getMaxObjectStoreId(db, maxObjectStoreIdKey, maxObjectStoreId); - return ok; -} - -template <typename DBOrTransaction> -WARN_UNUSED_RETURN static bool getMaxObjectStoreId(DBOrTransaction* db, const Vector<char>& maxObjectStoreIdKey, int64_t& maxObjectStoreId) -{ - maxObjectStoreId = -1; - bool found = false; - bool ok = getInt(db, maxObjectStoreIdKey, maxObjectStoreId, found); - if (!ok) - return false; - if (!found) - maxObjectStoreId = 0; - - ASSERT(maxObjectStoreId >= 0); - return true; -} - -class DefaultLevelDBFactory : public LevelDBFactory { -public: - virtual PassOwnPtr<LevelDBDatabase> openLevelDB(const String& fileName, const LevelDBComparator* comparator) - { - return LevelDBDatabase::open(fileName, comparator); - } - virtual bool destroyLevelDB(const String& fileName) - { - return LevelDBDatabase::destroy(fileName); - } -}; - -IDBBackingStoreLevelDB::IDBBackingStoreLevelDB(const String& identifier, PassOwnPtr<LevelDBDatabase> db, PassOwnPtr<LevelDBComparator> comparator) - : m_identifier(identifier) - , m_db(db) - , m_comparator(comparator) - , m_weakFactory(this) -{ -} - -IDBBackingStoreLevelDB::~IDBBackingStoreLevelDB() -{ - // m_db's destructor uses m_comparator. The order of destruction is important. - m_db.clear(); - m_comparator.clear(); -} - -enum IDBLevelDBBackingStoreOpenResult { - IDBLevelDBBackingStoreOpenMemorySuccess, - IDBLevelDBBackingStoreOpenSuccess, - IDBLevelDBBackingStoreOpenFailedDirectory, - IDBLevelDBBackingStoreOpenFailedUnknownSchema, - IDBLevelDBBackingStoreOpenCleanupDestroyFailed, - IDBLevelDBBackingStoreOpenCleanupReopenFailed, - IDBLevelDBBackingStoreOpenCleanupReopenSuccess, - IDBLevelDBBackingStoreOpenFailedIOErrCheckingSchema, - IDBLevelDBBackingStoreOpenFailedUnknownErr, - IDBLevelDBBackingStoreOpenMemoryFailed, - IDBLevelDBBackingStoreOpenAttemptNonASCII, - IDBLevelDBBackingStoreOpenMax, -}; - -PassRefPtr<IDBBackingStoreLevelDB> IDBBackingStoreLevelDB::open(const SecurityOrigin& securityOrigin, const String& pathBaseArg, const String& fileIdentifier) -{ - DefaultLevelDBFactory levelDBFactory; - return IDBBackingStoreLevelDB::open(securityOrigin, pathBaseArg, fileIdentifier, &levelDBFactory); -} - -PassRefPtr<IDBBackingStoreLevelDB> IDBBackingStoreLevelDB::open(const SecurityOrigin& securityOrigin, const String& pathBaseArg, const String& fileIdentifier, LevelDBFactory* levelDBFactory) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::open"); - ASSERT(!pathBaseArg.isEmpty()); - String pathBase = pathBaseArg; - - OwnPtr<LevelDBComparator> comparator = adoptPtr(new Comparator()); - OwnPtr<LevelDBDatabase> db; - - if (!pathBase.containsOnlyASCII()) - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenAttemptNonASCII, IDBLevelDBBackingStoreOpenMax); - if (!makeAllDirectories(pathBase)) { - LOG_ERROR("Unable to create IndexedDB database path %s", pathBase.utf8().data()); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenFailedDirectory, IDBLevelDBBackingStoreOpenMax); - return PassRefPtr<IDBBackingStoreLevelDB>(); - } - - String path = pathByAppendingComponent(pathBase, securityOrigin.databaseIdentifier() + ".indexeddb.leveldb"); - - db = levelDBFactory->openLevelDB(path, comparator.get()); - if (db) { - bool known = false; - bool ok = isSchemaKnown(db.get(), known); - if (!ok) { - LOG_ERROR("IndexedDB had IO error checking schema, treating it as failure to open"); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenFailedIOErrCheckingSchema, IDBLevelDBBackingStoreOpenMax); - db.clear(); - } else if (!known) { - LOG_ERROR("IndexedDB backing store had unknown schema, treating it as failure to open"); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenFailedUnknownSchema, IDBLevelDBBackingStoreOpenMax); - db.clear(); - } - } - - if (db) - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenSuccess, IDBLevelDBBackingStoreOpenMax); - else { - LOG_ERROR("IndexedDB backing store open failed, attempting cleanup"); - bool success = levelDBFactory->destroyLevelDB(path); - if (!success) { - LOG_ERROR("IndexedDB backing store cleanup failed"); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenCleanupDestroyFailed, IDBLevelDBBackingStoreOpenMax); - return PassRefPtr<IDBBackingStoreLevelDB>(); - } - - LOG_ERROR("IndexedDB backing store cleanup succeeded, reopening"); - db = levelDBFactory->openLevelDB(path, comparator.get()); - if (!db) { - LOG_ERROR("IndexedDB backing store reopen after recovery failed"); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenCleanupReopenFailed, IDBLevelDBBackingStoreOpenMax); - return PassRefPtr<IDBBackingStoreLevelDB>(); - } - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenCleanupReopenSuccess, IDBLevelDBBackingStoreOpenMax); - } - - if (!db) { - ASSERT_NOT_REACHED(); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenFailedUnknownErr, IDBLevelDBBackingStoreOpenMax); - return PassRefPtr<IDBBackingStoreLevelDB>(); - } - - return create(fileIdentifier, db.release(), comparator.release()); -} - -PassRefPtr<IDBBackingStoreLevelDB> IDBBackingStoreLevelDB::openInMemory(const String& identifier) -{ - DefaultLevelDBFactory levelDBFactory; - return IDBBackingStoreLevelDB::openInMemory(identifier, &levelDBFactory); -} - -PassRefPtr<IDBBackingStoreLevelDB> IDBBackingStoreLevelDB::openInMemory(const String& identifier, LevelDBFactory*) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::openInMemory"); - - OwnPtr<LevelDBComparator> comparator = adoptPtr(new Comparator()); - OwnPtr<LevelDBDatabase> db = LevelDBDatabase::openInMemory(comparator.get()); - if (!db) { - LOG_ERROR("LevelDBDatabase::openInMemory failed."); - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenMemoryFailed, IDBLevelDBBackingStoreOpenMax); - return PassRefPtr<IDBBackingStoreLevelDB>(); - } - HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenMemorySuccess, IDBLevelDBBackingStoreOpenMax); - - return create(identifier, db.release(), comparator.release()); -} - -PassRefPtr<IDBBackingStoreLevelDB> IDBBackingStoreLevelDB::create(const String& identifier, PassOwnPtr<LevelDBDatabase> db, PassOwnPtr<LevelDBComparator> comparator) -{ - // FIXME: Handle comparator name changes. - RefPtr<IDBBackingStoreLevelDB> backingStore(adoptRef(new IDBBackingStoreLevelDB(identifier, db, comparator))); - - if (!setUpMetadata(backingStore->m_db.get(), identifier)) - return PassRefPtr<IDBBackingStoreLevelDB>(); - - return backingStore.release(); -} - -Vector<String> IDBBackingStoreLevelDB::getDatabaseNames() -{ - Vector<String> foundNames; - const Vector<char> startKey = DatabaseNameKey::encodeMinKeyForOrigin(m_identifier); - const Vector<char> stopKey = DatabaseNameKey::encodeStopKeyForOrigin(m_identifier); - - ASSERT(foundNames.isEmpty()); - - OwnPtr<LevelDBIterator> it = m_db->createIterator(); - 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(); - - DatabaseNameKey databaseNameKey; - p = DatabaseNameKey::decode(p, limit, &databaseNameKey); - ASSERT(p); - - foundNames.append(databaseNameKey.databaseName()); - } - return foundNames; -} - -bool IDBBackingStoreLevelDB::getIDBDatabaseMetaData(const String& name, IDBDatabaseMetadata* metadata, bool& found) -{ - const Vector<char> key = DatabaseNameKey::encode(m_identifier, name); - found = false; - - bool ok = getInt(m_db.get(), key, metadata->id, found); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - return false; - } - if (!found) - return true; - - // FIXME: The string version is no longer supported, so the levelDB ports should consider refactoring off of it. - String stringVersion; - ok = getString(m_db.get(), DatabaseMetaDataKey::encode(metadata->id, DatabaseMetaDataKey::UserVersion), stringVersion, found); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - return false; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(GetIDBDatabaseMetaData); - return false; - } - - int64_t version; - ok = getVarInt(m_db.get(), DatabaseMetaDataKey::encode(metadata->id, DatabaseMetaDataKey::UserIntVersion), version, found); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - return false; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(GetIDBDatabaseMetaData); - return false; - } - - // FIXME: The versioning semantics have changed since this original code was written, and what was once a negative number - // stored in the database is no longer a valid version. - if (version < 0) - version = 0; - metadata->version = version; - - ok = getMaxObjectStoreId(m_db.get(), metadata->id, metadata->maxObjectStoreId); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - return false; - } - - return true; -} - -void IDBBackingStoreLevelDB::getOrEstablishIDBDatabaseMetadata(const String& name, std::function<void (const IDBDatabaseMetadata&, bool success)> metadataFunction) -{ - const Vector<char> key = DatabaseNameKey::encode(m_identifier, name); - bool found = false; - - IDBDatabaseMetadata resultMetadata; - - bool ok = getInt(m_db.get(), key, resultMetadata.id, found); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - metadataFunction(resultMetadata, false); - return; - } - - if (!found) { - resultMetadata.name = name; - resultMetadata.version = IDBDatabaseMetadata::DefaultIntVersion; - - metadataFunction(resultMetadata, createIDBDatabaseMetaData(resultMetadata)); - return; - } - - int64_t version; - ok = getVarInt(m_db.get(), DatabaseMetaDataKey::encode(resultMetadata.id, DatabaseMetaDataKey::UserIntVersion), version, found); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - metadataFunction(resultMetadata, false); - return; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(GetIDBDatabaseMetaData); - metadataFunction(resultMetadata, false); - return; - } - - // FIXME: The versioning semantics have changed since this original code was written, and what was once a negative number - // stored in the database is no longer a valid version. - if (version < 0) - version = 0; - resultMetadata.version = version; - - ok = getMaxObjectStoreId(m_db.get(), resultMetadata.id, resultMetadata.maxObjectStoreId); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - metadataFunction(resultMetadata, false); - return; - } - - ok = getObjectStores(resultMetadata.id, &resultMetadata.objectStores); - if (!ok) { - INTERNAL_READ_ERROR(GetIDBDatabaseMetaData); - metadataFunction(resultMetadata, false); - return; - } - - metadataFunction(resultMetadata, true); -} - -WARN_UNUSED_RETURN static bool getNewDatabaseId(LevelDBDatabase* db, int64_t& newId) -{ - RefPtr<LevelDBTransaction> transaction = LevelDBTransaction::create(db); - - newId = -1; - int64_t maxDatabaseId = -1; - bool found = false; - bool ok = getInt(transaction.get(), MaxDatabaseIdKey::encode(), maxDatabaseId, found); - if (!ok) { - INTERNAL_READ_ERROR(GetNewDatabaseId); - return false; - } - if (!found) - maxDatabaseId = 0; - - ASSERT(maxDatabaseId >= 0); - - int64_t databaseId = maxDatabaseId + 1; - putInt(transaction.get(), MaxDatabaseIdKey::encode(), databaseId); - if (!transaction->commit()) { - INTERNAL_WRITE_ERROR(GetNewDatabaseId); - return false; - } - newId = databaseId; - return true; -} - -// FIXME: LevelDB needs to support uint64_t as the version type. -bool IDBBackingStoreLevelDB::createIDBDatabaseMetaData(IDBDatabaseMetadata& metadata) -{ - bool ok = getNewDatabaseId(m_db.get(), metadata.id); - if (!ok) - return false; - ASSERT(metadata.id >= 0); - - RefPtr<LevelDBTransaction> transaction = LevelDBTransaction::create(m_db.get()); - putInt(transaction.get(), DatabaseNameKey::encode(m_identifier, metadata.name), metadata.id); - putVarInt(transaction.get(), DatabaseMetaDataKey::encode(metadata.id, DatabaseMetaDataKey::UserIntVersion), metadata.version); - if (!transaction->commit()) { - INTERNAL_WRITE_ERROR(CreateIDBDatabaseMetaData); - return false; - } - return true; -} - -bool IDBBackingStoreLevelDB::updateIDBDatabaseVersion(IDBBackingStoreTransactionLevelDB& transaction, int64_t rowId, uint64_t version) -{ - if (version == IDBDatabaseMetadata::NoIntVersion) - version = IDBDatabaseMetadata::DefaultIntVersion; - putVarInt(IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction), DatabaseMetaDataKey::encode(rowId, DatabaseMetaDataKey::UserIntVersion), version); - return true; -} - -static void deleteRange(LevelDBTransaction* transaction, const Vector<char>& begin, const Vector<char>& end) -{ - OwnPtr<LevelDBIterator> it = transaction->createIterator(); - for (it->seek(begin); it->isValid() && compareKeys(it->key(), end) < 0; it->next()) - transaction->remove(it->key()); -} - -void IDBBackingStoreLevelDB::deleteDatabase(const String& name, std::function<void (bool success)> boolCallbackFunction) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::deleteDatabase"); - OwnPtr<LevelDBWriteOnlyTransaction> transaction = LevelDBWriteOnlyTransaction::create(m_db.get()); - - IDBDatabaseMetadata metadata; - bool success = false; - bool ok = getIDBDatabaseMetaData(name, &metadata, success); - if (!ok) { - boolCallbackFunction(false); - return; - } - - if (!success) { - boolCallbackFunction(true); - return; - } - - const Vector<char> startKey = DatabaseMetaDataKey::encode(metadata.id, DatabaseMetaDataKey::OriginName); - const Vector<char> stopKey = DatabaseMetaDataKey::encode(metadata.id + 1, DatabaseMetaDataKey::OriginName); - OwnPtr<LevelDBIterator> it = m_db->createIterator(); - for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) - transaction->remove(it->key()); - - const Vector<char> key = DatabaseNameKey::encode(m_identifier, name); - transaction->remove(key); - - if (!transaction->commit()) { - INTERNAL_WRITE_ERROR(DeleteDatabase); - boolCallbackFunction(false); - return; - } - boolCallbackFunction(true); -} - -static bool checkObjectStoreAndMetaDataType(const LevelDBIterator* it, const Vector<char>& stopKey, int64_t objectStoreId, int64_t metaDataType) -{ - if (!it->isValid() || compareKeys(it->key(), stopKey) >= 0) - return false; - - ObjectStoreMetaDataKey metaDataKey; - const char* p = ObjectStoreMetaDataKey::decode(it->key().begin(), it->key().end(), &metaDataKey); - ASSERT_UNUSED(p, p); - if (metaDataKey.objectStoreId() != objectStoreId) - return false; - if (metaDataKey.metaDataType() != metaDataType) - return false; - return true; -} - -// FIXME: This should do some error handling rather than plowing ahead when bad data is encountered. -bool IDBBackingStoreLevelDB::getObjectStores(int64_t databaseId, IDBDatabaseMetadata::ObjectStoreMap* objectStores) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::getObjectStores"); - if (!KeyPrefix::isValidDatabaseId(databaseId)) - return false; - const Vector<char> startKey = ObjectStoreMetaDataKey::encode(databaseId, 1, 0); - const Vector<char> stopKey = ObjectStoreMetaDataKey::encodeMaxKey(databaseId); - - ASSERT(objectStores->isEmpty()); - - OwnPtr<LevelDBIterator> it = m_db->createIterator(); - it->seek(startKey); - while (it->isValid() && compareKeys(it->key(), stopKey) < 0) { - const char* p = it->key().begin(); - const char* limit = it->key().end(); - - ObjectStoreMetaDataKey metaDataKey; - p = ObjectStoreMetaDataKey::decode(p, limit, &metaDataKey); - ASSERT(p); - if (metaDataKey.metaDataType() != ObjectStoreMetaDataKey::Name) { - INTERNAL_CONSISTENCY_ERROR(GetObjectStores); - // Possible stale metadata, but don't fail the load. - it->next(); - continue; - } - - int64_t objectStoreId = metaDataKey.objectStoreId(); - - // FIXME: Do this by direct key lookup rather than iteration, to simplify. - String objectStoreName = decodeString(it->value().begin(), it->value().end()); - - it->next(); - if (!checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::KeyPath)) { - INTERNAL_CONSISTENCY_ERROR(GetObjectStores); - break; - } - IDBKeyPath keyPath = decodeIDBKeyPath(it->value().begin(), it->value().end()); - - it->next(); - if (!checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::AutoIncrement)) { - INTERNAL_CONSISTENCY_ERROR(GetObjectStores); - break; - } - bool autoIncrement = decodeBool(it->value().begin(), it->value().end()); - - it->next(); // Is evicatble. - if (!checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::Evictable)) { - INTERNAL_CONSISTENCY_ERROR(GetObjectStores); - break; - } - - it->next(); // Last version. - if (!checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::LastVersion)) { - INTERNAL_CONSISTENCY_ERROR(GetObjectStores); - break; - } - - it->next(); // Maximum index id allocated. - if (!checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::MaxIndexId)) { - INTERNAL_CONSISTENCY_ERROR(GetObjectStores); - break; - } - int64_t maxIndexId = decodeInt(it->value().begin(), it->value().end()); - - it->next(); // [optional] has key path (is not null) - if (checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::HasKeyPath)) { - bool hasKeyPath = decodeBool(it->value().begin(), it->value().end()); - // This check accounts for two layers of legacy coding: - // (1) Initially, hasKeyPath was added to distinguish null vs. string. - // (2) Later, null vs. string vs. array was stored in the keyPath itself. - // So this check is only relevant for string-type keyPaths. - if (!hasKeyPath && (keyPath.type() == IDBKeyPath::StringType && !keyPath.string().isEmpty())) { - INTERNAL_CONSISTENCY_ERROR(GetObjectStores); - break; - } - if (!hasKeyPath) - keyPath = IDBKeyPath(); - it->next(); - } - - int64_t keyGeneratorCurrentNumber = -1; - if (checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber)) { - keyGeneratorCurrentNumber = decodeInt(it->value().begin(), it->value().end()); - // FIXME: Return keyGeneratorCurrentNumber, cache in object store, and write lazily to backing store. - // For now, just assert that if it was written it was valid. - ASSERT_UNUSED(keyGeneratorCurrentNumber, keyGeneratorCurrentNumber >= KeyGeneratorInitialNumber); - it->next(); - } - - IDBObjectStoreMetadata metadata(objectStoreName, objectStoreId, keyPath, autoIncrement, maxIndexId); - if (!getIndexes(databaseId, objectStoreId, &metadata.indexes)) - return false; - objectStores->set(objectStoreId, metadata); - } - return true; -} - -WARN_UNUSED_RETURN static bool setMaxObjectStoreId(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId) -{ - const Vector<char> maxObjectStoreIdKey = DatabaseMetaDataKey::encode(databaseId, DatabaseMetaDataKey::MaxObjectStoreId); - int64_t maxObjectStoreId = -1; - bool ok = getMaxObjectStoreId(transaction, maxObjectStoreIdKey, maxObjectStoreId); - if (!ok) { - INTERNAL_READ_ERROR(SetMaxObjectStoreId); - return false; - } - - if (objectStoreId <= maxObjectStoreId) { - INTERNAL_CONSISTENCY_ERROR(SetMaxObjectStoreId); - return false; - } - putInt(transaction, maxObjectStoreIdKey, objectStoreId); - return true; -} - -bool IDBBackingStoreLevelDB::createObjectStore(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, const String& name, const IDBKeyPath& keyPath, bool autoIncrement) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::createObjectStore"); - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - if (!setMaxObjectStoreId(levelDBTransaction, databaseId, objectStoreId)) - return false; - - const Vector<char> nameKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::Name); - const Vector<char> keyPathKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyPath); - const Vector<char> autoIncrementKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::AutoIncrement); - const Vector<char> evictableKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::Evictable); - const Vector<char> lastVersionKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::LastVersion); - const Vector<char> maxIndexIdKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::MaxIndexId); - const Vector<char> hasKeyPathKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::HasKeyPath); - const Vector<char> keyGeneratorCurrentNumberKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber); - const Vector<char> namesKey = ObjectStoreNamesKey::encode(databaseId, name); - - putString(levelDBTransaction, nameKey, name); - putIDBKeyPath(levelDBTransaction, keyPathKey, keyPath); - putInt(levelDBTransaction, autoIncrementKey, autoIncrement); - putInt(levelDBTransaction, evictableKey, false); - putInt(levelDBTransaction, lastVersionKey, 1); - putInt(levelDBTransaction, maxIndexIdKey, MinimumIndexId); - putBool(levelDBTransaction, hasKeyPathKey, !keyPath.isNull()); - putInt(levelDBTransaction, keyGeneratorCurrentNumberKey, KeyGeneratorInitialNumber); - putInt(levelDBTransaction, namesKey, objectStoreId); - return true; -} - -bool IDBBackingStoreLevelDB::deleteObjectStore(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::deleteObjectStore"); - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - - String objectStoreName; - bool found = false; - bool ok = getString(levelDBTransaction, ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::Name), objectStoreName, found); - if (!ok) { - INTERNAL_READ_ERROR(DeleteObjectStore); - return false; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(DeleteObjectStore); - return false; - } - - deleteRange(levelDBTransaction, ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 0), ObjectStoreMetaDataKey::encodeMaxKey(databaseId, objectStoreId)); - - levelDBTransaction->remove(ObjectStoreNamesKey::encode(databaseId, objectStoreName)); - - deleteRange(levelDBTransaction, IndexFreeListKey::encode(databaseId, objectStoreId, 0), IndexFreeListKey::encodeMaxKey(databaseId, objectStoreId)); - deleteRange(levelDBTransaction, IndexMetaDataKey::encode(databaseId, objectStoreId, 0, 0), IndexMetaDataKey::encodeMaxKey(databaseId, objectStoreId)); - - return clearObjectStore(transaction, databaseId, objectStoreId); -} - -bool IDBBackingStoreLevelDB::getRecord(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, const IDBKey& key, Vector<char>& record) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::getRecord"); - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - - const Vector<char> leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key); - Vector<char> data; - - record.clear(); - - bool found = false; - bool ok = levelDBTransaction->safeGet(leveldbKey, data, found); - if (!ok) { - INTERNAL_READ_ERROR(GetRecord); - return false; - } - if (!found) - return true; - - int64_t version; - const char* p = decodeVarInt(data.begin(), data.end(), version); - if (!p) { - INTERNAL_READ_ERROR(GetRecord); - return false; - } - - record.appendRange(p, static_cast<const char*>(data.end())); - return true; -} - -WARN_UNUSED_RETURN static bool getNewVersionNumber(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t& newVersionNumber) -{ - const Vector<char> lastVersionKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::LastVersion); - - newVersionNumber = -1; - int64_t lastVersion = -1; - bool found = false; - bool ok = getInt(transaction, lastVersionKey, lastVersion, found); - if (!ok) { - INTERNAL_READ_ERROR(GetNewVersionNumber); - return false; - } - if (!found) - lastVersion = 0; - - ASSERT(lastVersion >= 0); - - int64_t version = lastVersion + 1; - putInt(transaction, lastVersionKey, version); - - ASSERT(version > lastVersion); // FIXME: Think about how we want to handle the overflow scenario. - - newVersionNumber = version; - return true; -} - -bool IDBBackingStoreLevelDB::putRecord(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, const IDBKey& key, PassRefPtr<SharedBuffer> prpValue, IDBRecordIdentifier* recordIdentifier) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::putRecord"); - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - ASSERT(key.isValid()); - - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - int64_t version = -1; - bool ok = getNewVersionNumber(levelDBTransaction, databaseId, objectStoreId, version); - if (!ok) - return false; - ASSERT(version >= 0); - const Vector<char> objectStoredataKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key); - - Vector<char> v; - v.appendVector(encodeVarInt(version)); - RefPtr<SharedBuffer> value = prpValue; - ASSERT(value); - v.append(value->data(), value->size()); - - levelDBTransaction->put(objectStoredataKey, v); - - const Vector<char> existsEntryKey = ExistsEntryKey::encode(databaseId, objectStoreId, key); - levelDBTransaction->put(existsEntryKey, encodeInt(version)); - - recordIdentifier->reset(encodeIDBKey(key), version); - return true; -} - -bool IDBBackingStoreLevelDB::clearObjectStore(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::clearObjectStore"); - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - const Vector<char> startKey = KeyPrefix(databaseId, objectStoreId).encode(); - const Vector<char> stopKey = KeyPrefix(databaseId, objectStoreId + 1).encode(); - - deleteRange(levelDBTransaction, startKey, stopKey); - return true; -} - -bool IDBBackingStoreLevelDB::deleteRecord(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, const IDBRecordIdentifier& recordIdentifier) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::deleteRecord"); - - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - - const Vector<char> objectStoreDataKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, recordIdentifier.encodedPrimaryKey()); - levelDBTransaction->remove(objectStoreDataKey); - - const Vector<char> existsEntryKey = ExistsEntryKey::encode(databaseId, objectStoreId, recordIdentifier.encodedPrimaryKey()); - levelDBTransaction->remove(existsEntryKey); - return true; -} - - -bool IDBBackingStoreLevelDB::getKeyGeneratorCurrentNumber(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t& keyGeneratorCurrentNumber) -{ - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - - const Vector<char> keyGeneratorCurrentNumberKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber); - - keyGeneratorCurrentNumber = -1; - Vector<char> data; - - bool found = false; - bool ok = levelDBTransaction->safeGet(keyGeneratorCurrentNumberKey, data, found); - if (!ok) { - INTERNAL_READ_ERROR(GetKeyGeneratorCurrentNumber); - return false; - } - if (found) - keyGeneratorCurrentNumber = decodeInt(data.begin(), data.end()); - else { - // Previously, the key generator state was not stored explicitly but derived from the - // maximum numeric key present in existing data. This violates the spec as the data may - // be cleared but the key generator state must be preserved. - const Vector<char> startKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, minIDBKey()); - const Vector<char> stopKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, maxIDBKey()); - - OwnPtr<LevelDBIterator> it = levelDBTransaction->createIterator(); - int64_t maxNumericKey = 0; - - 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; - } - } - - keyGeneratorCurrentNumber = maxNumericKey + 1; - } - - return keyGeneratorCurrentNumber; -} - -bool IDBBackingStoreLevelDB::maybeUpdateKeyGeneratorCurrentNumber(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t newNumber, bool checkCurrent) -{ - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - - if (checkCurrent) { - int64_t currentNumber; - bool ok = getKeyGeneratorCurrentNumber(transaction, databaseId, objectStoreId, currentNumber); - if (!ok) - return false; - if (newNumber <= currentNumber) - return true; - } - - const Vector<char> keyGeneratorCurrentNumberKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber); - putInt(levelDBTransaction, keyGeneratorCurrentNumberKey, newNumber); - return true; -} - -bool IDBBackingStoreLevelDB::keyExistsInObjectStore(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, const IDBKey& key, RefPtr<IDBRecordIdentifier>& foundIDBRecordIdentifier) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::keyExistsInObjectStore"); - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - bool found = false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - const Vector<char> leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key); - Vector<char> data; - - bool ok = levelDBTransaction->safeGet(leveldbKey, data, found); - if (!ok) { - INTERNAL_READ_ERROR(KeyExistsInObjectStore); - return false; - } - if (!found) - return true; - - int64_t version; - if (!decodeVarInt(data.begin(), data.end(), version)) - return false; - - foundIDBRecordIdentifier = IDBRecordIdentifier::create(encodeIDBKey(key), version); - return true; -} - -static bool checkIndexAndMetaDataKey(const LevelDBIterator* it, const Vector<char>& stopKey, int64_t indexId, unsigned char metaDataType) -{ - if (!it->isValid() || compareKeys(it->key(), stopKey) >= 0) - return false; - - IndexMetaDataKey metaDataKey; - const char* p = IndexMetaDataKey::decode(it->key().begin(), it->key().end(), &metaDataKey); - ASSERT_UNUSED(p, p); - if (metaDataKey.indexId() != indexId) - return false; - if (metaDataKey.metaDataType() != metaDataType) - return false; - return true; -} - - -// FIXME: This should do some error handling rather than plowing ahead when bad data is encountered. -bool IDBBackingStoreLevelDB::getIndexes(int64_t databaseId, int64_t objectStoreId, IDBObjectStoreMetadata::IndexMap* indexes) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::getIndexes"); - if (!KeyPrefix::validIds(databaseId, objectStoreId)) - return false; - const Vector<char> startKey = IndexMetaDataKey::encode(databaseId, objectStoreId, 0, 0); - const Vector<char> stopKey = IndexMetaDataKey::encode(databaseId, objectStoreId + 1, 0, 0); - - ASSERT(indexes->isEmpty()); - - OwnPtr<LevelDBIterator> it = m_db->createIterator(); - it->seek(startKey); - while (it->isValid() && compareKeys(it->key(), stopKey) < 0) { - const char* p = it->key().begin(); - const char* limit = it->key().end(); - - IndexMetaDataKey metaDataKey; - p = IndexMetaDataKey::decode(p, limit, &metaDataKey); - ASSERT(p); - if (metaDataKey.metaDataType() != IndexMetaDataKey::Name) { - INTERNAL_CONSISTENCY_ERROR(GetIndexes); - // Possible stale metadata due to http://webkit.org/b/85557 but don't fail the load. - it->next(); - continue; - } - - // FIXME: Do this by direct key lookup rather than iteration, to simplify. - int64_t indexId = metaDataKey.indexId(); - String indexName = decodeString(it->value().begin(), it->value().end()); - - it->next(); // unique flag - if (!checkIndexAndMetaDataKey(it.get(), stopKey, indexId, IndexMetaDataKey::Unique)) { - INTERNAL_CONSISTENCY_ERROR(GetIndexes); - break; - } - bool indexUnique = decodeBool(it->value().begin(), it->value().end()); - - it->next(); // keyPath - if (!checkIndexAndMetaDataKey(it.get(), stopKey, indexId, IndexMetaDataKey::KeyPath)) { - INTERNAL_CONSISTENCY_ERROR(GetIndexes); - break; - } - IDBKeyPath keyPath = decodeIDBKeyPath(it->value().begin(), it->value().end()); - - it->next(); // [optional] multiEntry flag - bool indexMultiEntry = false; - if (checkIndexAndMetaDataKey(it.get(), stopKey, indexId, IndexMetaDataKey::MultiEntry)) { - indexMultiEntry = decodeBool(it->value().begin(), it->value().end()); - it->next(); - } - - indexes->set(indexId, IDBIndexMetadata(indexName, indexId, keyPath, indexUnique, indexMultiEntry)); - } - return true; -} - -WARN_UNUSED_RETURN static bool setMaxIndexId(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId) -{ - int64_t maxIndexId = -1; - const Vector<char> maxIndexIdKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::MaxIndexId); - bool found = false; - bool ok = getInt(transaction, maxIndexIdKey, maxIndexId, found); - if (!ok) { - INTERNAL_READ_ERROR(SetMaxIndexId); - return false; - } - if (!found) - maxIndexId = MinimumIndexId; - - if (indexId <= maxIndexId) { - INTERNAL_CONSISTENCY_ERROR(SetMaxIndexId); - return false; - } - - putInt(transaction, maxIndexIdKey, indexId); - return true; -} - -bool IDBBackingStoreLevelDB::createIndex(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath& keyPath, bool isUnique, bool isMultiEntry) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::createIndex"); - if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - if (!setMaxIndexId(levelDBTransaction, databaseId, objectStoreId, indexId)) - return false; - - const Vector<char> nameKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, IndexMetaDataKey::Name); - const Vector<char> uniqueKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, IndexMetaDataKey::Unique); - const Vector<char> keyPathKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, IndexMetaDataKey::KeyPath); - const Vector<char> multiEntryKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, IndexMetaDataKey::MultiEntry); - - putString(levelDBTransaction, nameKey, name); - putBool(levelDBTransaction, uniqueKey, isUnique); - putIDBKeyPath(levelDBTransaction, keyPathKey, keyPath); - putBool(levelDBTransaction, multiEntryKey, isMultiEntry); - return true; -} - -bool IDBBackingStoreLevelDB::deleteIndex(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::deleteIndex"); - if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId)) - return false; - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - - const Vector<char> indexMetaDataStart = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, 0); - const Vector<char> indexMetaDataEnd = IndexMetaDataKey::encodeMaxKey(databaseId, objectStoreId, indexId); - deleteRange(levelDBTransaction, indexMetaDataStart, indexMetaDataEnd); - - const Vector<char> indexDataStart = IndexDataKey::encodeMinKey(databaseId, objectStoreId, indexId); - const Vector<char> indexDataEnd = IndexDataKey::encodeMaxKey(databaseId, objectStoreId, indexId); - deleteRange(levelDBTransaction, indexDataStart, indexDataEnd); - return true; -} - -bool IDBBackingStoreLevelDB::putIndexDataForRecord(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, const IDBRecordIdentifier* recordIdentifier) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::putIndexDataForRecord"); - ASSERT(key.isValid()); - ASSERT(recordIdentifier); - if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId)) - return false; - - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - const Vector<char> indexDataKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, encodeIDBKey(key), recordIdentifier->encodedPrimaryKey()); - - Vector<char> data; - data.appendVector(encodeVarInt(recordIdentifier->version())); - data.appendVector(recordIdentifier->encodedPrimaryKey()); - - levelDBTransaction->put(indexDataKey, data); - return true; -} - -static bool findGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction, const Vector<char>& target, Vector<char>& foundKey) -{ - OwnPtr<LevelDBIterator> it = transaction->createIterator(); - it->seek(target); - - if (!it->isValid()) { - it->seekToLast(); - if (!it->isValid()) - return false; - } - - while (IDBBackingStoreLevelDB::compareIndexKeys(it->key(), target) > 0) { - it->prev(); - if (!it->isValid()) - return false; - } - - do { - foundKey.clear(); - foundKey.append(it->key().begin(), it->key().end() - it->key().begin()); - - // There can be several index keys that compare equal. We want the last one. - it->next(); - } while (it->isValid() && !IDBBackingStoreLevelDB::compareIndexKeys(it->key(), target)); - - return true; -} - -static bool versionExists(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t version, const Vector<char>& encodedPrimaryKey, bool& exists) -{ - const Vector<char> key = ExistsEntryKey::encode(databaseId, objectStoreId, encodedPrimaryKey); - Vector<char> data; - - bool ok = transaction->safeGet(key, data, exists); - if (!ok) { - INTERNAL_READ_ERROR(VersionExists); - return false; - } - if (!exists) - return true; - - exists = (decodeInt(data.begin(), data.end()) == version); - return true; -} - -bool IDBBackingStoreLevelDB::findKeyInIndex(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, Vector<char>& foundEncodedPrimaryKey, bool& found) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::findKeyInIndex"); - ASSERT(KeyPrefix::validIds(databaseId, objectStoreId, indexId)); - - ASSERT(foundEncodedPrimaryKey.isEmpty()); - found = false; - - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - const Vector<char> leveldbKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, key); - OwnPtr<LevelDBIterator> it = levelDBTransaction->createIterator(); - it->seek(leveldbKey); - - for (;;) { - if (!it->isValid()) - return true; - if (compareIndexKeys(it->key(), leveldbKey) > 0) - return true; - - int64_t version; - const char* p = decodeVarInt(it->value().begin(), it->value().end(), version); - if (!p) { - INTERNAL_READ_ERROR(FindKeyInIndex); - return false; - } - foundEncodedPrimaryKey.append(p, it->value().end() - p); - - bool exists = false; - bool ok = versionExists(levelDBTransaction, databaseId, objectStoreId, version, foundEncodedPrimaryKey, exists); - if (!ok) - return false; - if (!exists) { - // Delete stale index data entry and continue. - levelDBTransaction->remove(it->key()); - it->next(); - continue; - } - found = true; - return true; - } -} - -bool IDBBackingStoreLevelDB::getPrimaryKeyViaIndex(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, RefPtr<IDBKey>& primaryKey) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::getPrimaryKeyViaIndex"); - if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId)) - return false; - - bool found = false; - Vector<char> foundEncodedPrimaryKey; - bool ok = findKeyInIndex(transaction, databaseId, objectStoreId, indexId, key, foundEncodedPrimaryKey, found); - if (!ok) { - INTERNAL_READ_ERROR(GetPrimaryKeyViaIndex); - return false; - } - if (found) { - decodeIDBKey(foundEncodedPrimaryKey.begin(), foundEncodedPrimaryKey.end(), primaryKey); - return true; - } - - return true; -} - -bool IDBBackingStoreLevelDB::keyExistsInIndex(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& indexKey, RefPtr<IDBKey>& foundPrimaryKey, bool& exists) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::keyExistsInIndex"); - if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId)) - return false; - - exists = false; - Vector<char> foundEncodedPrimaryKey; - bool ok = findKeyInIndex(transaction, databaseId, objectStoreId, indexId, indexKey, foundEncodedPrimaryKey, exists); - if (!ok) { - INTERNAL_READ_ERROR(KeyExistsInIndex); - return false; - } - if (!exists) - return true; - - decodeIDBKey(foundEncodedPrimaryKey.begin(), foundEncodedPrimaryKey.end(), foundPrimaryKey); - return true; -} - - -bool IDBBackingStoreLevelDB::makeIndexWriters(int64_t transactionID, int64_t databaseID, const IDBObjectStoreMetadata& objectStore, IDBKey& primaryKey, bool keyWasGenerated, const Vector<int64_t>& indexIDs, const Vector<Vector<RefPtr<IDBKey>>>& indexKeys, Vector<RefPtr<IDBIndexWriterLevelDB>>& indexWriters, String* errorMessage, bool& completed) -{ - ASSERT(indexIDs.size() == indexKeys.size()); - completed = false; - - HashMap<int64_t, IndexKeys> indexKeyMap; - for (size_t i = 0; i < indexIDs.size(); ++i) - indexKeyMap.add(indexIDs[i], indexKeys[i]); - - for (IDBObjectStoreMetadata::IndexMap::const_iterator it = objectStore.indexes.begin(); it != objectStore.indexes.end(); ++it) { - const IDBIndexMetadata& index = it->value; - - IndexKeys keys = indexKeyMap.get(it->key); - // If the objectStore is using autoIncrement, then any indexes with an identical keyPath need to also use the primary (generated) key as a key. - if (keyWasGenerated && (index.keyPath == objectStore.keyPath)) - keys.append(&primaryKey); - - RefPtr<IDBIndexWriterLevelDB> indexWriter = IDBIndexWriterLevelDB::create(index, keys); - bool canAddKeys = false; - ASSERT(m_backingStoreTransactions.contains(transactionID)); - bool backingStoreSuccess = indexWriter->verifyIndexKeys(*this, *m_backingStoreTransactions.get(transactionID), databaseID, objectStore.id, index.id, canAddKeys, &primaryKey, errorMessage); - if (!backingStoreSuccess) - return false; - if (!canAddKeys) - return true; - - indexWriters.append(indexWriter.release()); - } - - completed = true; - return true; -} - -PassRefPtr<IDBKey> IDBBackingStoreLevelDB::generateKey(IDBTransactionBackend& transaction, int64_t databaseId, int64_t objectStoreId) -{ - const int64_t maxGeneratorValue = 9007199254740992LL; // Maximum integer storable as ECMAScript number. - int64_t currentNumber; - ASSERT(m_backingStoreTransactions.contains(transaction.id())); - bool ok = getKeyGeneratorCurrentNumber(*m_backingStoreTransactions.get(transaction.id()), databaseId, objectStoreId, currentNumber); - if (!ok) { - LOG_ERROR("Failed to getKeyGeneratorCurrentNumber"); - return IDBKey::createInvalid(); - } - if (currentNumber < 0 || currentNumber > maxGeneratorValue) - return IDBKey::createInvalid(); - - return IDBKey::createNumber(currentNumber); -} - - -bool IDBBackingStoreLevelDB::updateKeyGenerator(IDBTransactionBackend& transaction, int64_t databaseId, int64_t objectStoreId, const IDBKey& key, bool checkCurrent) -{ - ASSERT(key.type() == IDBKey::NumberType); - ASSERT(m_backingStoreTransactions.contains(transaction.id())); - - return maybeUpdateKeyGeneratorCurrentNumber(*m_backingStoreTransactions.get(transaction.id()), databaseId, objectStoreId, static_cast<int64_t>(floor(key.number())) + 1, checkCurrent); -} - -class ObjectStoreKeyCursorImpl : public IDBBackingStoreCursorLevelDB { -public: - static PassRefPtr<ObjectStoreKeyCursorImpl> create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - { - return adoptRef(new ObjectStoreKeyCursorImpl(cursorID, transaction, cursorOptions)); - } - - virtual PassRefPtr<IDBBackingStoreCursorLevelDB> clone() - { - return adoptRef(new ObjectStoreKeyCursorImpl(this)); - } - - // IDBBackingStoreCursorLevelDB - virtual PassRefPtr<SharedBuffer> value() const override { ASSERT_NOT_REACHED(); return 0; } - virtual bool loadCurrentRow() override; - -protected: - virtual Vector<char> encodeKey(const IDBKey &key) - { - return ObjectStoreDataKey::encode(m_cursorOptions.databaseId, m_cursorOptions.objectStoreId, key); - } - -private: - ObjectStoreKeyCursorImpl(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - : IDBBackingStoreCursorLevelDB(cursorID, transaction, cursorOptions) - { - } - - ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other) - : IDBBackingStoreCursorLevelDB(other) - { - } -}; - -bool ObjectStoreKeyCursorImpl::loadCurrentRow() -{ - const char* keyPosition = m_iterator->key().begin(); - const char* keyLimit = m_iterator->key().end(); - - ObjectStoreDataKey objectStoreDataKey; - keyPosition = ObjectStoreDataKey::decode(keyPosition, keyLimit, &objectStoreDataKey); - if (!keyPosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - m_currentKey = objectStoreDataKey.userKey(); - - int64_t version; - const char* valuePosition = decodeVarInt(m_iterator->value().begin(), m_iterator->value().end(), version); - if (!valuePosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - // FIXME: This re-encodes what was just decoded; try and optimize. - m_recordIdentifier->reset(encodeIDBKey(*m_currentKey), version); - - return true; -} - -class ObjectStoreCursorImpl : public IDBBackingStoreCursorLevelDB { -public: - static PassRefPtr<ObjectStoreCursorImpl> create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - { - return adoptRef(new ObjectStoreCursorImpl(cursorID, transaction, cursorOptions)); - } - - virtual PassRefPtr<IDBBackingStoreCursorLevelDB> clone() - { - return adoptRef(new ObjectStoreCursorImpl(this)); - } - - // IDBBackingStoreCursorLevelDB - virtual PassRefPtr<SharedBuffer> value() const override { return m_currentValue; } - virtual bool loadCurrentRow() override; - -protected: - virtual Vector<char> encodeKey(const IDBKey &key) - { - return ObjectStoreDataKey::encode(m_cursorOptions.databaseId, m_cursorOptions.objectStoreId, key); - } - -private: - ObjectStoreCursorImpl(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - : IDBBackingStoreCursorLevelDB(cursorID, transaction, cursorOptions) - { - } - - ObjectStoreCursorImpl(const ObjectStoreCursorImpl* other) - : IDBBackingStoreCursorLevelDB(other) - , m_currentValue(other->m_currentValue) - { - } - - RefPtr<SharedBuffer> m_currentValue; -}; - -bool ObjectStoreCursorImpl::loadCurrentRow() -{ - const char* keyPosition = m_iterator->key().begin(); - const char* keyLimit = m_iterator->key().end(); - - ObjectStoreDataKey objectStoreDataKey; - keyPosition = ObjectStoreDataKey::decode(keyPosition, keyLimit, &objectStoreDataKey); - if (!keyPosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - m_currentKey = objectStoreDataKey.userKey(); - - int64_t version; - const char* valuePosition = decodeVarInt(m_iterator->value().begin(), m_iterator->value().end(), version); - if (!valuePosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - // FIXME: This re-encodes what was just decoded; try and optimize. - m_recordIdentifier->reset(encodeIDBKey(*m_currentKey), version); - - Vector<char> value; - value.append(valuePosition, m_iterator->value().end() - valuePosition); - m_currentValue = SharedBuffer::adoptVector(value); - return true; -} - -class IndexKeyCursorImpl final : public IDBBackingStoreCursorLevelDB { -public: - static PassRefPtr<IndexKeyCursorImpl> create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - { - return adoptRef(new IndexKeyCursorImpl(cursorID, transaction, cursorOptions)); - } - - virtual PassRefPtr<IDBBackingStoreCursorLevelDB> clone() - { - return adoptRef(new IndexKeyCursorImpl(this)); - } - - // IDBBackingStoreCursorLevelDB - virtual PassRefPtr<SharedBuffer> value() const override { ASSERT_NOT_REACHED(); return 0; } - virtual PassRefPtr<IDBKey> primaryKey() const override { return m_primaryKey; } - virtual const IDBRecordIdentifier& recordIdentifier() const override { ASSERT_NOT_REACHED(); return *m_recordIdentifier; } - virtual bool loadCurrentRow() override; - -protected: - virtual Vector<char> encodeKey(const IDBKey &key) - { - return IndexDataKey::encode(m_cursorOptions.databaseId, m_cursorOptions.objectStoreId, m_cursorOptions.indexId, key); - } - -private: - IndexKeyCursorImpl(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - : IDBBackingStoreCursorLevelDB(cursorID, transaction, cursorOptions) - { - } - - IndexKeyCursorImpl(const IndexKeyCursorImpl* other) - : IDBBackingStoreCursorLevelDB(other) - , m_primaryKey(other->m_primaryKey) - { - } - - RefPtr<IDBKey> m_primaryKey; -}; - -bool IndexKeyCursorImpl::loadCurrentRow() -{ - const char* keyPosition = m_iterator->key().begin(); - const char* keyLimit = m_iterator->key().end(); - - IndexDataKey indexDataKey; - keyPosition = IndexDataKey::decode(keyPosition, keyLimit, &indexDataKey); - - m_currentKey = indexDataKey.userKey(); - - int64_t indexDataVersion; - const char* valuePosition = decodeVarInt(m_iterator->value().begin(), m_iterator->value().end(), indexDataVersion); - if (!valuePosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - valuePosition = decodeIDBKey(valuePosition, m_iterator->value().end(), m_primaryKey); - if (!valuePosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - Vector<char> primaryLevelDBKey = ObjectStoreDataKey::encode(indexDataKey.databaseId(), indexDataKey.objectStoreId(), *m_primaryKey); - - Vector<char> result; - bool found = false; - bool ok = m_transaction->safeGet(primaryLevelDBKey, result, found); - if (!ok) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - if (!found) { - m_transaction->remove(m_iterator->key()); - return false; - } - - int64_t objectStoreDataVersion; - const char* t = decodeVarInt(result.begin(), result.end(), objectStoreDataVersion); - if (!t) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - if (objectStoreDataVersion != indexDataVersion) { - m_transaction->remove(m_iterator->key()); - return false; - } - - return true; -} - -class IndexCursorImpl final : public IDBBackingStoreCursorLevelDB { -public: - static PassRefPtr<IndexCursorImpl> create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - { - return adoptRef(new IndexCursorImpl(cursorID, transaction, cursorOptions)); - } - - virtual PassRefPtr<IDBBackingStoreCursorLevelDB> clone() - { - return adoptRef(new IndexCursorImpl(this)); - } - - // IDBBackingStoreCursorLevelDB - virtual PassRefPtr<SharedBuffer> value() const override { return m_currentValue; } - virtual PassRefPtr<IDBKey> primaryKey() const override { return m_primaryKey; } - virtual const IDBRecordIdentifier& recordIdentifier() const override { ASSERT_NOT_REACHED(); return *m_recordIdentifier; } - virtual bool loadCurrentRow() override; - -protected: - virtual Vector<char> encodeKey(const IDBKey &key) - { - return IndexDataKey::encode(m_cursorOptions.databaseId, m_cursorOptions.objectStoreId, m_cursorOptions.indexId, key); - } - -private: - IndexCursorImpl(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) - : IDBBackingStoreCursorLevelDB(cursorID, transaction, cursorOptions) - { - } - - IndexCursorImpl(const IndexCursorImpl* other) - : IDBBackingStoreCursorLevelDB(other) - , m_primaryKey(other->m_primaryKey) - , m_currentValue(other->m_currentValue) - , m_primaryLevelDBKey(other->m_primaryLevelDBKey) - { - } - - RefPtr<IDBKey> m_primaryKey; - RefPtr<SharedBuffer> m_currentValue; - Vector<char> m_primaryLevelDBKey; -}; - -bool IndexCursorImpl::loadCurrentRow() -{ - const char* keyPosition = m_iterator->key().begin(); - const char* keyLimit = m_iterator->key().end(); - - IndexDataKey indexDataKey; - keyPosition = IndexDataKey::decode(keyPosition, keyLimit, &indexDataKey); - - m_currentKey = indexDataKey.userKey(); - - const char* valuePosition = m_iterator->value().begin(); - const char* valueLimit = m_iterator->value().end(); - - int64_t indexDataVersion; - valuePosition = decodeVarInt(valuePosition, valueLimit, indexDataVersion); - if (!valuePosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - valuePosition = decodeIDBKey(valuePosition, valueLimit, m_primaryKey); - if (!valuePosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - m_primaryLevelDBKey = ObjectStoreDataKey::encode(indexDataKey.databaseId(), indexDataKey.objectStoreId(), *m_primaryKey); - - Vector<char> result; - bool found = false; - bool ok = m_transaction->safeGet(m_primaryLevelDBKey, result, found); - if (!ok) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - if (!found) { - m_transaction->remove(m_iterator->key()); - return false; - } - - int64_t objectStoreDataVersion; - valuePosition = decodeVarInt(result.begin(), result.end(), objectStoreDataVersion); - if (!valuePosition) { - INTERNAL_READ_ERROR(LoadCurrentRow); - return false; - } - - if (objectStoreDataVersion != indexDataVersion) { - m_transaction->remove(m_iterator->key()); - return false; - } - - Vector<char> value; - value.append(valuePosition, result.end() - valuePosition); - m_currentValue = SharedBuffer::adoptVector(value); - return true; -} - -static bool objectStoreCursorOptions(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, const IDBKeyRange* range, IndexedDB::CursorDirection direction, IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) -{ - cursorOptions.databaseId = databaseId; - cursorOptions.objectStoreId = objectStoreId; - - bool lowerBound = range && range->lower(); - bool upperBound = range && range->upper(); - cursorOptions.forward = (direction == IndexedDB::CursorDirection::NextNoDuplicate || direction == IndexedDB::CursorDirection::Next); - cursorOptions.unique = (direction == IndexedDB::CursorDirection::NextNoDuplicate || direction == IndexedDB::CursorDirection::PrevNoDuplicate); - - if (!lowerBound) { - cursorOptions.lowKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, minIDBKey()); - cursorOptions.lowOpen = true; // Not included. - } else { - cursorOptions.lowKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, *range->lower()); - cursorOptions.lowOpen = range->lowerOpen(); - } - - if (!upperBound) { - cursorOptions.highKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, maxIDBKey()); - - if (cursorOptions.forward) - cursorOptions.highOpen = true; // Not included. - else { - // We need a key that exists. - if (!findGreatestKeyLessThanOrEqual(transaction, cursorOptions.highKey, cursorOptions.highKey)) - return false; - cursorOptions.highOpen = false; - } - } else { - cursorOptions.highKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, *range->upper()); - cursorOptions.highOpen = range->upperOpen(); - - if (!cursorOptions.forward) { - // For reverse cursors, we need a key that exists. - Vector<char> foundHighKey; - if (!findGreatestKeyLessThanOrEqual(transaction, cursorOptions.highKey, foundHighKey)) - return false; - - // If the target key should not be included, but we end up with a smaller key, we should include that. - if (cursorOptions.highOpen && IDBBackingStoreLevelDB::compareIndexKeys(foundHighKey, cursorOptions.highKey) < 0) - cursorOptions.highOpen = false; - - cursorOptions.highKey = foundHighKey; - } - } - - return true; -} - -static bool indexCursorOptions(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IndexedDB::CursorDirection direction, IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions) -{ - ASSERT(transaction); - if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId)) - return false; - - cursorOptions.databaseId = databaseId; - cursorOptions.objectStoreId = objectStoreId; - cursorOptions.indexId = indexId; - - bool lowerBound = range && range->lower(); - bool upperBound = range && range->upper(); - cursorOptions.forward = (direction == IndexedDB::CursorDirection::NextNoDuplicate || direction == IndexedDB::CursorDirection::Next); - cursorOptions.unique = (direction == IndexedDB::CursorDirection::NextNoDuplicate || direction == IndexedDB::CursorDirection::PrevNoDuplicate); - - if (!lowerBound) { - cursorOptions.lowKey = IndexDataKey::encodeMinKey(databaseId, objectStoreId, indexId); - cursorOptions.lowOpen = false; // Included. - } else { - cursorOptions.lowKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->lower()); - cursorOptions.lowOpen = range->lowerOpen(); - } - - if (!upperBound) { - cursorOptions.highKey = IndexDataKey::encodeMaxKey(databaseId, objectStoreId, indexId); - cursorOptions.highOpen = false; // Included. - - if (!cursorOptions.forward) { // We need a key that exists. - if (!findGreatestKeyLessThanOrEqual(transaction, cursorOptions.highKey, cursorOptions.highKey)) - return false; - cursorOptions.highOpen = false; - } - } else { - cursorOptions.highKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->upper()); - cursorOptions.highOpen = range->upperOpen(); - - Vector<char> foundHighKey; - if (!findGreatestKeyLessThanOrEqual(transaction, cursorOptions.highKey, foundHighKey)) // Seek to the *last* key in the set of non-unique keys. - return false; - - // If the target key should not be included, but we end up with a smaller key, we should include that. - if (cursorOptions.highOpen && IDBBackingStoreLevelDB::compareIndexKeys(foundHighKey, cursorOptions.highKey) < 0) - cursorOptions.highOpen = false; - - cursorOptions.highKey = foundHighKey; - } - - return true; -} -PassRefPtr<IDBBackingStoreCursorLevelDB> IDBBackingStoreLevelDB::openObjectStoreCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseID, int64_t objectStoreId, const IDBKeyRange* range, IndexedDB::CursorDirection direction) - -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::openObjectStoreCursor"); - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - IDBBackingStoreCursorLevelDB::CursorOptions cursorOptions; - if (!objectStoreCursorOptions(levelDBTransaction, databaseID, objectStoreId, range, direction, cursorOptions)) - return 0; - RefPtr<ObjectStoreCursorImpl> cursor = ObjectStoreCursorImpl::create(cursorID, levelDBTransaction, cursorOptions); - if (!cursor->firstSeek()) - return 0; - - return cursor.release(); -} - -PassRefPtr<IDBBackingStoreCursorLevelDB> IDBBackingStoreLevelDB::openObjectStoreKeyCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseID, int64_t objectStoreId, const IDBKeyRange* range, IndexedDB::CursorDirection direction) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::openObjectStoreKeyCursor"); - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - IDBBackingStoreCursorLevelDB::CursorOptions cursorOptions; - if (!objectStoreCursorOptions(levelDBTransaction, databaseID, objectStoreId, range, direction, cursorOptions)) - return 0; - RefPtr<ObjectStoreKeyCursorImpl> cursor = ObjectStoreKeyCursorImpl::create(cursorID, levelDBTransaction, cursorOptions); - if (!cursor->firstSeek()) - return 0; - - return cursor.release(); -} - -PassRefPtr<IDBBackingStoreCursorLevelDB> IDBBackingStoreLevelDB::openIndexKeyCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseID, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IndexedDB::CursorDirection direction) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::openIndexKeyCursor"); - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - IDBBackingStoreCursorLevelDB::CursorOptions cursorOptions; - if (!indexCursorOptions(levelDBTransaction, databaseID, objectStoreId, indexId, range, direction, cursorOptions)) - return 0; - RefPtr<IndexKeyCursorImpl> cursor = IndexKeyCursorImpl::create(cursorID, levelDBTransaction, cursorOptions); - if (!cursor->firstSeek()) - return 0; - - return cursor.release(); -} - -PassRefPtr<IDBBackingStoreCursorLevelDB> IDBBackingStoreLevelDB::openIndexCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseID, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IndexedDB::CursorDirection direction) -{ - LOG(StorageAPI, "IDBBackingStoreLevelDB::openIndexCursor"); - LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction); - IDBBackingStoreCursorLevelDB::CursorOptions cursorOptions; - if (!indexCursorOptions(levelDBTransaction, databaseID, objectStoreId, indexId, range, direction, cursorOptions)) - return 0; - RefPtr<IndexCursorImpl> cursor = IndexCursorImpl::create(cursorID, levelDBTransaction, cursorOptions); - if (!cursor->firstSeek()) - return 0; - - return cursor.release(); -} - -void IDBBackingStoreLevelDB::establishBackingStoreTransaction(int64_t transactionID) -{ - ASSERT(!m_backingStoreTransactions.contains(transactionID)); - m_backingStoreTransactions.set(transactionID, IDBBackingStoreTransactionLevelDB::create(transactionID, this)); -} - -void IDBBackingStoreLevelDB::removeBackingStoreTransaction(IDBBackingStoreTransactionLevelDB* transaction) -{ - ASSERT(m_backingStoreTransactions.contains(transaction->transactionID())); - m_backingStoreTransactions.remove(transaction->transactionID()); -} - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB) |