diff options
Diffstat (limited to 'content/browser/indexed_db/indexed_db_backing_store.cc')
-rw-r--r-- | content/browser/indexed_db/indexed_db_backing_store.cc | 2543 |
1 files changed, 0 insertions, 2543 deletions
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc deleted file mode 100644 index 1596aef..0000000 --- a/content/browser/indexed_db/indexed_db_backing_store.cc +++ /dev/null @@ -1,2543 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/indexed_db/indexed_db_backing_store.h" - -#include <string> - -#include "base/file_util.h" -#include "base/logging.h" -#include "base/metrics/histogram.h" -#include "base/utf_string_conversions.h" -#include "content/browser/indexed_db/indexed_db_leveldb_coding.h" -#include "content/browser/indexed_db/indexed_db_metadata.h" -#include "content/browser/indexed_db/indexed_db_tracing.h" -#include "content/browser/indexed_db/leveldb/leveldb_comparator.h" -#include "content/browser/indexed_db/leveldb/leveldb_database.h" -#include "content/browser/indexed_db/leveldb/leveldb_iterator.h" -#include "content/browser/indexed_db/leveldb/leveldb_slice.h" -#include "content/browser/indexed_db/leveldb/leveldb_transaction.h" -#include "content/common/indexed_db/indexed_db_key.h" -#include "content/common/indexed_db/indexed_db_key_path.h" -#include "content/common/indexed_db/indexed_db_key_range.h" -#include "third_party/WebKit/public/platform/WebIDBKey.h" -#include "third_party/WebKit/public/platform/WebIDBKeyPath.h" - -// TODO(jsbell): Make blink push the version during the open() call. -static const uint32 kWireVersion = 2; - -namespace content { - -static const int64 kKeyGeneratorInitialNumber = - 1; // From the IndexedDB specification. - -enum IndexedDBBackingStoreErrorSource { - // 0 - 2 are no longer used. - FIND_KEY_IN_INDEX = 3, - GET_IDBDATABASE_METADATA, - GET_INDEXES, - GET_KEY_GENERATOR_CURRENT_NUMBER, - GET_OBJECT_STORES, - GET_RECORD, - KEY_EXISTS_IN_OBJECT_STORE, - LOAD_CURRENT_ROW, - SET_UP_METADATA, - GET_PRIMARY_KEY_VIA_INDEX, - KEY_EXISTS_IN_INDEX, - VERSION_EXISTS, - DELETE_OBJECT_STORE, - SET_MAX_OBJECT_STORE_ID, - SET_MAX_INDEX_ID, - GET_NEW_DATABASE_ID, - GET_NEW_VERSION_NUMBER, - CREATE_IDBDATABASE_METADATA, - DELETE_DATABASE, - TRANSACTION_COMMIT_METHOD, // TRANSACTION_COMMIT is a WinNT.h macro - INTERNAL_ERROR_MAX, -}; - -static void RecordInternalError(const char* type, - IndexedDBBackingStoreErrorSource location) { - string16 name = ASCIIToUTF16("WebCore.IndexedDB.BackingStore.") + - UTF8ToUTF16(type) + ASCIIToUTF16("Error"); - base::Histogram::FactoryGet(UTF16ToUTF8(name), - 1, - INTERNAL_ERROR_MAX, - INTERNAL_ERROR_MAX + 1, - base::HistogramBase::kUmaTargetedHistogramFlag) - ->Add(location); -} - -// 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 " type " Error: " #location; \ - NOTREACHED(); \ - 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& found_int, - bool& found) { - std::vector<char> result; - bool ok = db->Get(key, result, found); - if (!ok) - return false; - if (!found) - return true; - - found_int = DecodeInt(result.begin(), result.end()); - return true; -} - -static void PutInt(LevelDBTransaction* transaction, - const LevelDBSlice& key, - int64 value) { - DCHECK_GE(value, 0); - transaction->Put(key, EncodeInt(value)); -} - -template <typename DBOrTransaction> -WARN_UNUSED_RESULT static bool GetVarInt(DBOrTransaction* db, - const LevelDBSlice& key, - int64& found_int, - bool& found) { - std::vector<char> result; - bool ok = db->Get(key, result, found); - if (!ok) - return false; - if (!found) - return true; - if (!result.size()) - return false; - - found = DecodeVarInt(&*result.begin(), &*result.rbegin() + 1, found_int) == - &*result.rbegin() + 1; - return true; -} - -static void PutVarInt(LevelDBTransaction* transaction, - const LevelDBSlice& key, - int64 value) { - transaction->Put(key, EncodeVarInt(value)); -} - -template <typename DBOrTransaction> -WARN_UNUSED_RESULT static bool GetString(DBOrTransaction* db, - const LevelDBSlice& key, - string16& found_string, - bool& found) { - std::vector<char> result; - found = false; - bool ok = db->Get(key, result, found); - if (!ok) - return false; - if (!found) - return true; - if (!result.size()) { - found_string.clear(); - return true; - } - - found_string = DecodeString(&*result.begin(), &*result.rbegin() + 1); - return true; -} - -static void PutString(LevelDBTransaction* transaction, - const LevelDBSlice& key, - const string16& value) { - transaction->Put(key, EncodeString(value)); -} - -static void PutIDBKeyPath(LevelDBTransaction* transaction, - const LevelDBSlice& key, - const IndexedDBKeyPath& value) { - transaction->Put(key, EncodeIDBKeyPath(value)); -} - -static int CompareKeys(const LevelDBSlice& a, const LevelDBSlice& b) { - return Compare(a, b); -} - -static int 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 - OVERRIDE { - return content::Compare(a, b); - } - virtual const char* Name() const OVERRIDE { return "idb_cmp1"; } -}; - -// 0 - Initial version. -// 1 - Adds UserIntVersion to DatabaseMetaData. -// 2 - Adds DataVersion to to global metadata. -static const int64 kLatestKnownSchemaVersion = 2; -WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) { - int64 db_schema_version = 0; - bool found = false; - bool ok = GetInt( - db, LevelDBSlice(SchemaVersionKey::Encode()), db_schema_version, found); - if (!ok) - return false; - if (!found) { - *known = true; - return true; - } - if (db_schema_version > kLatestKnownSchemaVersion) { - *known = false; - return true; - } - - const uint32 latest_known_data_version = kWireVersion; - int64 db_data_version = 0; - ok = GetInt( - db, LevelDBSlice(DataVersionKey::Encode()), db_data_version, found); - if (!ok) - return false; - if (!found) { - *known = true; - return true; - } - - if (db_data_version > latest_known_data_version) { - *known = false; - return true; - } - - *known = true; - return true; -} - -WARN_UNUSED_RESULT static bool SetUpMetadata(LevelDBDatabase* db, - const string16& origin) { - const uint32 latest_known_data_version = kWireVersion; - const std::vector<char> schema_version_key = SchemaVersionKey::Encode(); - const std::vector<char> data_version_key = DataVersionKey::Encode(); - - scoped_refptr<LevelDBTransaction> transaction = - LevelDBTransaction::Create(db); - - int64 db_schema_version = 0; - int64 db_data_version = 0; - bool found = false; - bool ok = GetInt(transaction.get(), - LevelDBSlice(schema_version_key), - db_schema_version, - found); - if (!ok) { - INTERNAL_READ_ERROR(SET_UP_METADATA); - return false; - } - if (!found) { - // Initialize new backing store. - db_schema_version = kLatestKnownSchemaVersion; - PutInt( - transaction.get(), LevelDBSlice(schema_version_key), db_schema_version); - db_data_version = latest_known_data_version; - PutInt(transaction.get(), LevelDBSlice(data_version_key), db_data_version); - } else { - // Upgrade old backing store. - DCHECK_LE(db_schema_version, kLatestKnownSchemaVersion); - if (db_schema_version < 1) { - db_schema_version = 1; - PutInt(transaction.get(), - LevelDBSlice(schema_version_key), - db_schema_version); - const std::vector<char> start_key = - DatabaseNameKey::EncodeMinKeyForOrigin(origin); - const std::vector<char> stop_key = - DatabaseNameKey::EncodeStopKeyForOrigin(origin); - scoped_ptr<LevelDBIterator> it = db->CreateIterator(); - for (it->Seek(LevelDBSlice(start_key)); - it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(stop_key)) < 0; - it->Next()) { - int64 database_id = 0; - found = false; - bool ok = GetInt(transaction.get(), it->Key(), database_id, found); - if (!ok) { - INTERNAL_READ_ERROR(SET_UP_METADATA); - return false; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(SET_UP_METADATA); - return false; - } - std::vector<char> int_version_key = DatabaseMetaDataKey::Encode( - database_id, DatabaseMetaDataKey::USER_INT_VERSION); - PutVarInt(transaction.get(), - LevelDBSlice(int_version_key), - IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION); - } - } - if (db_schema_version < 2) { - db_schema_version = 2; - PutInt(transaction.get(), - LevelDBSlice(schema_version_key), - db_schema_version); - db_data_version = kWireVersion; - PutInt( - transaction.get(), LevelDBSlice(data_version_key), db_data_version); - } - } - - // All new values will be written using this serialization version. - found = false; - ok = GetInt(transaction.get(), - LevelDBSlice(data_version_key), - db_data_version, - found); - if (!ok) { - INTERNAL_READ_ERROR(SET_UP_METADATA); - return false; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(SET_UP_METADATA); - return false; - } - if (db_data_version < latest_known_data_version) { - db_data_version = latest_known_data_version; - PutInt(transaction.get(), LevelDBSlice(data_version_key), db_data_version); - } - - DCHECK_EQ(db_schema_version, kLatestKnownSchemaVersion); - DCHECK_EQ(db_data_version, latest_known_data_version); - - if (!transaction->Commit()) { - INTERNAL_WRITE_ERROR(SET_UP_METADATA); - return false; - } - return true; -} - -template <typename DBOrTransaction> -WARN_UNUSED_RESULT static bool GetMaxObjectStoreId(DBOrTransaction* db, - int64 database_id, - int64& max_object_store_id) { - const std::vector<char> max_object_store_id_key = DatabaseMetaDataKey::Encode( - database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID); - bool ok = - GetMaxObjectStoreId(db, max_object_store_id_key, max_object_store_id); - return ok; -} - -template <typename DBOrTransaction> -WARN_UNUSED_RESULT static bool GetMaxObjectStoreId( - DBOrTransaction* db, - const std::vector<char>& max_object_store_id_key, - int64& max_object_store_id) { - max_object_store_id = -1; - bool found = false; - bool ok = GetInt( - db, LevelDBSlice(max_object_store_id_key), max_object_store_id, found); - if (!ok) - return false; - if (!found) - max_object_store_id = 0; - - DCHECK_GE(max_object_store_id, 0); - return true; -} - -class DefaultLevelDBFactory : public LevelDBFactory { - public: - virtual scoped_ptr<LevelDBDatabase> OpenLevelDB( - const base::FilePath& file_name, - const LevelDBComparator* comparator) OVERRIDE { - return LevelDBDatabase::Open(file_name, comparator); - } - virtual bool DestroyLevelDB(const base::FilePath& file_name) OVERRIDE { - return LevelDBDatabase::Destroy(file_name); - } -}; - -IndexedDBBackingStore::IndexedDBBackingStore( - const string16& identifier, - scoped_ptr<LevelDBDatabase> db, - scoped_ptr<LevelDBComparator> comparator) - : identifier_(identifier), - db_(db.Pass()), - comparator_(comparator.Pass()), - weak_factory_(this) {} - -IndexedDBBackingStore::~IndexedDBBackingStore() { - // db_'s destructor uses comparator_. The order of destruction is important. - db_.reset(); - comparator_.reset(); -} - -IndexedDBBackingStore::RecordIdentifier::RecordIdentifier( - const std::vector<char>& primary_key, - int64 version) - : primary_key_(primary_key), version_(version) { - DCHECK(!primary_key.empty()); -} -IndexedDBBackingStore::RecordIdentifier::RecordIdentifier() - : primary_key_(), version_(-1) {} -IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {} - -IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions() {} -IndexedDBBackingStore::Cursor::CursorOptions::~CursorOptions() {} - -enum IndexedDBLevelDBBackingStoreOpenResult { - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_SUCCESS, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MEMORY_FAILED, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX, -}; - -scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open( - const string16& database_identifier, - const string16& path_base_arg, - const string16& file_identifier) { - DefaultLevelDBFactory leveldb_factory; - return IndexedDBBackingStore::Open( - database_identifier, path_base_arg, file_identifier, &leveldb_factory); -} - -scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open( - const string16& database_identifier, - const string16& path_base_arg, - const string16& file_identifier, - LevelDBFactory* leveldb_factory) { - IDB_TRACE("IndexedDBBackingStore::open"); - DCHECK(!path_base_arg.empty()); - string16 path_base = path_base_arg; - - scoped_ptr<LevelDBComparator> comparator(new Comparator()); - scoped_ptr<LevelDBDatabase> db; - - if (!IsStringASCII(path_base)) { - base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", - 1, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1, - base::HistogramBase::kUmaTargetedHistogramFlag) - ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII); - } - base::FilePath file_path_base = - base::FilePath::FromUTF8Unsafe(UTF16ToUTF8(path_base)); - if (!file_util::CreateDirectory(file_path_base)) { - LOG(ERROR) << "Unable to create IndexedDB database path " << path_base; - base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", - 1, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1, - base::HistogramBase::kUmaTargetedHistogramFlag) - ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY); - return scoped_refptr<IndexedDBBackingStore>(); - } - - // TODO(jsbell): Rework to use FilePath throughout. - base::FilePath identifier_path = - base::FilePath::FromUTF8Unsafe(UTF16ToUTF8(database_identifier)); - base::FilePath file_path = - file_path_base.Append(identifier_path).AppendASCII(".indexeddb.leveldb"); - - db = leveldb_factory->OpenLevelDB(file_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"; - base::Histogram::FactoryGet( - "WebCore.IndexedDB.BackingStore.OpenStatus", - 1, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1, - base::HistogramBase::kUmaTargetedHistogramFlag) - ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA); - db.reset(); - } else if (!known) { - LOG(ERROR) << "IndexedDB backing store had unknown schema, treating it " - "as failure to open"; - base::Histogram::FactoryGet( - "WebCore.IndexedDB.BackingStore.OpenStatus", - 1, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1, - base::HistogramBase::kUmaTargetedHistogramFlag) - ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA); - db.reset(); - } - } - - if (db) { - base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", - 1, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1, - base::HistogramBase::kUmaTargetedHistogramFlag) - ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_SUCCESS); - } else { - LOG(ERROR) << "IndexedDB backing store open failed, attempting cleanup"; - bool success = leveldb_factory->DestroyLevelDB(file_path); - if (!success) { - LOG(ERROR) << "IndexedDB backing store cleanup failed"; - base::Histogram::FactoryGet( - "WebCore.IndexedDB.BackingStore.OpenStatus", - 1, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1, - base::HistogramBase::kUmaTargetedHistogramFlag) - ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED); - return scoped_refptr<IndexedDBBackingStore>(); - } - - LOG(ERROR) << "IndexedDB backing store cleanup succeeded, reopening"; - db = leveldb_factory->OpenLevelDB(file_path, comparator.get()); - if (!db) { - LOG(ERROR) << "IndexedDB backing store reopen after recovery failed"; - base::Histogram::FactoryGet( - "WebCore.IndexedDB.BackingStore.OpenStatus", - 1, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1, - base::HistogramBase::kUmaTargetedHistogramFlag) - ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED); - return scoped_refptr<IndexedDBBackingStore>(); - } - base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", - 1, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1, - base::HistogramBase::kUmaTargetedHistogramFlag) - ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS); - } - - if (!db) { - NOTREACHED(); - base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", - 1, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1, - base::HistogramBase::kUmaTargetedHistogramFlag) - ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR); - return scoped_refptr<IndexedDBBackingStore>(); - } - - return Create(file_identifier, db.Pass(), comparator.Pass()); -} - -scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory( - const string16& identifier) { - DefaultLevelDBFactory leveldb_factory; - return IndexedDBBackingStore::OpenInMemory(identifier, &leveldb_factory); -} - -scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory( - const string16& identifier, - LevelDBFactory* leveldb_factory) { - IDB_TRACE("IndexedDBBackingStore::open_in_memory"); - - scoped_ptr<LevelDBComparator> comparator(new Comparator()); - scoped_ptr<LevelDBDatabase> db = - LevelDBDatabase::OpenInMemory(comparator.get()); - if (!db) { - LOG(ERROR) << "LevelDBDatabase::open_in_memory failed."; - base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", - 1, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1, - base::HistogramBase::kUmaTargetedHistogramFlag) - ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MEMORY_FAILED); - return scoped_refptr<IndexedDBBackingStore>(); - } - base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", - 1, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX, - INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1, - base::HistogramBase::kUmaTargetedHistogramFlag) - ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS); - - return Create(identifier, db.Pass(), comparator.Pass()); -} - -scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create( - const string16& identifier, - scoped_ptr<LevelDBDatabase> db, - scoped_ptr<LevelDBComparator> comparator) { - // TODO(jsbell): Handle comparator name changes. - scoped_refptr<IndexedDBBackingStore> backing_store( - new IndexedDBBackingStore(identifier, db.Pass(), comparator.Pass())); - - if (!SetUpMetadata(backing_store->db_.get(), identifier)) - return scoped_refptr<IndexedDBBackingStore>(); - - return backing_store; -} - -std::vector<string16> IndexedDBBackingStore::GetDatabaseNames() { - std::vector<string16> found_names; - const std::vector<char> start_key = - DatabaseNameKey::EncodeMinKeyForOrigin(identifier_); - const std::vector<char> stop_key = - DatabaseNameKey::EncodeStopKeyForOrigin(identifier_); - - DCHECK(found_names.empty()); - - scoped_ptr<LevelDBIterator> it = db_->CreateIterator(); - for (it->Seek(LevelDBSlice(start_key)); - it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(stop_key)) < 0; - it->Next()) { - const char* p = it->Key().begin(); - const char* limit = it->Key().end(); - - DatabaseNameKey database_name_key; - p = DatabaseNameKey::Decode(p, limit, &database_name_key); - DCHECK(p); - - found_names.push_back(database_name_key.database_name()); - } - return found_names; -} - -bool IndexedDBBackingStore::GetIDBDatabaseMetaData( - const string16& name, - IndexedDBDatabaseMetadata* metadata, - bool& found) { - const std::vector<char> key = DatabaseNameKey::Encode(identifier_, name); - found = false; - - bool ok = GetInt(db_.get(), LevelDBSlice(key), metadata->id, found); - if (!ok) { - INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA); - return false; - } - if (!found) - return true; - - ok = GetString(db_.get(), - LevelDBSlice(DatabaseMetaDataKey::Encode( - metadata->id, DatabaseMetaDataKey::USER_VERSION)), - metadata->version, - found); - if (!ok) { - INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA); - return false; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(GET_IDBDATABASE_METADATA); - return false; - } - - ok = GetVarInt(db_.get(), - LevelDBSlice(DatabaseMetaDataKey::Encode( - metadata->id, DatabaseMetaDataKey::USER_INT_VERSION)), - metadata->int_version, - found); - if (!ok) { - INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA); - return false; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(GET_IDBDATABASE_METADATA); - return false; - } - - if (metadata->int_version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) - metadata->int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION; - - ok = GetMaxObjectStoreId( - db_.get(), metadata->id, metadata->max_object_store_id); - if (!ok) { - INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA); - return false; - } - - return true; -} - -WARN_UNUSED_RESULT static bool GetNewDatabaseId(LevelDBDatabase* db, - int64& new_id) { - scoped_refptr<LevelDBTransaction> transaction = - LevelDBTransaction::Create(db); - - new_id = -1; - int64 max_database_id = -1; - bool found = false; - bool ok = GetInt(transaction.get(), - LevelDBSlice(MaxDatabaseIdKey::Encode()), - max_database_id, - found); - if (!ok) { - INTERNAL_READ_ERROR(GET_NEW_DATABASE_ID); - return false; - } - if (!found) - max_database_id = 0; - - DCHECK_GE(max_database_id, 0); - - int64 database_id = max_database_id + 1; - PutInt( - transaction.get(), LevelDBSlice(MaxDatabaseIdKey::Encode()), database_id); - if (!transaction->Commit()) { - INTERNAL_WRITE_ERROR(GET_NEW_DATABASE_ID); - return false; - } - new_id = database_id; - return true; -} - -bool IndexedDBBackingStore::CreateIDBDatabaseMetaData(const string16& name, - const string16& version, - int64 int_version, - int64& row_id) { - bool ok = GetNewDatabaseId(db_.get(), row_id); - if (!ok) - return false; - DCHECK_GE(row_id, 0); - - if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION) - int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION; - - scoped_refptr<LevelDBTransaction> transaction = - LevelDBTransaction::Create(db_.get()); - PutInt(transaction.get(), - LevelDBSlice(DatabaseNameKey::Encode(identifier_, name)), - row_id); - PutString(transaction.get(), - LevelDBSlice(DatabaseMetaDataKey::Encode( - row_id, DatabaseMetaDataKey::USER_VERSION)), - version); - PutVarInt(transaction.get(), - LevelDBSlice(DatabaseMetaDataKey::Encode( - row_id, DatabaseMetaDataKey::USER_INT_VERSION)), - int_version); - if (!transaction->Commit()) { - INTERNAL_WRITE_ERROR(CREATE_IDBDATABASE_METADATA); - return false; - } - return true; -} - -bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion( - IndexedDBBackingStore::Transaction* transaction, - int64 row_id, - int64 int_version) { - if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION) - int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION; - DCHECK_GE(int_version, 0) << "int_version was " << int_version; - PutVarInt(Transaction::LevelDBTransactionFrom(transaction), - LevelDBSlice(DatabaseMetaDataKey::Encode( - row_id, DatabaseMetaDataKey::USER_INT_VERSION)), - int_version); - return true; -} - -bool IndexedDBBackingStore::UpdateIDBDatabaseMetaData( - IndexedDBBackingStore::Transaction* transaction, - int64 row_id, - const string16& version) { - PutString(Transaction::LevelDBTransactionFrom(transaction), - LevelDBSlice(DatabaseMetaDataKey::Encode( - row_id, DatabaseMetaDataKey::USER_VERSION)), - version); - return true; -} - -static void DeleteRange(LevelDBTransaction* transaction, - const std::vector<char>& begin, - const std::vector<char>& end) { - scoped_ptr<LevelDBIterator> it = transaction->CreateIterator(); - for (it->Seek(LevelDBSlice(begin)); - it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(end)) < 0; - it->Next()) - transaction->Remove(it->Key()); -} - -bool IndexedDBBackingStore::DeleteDatabase(const string16& name) { - IDB_TRACE("IndexedDBBackingStore::delete_database"); - scoped_ptr<LevelDBWriteOnlyTransaction> transaction = - LevelDBWriteOnlyTransaction::Create(db_.get()); - - IndexedDBDatabaseMetadata metadata; - bool success = false; - bool ok = GetIDBDatabaseMetaData(name, &metadata, success); - if (!ok) - return false; - if (!success) - return true; - - const std::vector<char> start_key = DatabaseMetaDataKey::Encode( - metadata.id, DatabaseMetaDataKey::ORIGIN_NAME); - const std::vector<char> stop_key = DatabaseMetaDataKey::Encode( - metadata.id + 1, DatabaseMetaDataKey::ORIGIN_NAME); - scoped_ptr<LevelDBIterator> it = db_->CreateIterator(); - for (it->Seek(LevelDBSlice(start_key)); - it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(stop_key)) < 0; - it->Next()) - transaction->Remove(it->Key()); - - const std::vector<char> key = DatabaseNameKey::Encode(identifier_, name); - transaction->Remove(LevelDBSlice(key)); - - if (!transaction->Commit()) { - INTERNAL_WRITE_ERROR(DELETE_DATABASE); - return false; - } - return true; -} - -static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator* it, - const std::vector<char>& stop_key, - int64 object_store_id, - int64 meta_data_type) { - if (!it->IsValid() || CompareKeys(it->Key(), LevelDBSlice(stop_key)) >= 0) - return false; - - ObjectStoreMetaDataKey meta_data_key; - const char* p = ObjectStoreMetaDataKey::Decode( - it->Key().begin(), it->Key().end(), &meta_data_key); - DCHECK(p); - if (meta_data_key.ObjectStoreId() != object_store_id) - return false; - if (meta_data_key.MetaDataType() != meta_data_type) - return false; - return true; -} - -// TODO(jsbell): This should do some error handling rather than -// plowing ahead when bad data is encountered. -bool IndexedDBBackingStore::GetObjectStores( - int64 database_id, - IndexedDBDatabaseMetadata::ObjectStoreMap* object_stores) { - IDB_TRACE("IndexedDBBackingStore::get_object_stores"); - if (!KeyPrefix::IsValidDatabaseId(database_id)) - return false; - const std::vector<char> start_key = - ObjectStoreMetaDataKey::Encode(database_id, 1, 0); - const std::vector<char> stop_key = - ObjectStoreMetaDataKey::EncodeMaxKey(database_id); - - DCHECK(object_stores->empty()); - - scoped_ptr<LevelDBIterator> it = db_->CreateIterator(); - it->Seek(LevelDBSlice(start_key)); - while (it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(stop_key)) < 0) { - const char* p = it->Key().begin(); - const char* limit = it->Key().end(); - - ObjectStoreMetaDataKey meta_data_key; - p = ObjectStoreMetaDataKey::Decode(p, limit, &meta_data_key); - DCHECK(p); - if (meta_data_key.MetaDataType() != ObjectStoreMetaDataKey::NAME) { - INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES); - // Possible stale metadata, but don't fail the load. - it->Next(); - continue; - } - - int64 object_store_id = meta_data_key.ObjectStoreId(); - - // TODO(jsbell): Do this by direct key lookup rather than iteration, to - // simplify. - string16 object_store_name = - DecodeString(it->Value().begin(), it->Value().end()); - - it->Next(); - if (!CheckObjectStoreAndMetaDataType(it.get(), - stop_key, - object_store_id, - ObjectStoreMetaDataKey::KEY_PATH)) { - INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES); - break; - } - IndexedDBKeyPath key_path = - DecodeIDBKeyPath(it->Value().begin(), it->Value().end()); - - it->Next(); - if (!CheckObjectStoreAndMetaDataType( - it.get(), - stop_key, - object_store_id, - ObjectStoreMetaDataKey::AUTO_INCREMENT)) { - INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES); - break; - } - bool auto_increment = DecodeBool(it->Value().begin(), it->Value().end()); - - it->Next(); // Is evicatble. - if (!CheckObjectStoreAndMetaDataType(it.get(), - stop_key, - object_store_id, - ObjectStoreMetaDataKey::EVICTABLE)) { - INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES); - break; - } - - it->Next(); // Last version. - if (!CheckObjectStoreAndMetaDataType( - it.get(), - stop_key, - object_store_id, - ObjectStoreMetaDataKey::LAST_VERSION)) { - INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES); - break; - } - - it->Next(); // Maximum index id allocated. - if (!CheckObjectStoreAndMetaDataType( - it.get(), - stop_key, - object_store_id, - ObjectStoreMetaDataKey::MAX_INDEX_ID)) { - INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES); - break; - } - int64 max_index_id = DecodeInt(it->Value().begin(), it->Value().end()); - - it->Next(); // [optional] has key path (is not null) - if (CheckObjectStoreAndMetaDataType(it.get(), - stop_key, - object_store_id, - ObjectStoreMetaDataKey::HAS_KEY_PATH)) { - bool has_key_path = DecodeBool(it->Value().begin(), it->Value().end()); - // This check accounts for two layers of legacy coding: - // (1) Initially, has_key_path was added to distinguish null vs. string. - // (2) Later, null vs. string vs. array was stored in the key_path itself. - // So this check is only relevant for string-type key_paths. - if (!has_key_path && - (key_path.type() == WebKit::WebIDBKeyPath::StringType && - !key_path.string().empty())) { - INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES); - break; - } - if (!has_key_path) - key_path = IndexedDBKeyPath(); - it->Next(); - } - - int64 key_generator_current_number = -1; - if (CheckObjectStoreAndMetaDataType( - it.get(), - stop_key, - object_store_id, - ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER)) { - key_generator_current_number = - DecodeInt(it->Value().begin(), it->Value().end()); - // TODO(jsbell): Return key_generator_current_number, cache in - // object store, and write lazily to backing store. For now, - // just assert that if it was written it was valid. - DCHECK_GE(key_generator_current_number, kKeyGeneratorInitialNumber); - it->Next(); - } - - IndexedDBObjectStoreMetadata metadata(object_store_name, - object_store_id, - key_path, - auto_increment, - max_index_id); - if (!GetIndexes(database_id, object_store_id, &metadata.indexes)) - return false; - (*object_stores)[object_store_id] = metadata; - } - return true; -} - -WARN_UNUSED_RESULT static bool SetMaxObjectStoreId( - LevelDBTransaction* transaction, - int64 database_id, - int64 object_store_id) { - const std::vector<char> max_object_store_id_key = DatabaseMetaDataKey::Encode( - database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID); - int64 max_object_store_id = -1; - bool ok = GetMaxObjectStoreId( - transaction, max_object_store_id_key, max_object_store_id); - if (!ok) { - INTERNAL_READ_ERROR(SET_MAX_OBJECT_STORE_ID); - return false; - } - - if (object_store_id <= max_object_store_id) { - INTERNAL_CONSISTENCY_ERROR(SET_MAX_OBJECT_STORE_ID); - return false; - } - PutInt(transaction, LevelDBSlice(max_object_store_id_key), object_store_id); - return true; -} - -bool IndexedDBBackingStore::CreateObjectStore( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - const string16& name, - const IndexedDBKeyPath& key_path, - bool auto_increment) { - IDB_TRACE("IndexedDBBackingStore::create_object_store"); - if (!KeyPrefix::ValidIds(database_id, object_store_id)) - return false; - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - if (!SetMaxObjectStoreId(leveldb_transaction, database_id, object_store_id)) - return false; - - const std::vector<char> name_key = ObjectStoreMetaDataKey::Encode( - database_id, object_store_id, ObjectStoreMetaDataKey::NAME); - const std::vector<char> key_path_key = ObjectStoreMetaDataKey::Encode( - database_id, object_store_id, ObjectStoreMetaDataKey::KEY_PATH); - const std::vector<char> auto_increment_key = ObjectStoreMetaDataKey::Encode( - database_id, object_store_id, ObjectStoreMetaDataKey::AUTO_INCREMENT); - const std::vector<char> evictable_key = ObjectStoreMetaDataKey::Encode( - database_id, object_store_id, ObjectStoreMetaDataKey::EVICTABLE); - const std::vector<char> last_version_key = ObjectStoreMetaDataKey::Encode( - database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION); - const std::vector<char> max_index_id_key = ObjectStoreMetaDataKey::Encode( - database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID); - const std::vector<char> has_key_path_key = ObjectStoreMetaDataKey::Encode( - database_id, object_store_id, ObjectStoreMetaDataKey::HAS_KEY_PATH); - const std::vector<char> key_generator_current_number_key = - ObjectStoreMetaDataKey::Encode( - database_id, - object_store_id, - ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER); - const std::vector<char> names_key = - ObjectStoreNamesKey::Encode(database_id, name); - - PutString(leveldb_transaction, LevelDBSlice(name_key), name); - PutIDBKeyPath(leveldb_transaction, LevelDBSlice(key_path_key), key_path); - PutInt(leveldb_transaction, LevelDBSlice(auto_increment_key), auto_increment); - PutInt(leveldb_transaction, LevelDBSlice(evictable_key), false); - PutInt(leveldb_transaction, LevelDBSlice(last_version_key), 1); - PutInt(leveldb_transaction, LevelDBSlice(max_index_id_key), kMinimumIndexId); - PutBool( - leveldb_transaction, LevelDBSlice(has_key_path_key), !key_path.IsNull()); - PutInt(leveldb_transaction, - LevelDBSlice(key_generator_current_number_key), - kKeyGeneratorInitialNumber); - PutInt(leveldb_transaction, LevelDBSlice(names_key), object_store_id); - return true; -} - -bool IndexedDBBackingStore::DeleteObjectStore( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id) { - IDB_TRACE("IndexedDBBackingStore::delete_object_store"); - if (!KeyPrefix::ValidIds(database_id, object_store_id)) - return false; - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - - string16 object_store_name; - bool found = false; - bool ok = GetString( - leveldb_transaction, - LevelDBSlice(ObjectStoreMetaDataKey::Encode( - database_id, object_store_id, ObjectStoreMetaDataKey::NAME)), - object_store_name, - found); - if (!ok) { - INTERNAL_READ_ERROR(DELETE_OBJECT_STORE); - return false; - } - if (!found) { - INTERNAL_CONSISTENCY_ERROR(DELETE_OBJECT_STORE); - return false; - } - - DeleteRange( - leveldb_transaction, - ObjectStoreMetaDataKey::Encode(database_id, object_store_id, 0), - ObjectStoreMetaDataKey::EncodeMaxKey(database_id, object_store_id)); - - leveldb_transaction->Remove(LevelDBSlice( - ObjectStoreNamesKey::Encode(database_id, object_store_name))); - - DeleteRange(leveldb_transaction, - IndexFreeListKey::Encode(database_id, object_store_id, 0), - IndexFreeListKey::EncodeMaxKey(database_id, object_store_id)); - DeleteRange(leveldb_transaction, - IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0), - IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id)); - - return ClearObjectStore(transaction, database_id, object_store_id); -} - -bool IndexedDBBackingStore::GetRecord( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - const IndexedDBKey& key, - std::vector<char>& record) { - IDB_TRACE("IndexedDBBackingStore::get_record"); - if (!KeyPrefix::ValidIds(database_id, object_store_id)) - return false; - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - - const std::vector<char> leveldb_key = - ObjectStoreDataKey::Encode(database_id, object_store_id, key); - std::vector<char> data; - - record.clear(); - - bool found = false; - bool ok = leveldb_transaction->Get(LevelDBSlice(leveldb_key), data, found); - if (!ok) { - INTERNAL_READ_ERROR(GET_RECORD); - return false; - } - if (!found) - return true; - if (!data.size()) { - INTERNAL_READ_ERROR(GET_RECORD); - return false; - } - - int64 version; - const char* p = DecodeVarInt(&*data.begin(), &*data.rbegin() + 1, version); - if (!p) { - INTERNAL_READ_ERROR(GET_RECORD); - return false; - } - - record.insert(record.end(), p, static_cast<const char*>(&*data.rbegin()) + 1); - return true; -} - -WARN_UNUSED_RESULT static bool GetNewVersionNumber( - LevelDBTransaction* transaction, - int64 database_id, - int64 object_store_id, - int64& new_version_number) { - const std::vector<char> last_version_key = ObjectStoreMetaDataKey::Encode( - database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION); - - new_version_number = -1; - int64 last_version = -1; - bool found = false; - bool ok = - GetInt(transaction, LevelDBSlice(last_version_key), last_version, found); - if (!ok) { - INTERNAL_READ_ERROR(GET_NEW_VERSION_NUMBER); - return false; - } - if (!found) - last_version = 0; - - DCHECK_GE(last_version, 0); - - int64 version = last_version + 1; - PutInt(transaction, LevelDBSlice(last_version_key), version); - - DCHECK(version > - last_version); // TODO(jsbell): Think about how we want to handle - // the overflow scenario. - - new_version_number = version; - return true; -} - -bool IndexedDBBackingStore::PutRecord( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - const IndexedDBKey& key, - const std::vector<char>& value, - RecordIdentifier* record_identifier) { - IDB_TRACE("IndexedDBBackingStore::put_record"); - if (!KeyPrefix::ValidIds(database_id, object_store_id)) - return false; - DCHECK(key.IsValid()); - - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - int64 version = -1; - bool ok = GetNewVersionNumber( - leveldb_transaction, database_id, object_store_id, version); - if (!ok) - return false; - DCHECK_GE(version, 0); - const std::vector<char> object_storedata_key = - ObjectStoreDataKey::Encode(database_id, object_store_id, key); - - std::vector<char> v(EncodeVarInt(version)); - - v.insert(v.end(), value.begin(), value.end()); - - leveldb_transaction->Put(LevelDBSlice(object_storedata_key), v); - - const std::vector<char> exists_entry_key = - ExistsEntryKey::Encode(database_id, object_store_id, key); - leveldb_transaction->Put(LevelDBSlice(exists_entry_key), EncodeInt(version)); - - record_identifier->Reset(EncodeIDBKey(key), version); - return true; -} - -bool IndexedDBBackingStore::ClearObjectStore( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id) { - IDB_TRACE("IndexedDBBackingStore::clear_object_store"); - if (!KeyPrefix::ValidIds(database_id, object_store_id)) - return false; - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - const std::vector<char> start_key = - KeyPrefix(database_id, object_store_id).Encode(); - const std::vector<char> stop_key = - KeyPrefix(database_id, object_store_id + 1).Encode(); - - DeleteRange(leveldb_transaction, start_key, stop_key); - return true; -} - -bool IndexedDBBackingStore::DeleteRecord( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - const RecordIdentifier& record_identifier) { - IDB_TRACE("IndexedDBBackingStore::delete_record"); - if (!KeyPrefix::ValidIds(database_id, object_store_id)) - return false; - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - - const std::vector<char> object_store_data_key = ObjectStoreDataKey::Encode( - database_id, object_store_id, record_identifier.primary_key()); - leveldb_transaction->Remove(LevelDBSlice(object_store_data_key)); - - const std::vector<char> exists_entry_key = ExistsEntryKey::Encode( - database_id, object_store_id, record_identifier.primary_key()); - leveldb_transaction->Remove(LevelDBSlice(exists_entry_key)); - return true; -} - -bool IndexedDBBackingStore::GetKeyGeneratorCurrentNumber( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - int64& key_generator_current_number) { - if (!KeyPrefix::ValidIds(database_id, object_store_id)) - return false; - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - - const std::vector<char> key_generator_current_number_key = - ObjectStoreMetaDataKey::Encode( - database_id, - object_store_id, - ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER); - - key_generator_current_number = -1; - std::vector<char> data; - - bool found = false; - bool ok = leveldb_transaction->Get( - LevelDBSlice(key_generator_current_number_key), data, found); - if (!ok) { - INTERNAL_READ_ERROR(GET_KEY_GENERATOR_CURRENT_NUMBER); - return false; - } - if (found) { - key_generator_current_number = 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. - // TODO(jsbell): Fix this for all stores on database open? - const std::vector<char> start_key = - ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey()); - const std::vector<char> stop_key = - ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey()); - - scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator(); - int64 max_numeric_key = 0; - - for (it->Seek(LevelDBSlice(start_key)); - it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(stop_key)) < 0; - it->Next()) { - const char* p = it->Key().begin(); - const char* limit = it->Key().end(); - - ObjectStoreDataKey data_key; - p = ObjectStoreDataKey::Decode(p, limit, &data_key); - DCHECK(p); - - scoped_ptr<IndexedDBKey> user_key = data_key.user_key(); - if (user_key->type() == WebKit::WebIDBKey::NumberType) { - int64 n = static_cast<int64>(user_key->number()); - if (n > max_numeric_key) - max_numeric_key = n; - } - } - - key_generator_current_number = max_numeric_key + 1; - } - - return true; -} - -bool IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - int64 new_number, - bool check_current) { - if (!KeyPrefix::ValidIds(database_id, object_store_id)) - return false; - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - - if (check_current) { - int64 current_number; - bool ok = GetKeyGeneratorCurrentNumber( - transaction, database_id, object_store_id, current_number); - if (!ok) - return false; - if (new_number <= current_number) - return true; - } - - const std::vector<char> key_generator_current_number_key = - ObjectStoreMetaDataKey::Encode( - database_id, - object_store_id, - ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER); - PutInt(leveldb_transaction, - LevelDBSlice(key_generator_current_number_key), - new_number); - return true; -} - -bool IndexedDBBackingStore::KeyExistsInObjectStore( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - const IndexedDBKey& key, - RecordIdentifier* found_record_identifier, - bool& found) { - IDB_TRACE("IndexedDBBackingStore::key_exists_in_object_store"); - if (!KeyPrefix::ValidIds(database_id, object_store_id)) - return false; - found = false; - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - const std::vector<char> leveldb_key = - ObjectStoreDataKey::Encode(database_id, object_store_id, key); - std::vector<char> data; - - bool ok = leveldb_transaction->Get(LevelDBSlice(leveldb_key), data, found); - if (!ok) { - INTERNAL_READ_ERROR(KEY_EXISTS_IN_OBJECT_STORE); - return false; - } - if (!found) - return true; - if (!data.size()) { - INTERNAL_READ_ERROR(KEY_EXISTS_IN_OBJECT_STORE); - return false; - } - - int64 version; - if (DecodeVarInt(&*data.begin(), &*data.rbegin() + 1, version) == 0) - return false; - - found_record_identifier->Reset(EncodeIDBKey(key), version); - return true; -} - -static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it, - const std::vector<char>& stop_key, - int64 index_id, - unsigned char meta_data_type) { - if (!it->IsValid() || CompareKeys(it->Key(), LevelDBSlice(stop_key)) >= 0) - return false; - - IndexMetaDataKey meta_data_key; - const char* p = IndexMetaDataKey::Decode( - it->Key().begin(), it->Key().end(), &meta_data_key); - DCHECK(p); - if (meta_data_key.IndexId() != index_id) - return false; - if (meta_data_key.meta_data_type() != meta_data_type) - return false; - return true; -} - -// TODO(jsbell): This should do some error handling rather than plowing ahead -// when bad -// data is encountered. -bool IndexedDBBackingStore::GetIndexes( - int64 database_id, - int64 object_store_id, - IndexedDBObjectStoreMetadata::IndexMap* indexes) { - IDB_TRACE("IndexedDBBackingStore::get_indexes"); - if (!KeyPrefix::ValidIds(database_id, object_store_id)) - return false; - const std::vector<char> start_key = - IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0); - const std::vector<char> stop_key = - IndexMetaDataKey::Encode(database_id, object_store_id + 1, 0, 0); - - DCHECK(indexes->empty()); - - scoped_ptr<LevelDBIterator> it = db_->CreateIterator(); - it->Seek(LevelDBSlice(start_key)); - while (it->IsValid() && - CompareKeys(LevelDBSlice(it->Key()), LevelDBSlice(stop_key)) < 0) { - const char* p = it->Key().begin(); - const char* limit = it->Key().end(); - - IndexMetaDataKey meta_data_key; - p = IndexMetaDataKey::Decode(p, limit, &meta_data_key); - DCHECK(p); - if (meta_data_key.meta_data_type() != IndexMetaDataKey::NAME) { - INTERNAL_CONSISTENCY_ERROR(GET_INDEXES); - // Possible stale metadata due to http://webkit.org/b/85557 but don't fail - // the load. - it->Next(); - continue; - } - - // TODO(jsbell): Do this by direct key lookup rather than iteration, to - // simplify. - int64 index_id = meta_data_key.IndexId(); - string16 index_name = DecodeString(it->Value().begin(), it->Value().end()); - - it->Next(); // unique flag - if (!CheckIndexAndMetaDataKey( - it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) { - INTERNAL_CONSISTENCY_ERROR(GET_INDEXES); - break; - } - bool index_unique = DecodeBool(it->Value().begin(), it->Value().end()); - - it->Next(); // key_path - if (!CheckIndexAndMetaDataKey( - it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) { - INTERNAL_CONSISTENCY_ERROR(GET_INDEXES); - break; - } - IndexedDBKeyPath key_path = - DecodeIDBKeyPath(it->Value().begin(), it->Value().end()); - - it->Next(); // [optional] multi_entry flag - bool index_multi_entry = false; - if (CheckIndexAndMetaDataKey( - it.get(), stop_key, index_id, IndexMetaDataKey::MULTI_ENTRY)) { - index_multi_entry = DecodeBool(it->Value().begin(), it->Value().end()); - it->Next(); - } - - (*indexes)[index_id] = IndexedDBIndexMetadata( - index_name, index_id, key_path, index_unique, index_multi_entry); - } - return true; -} - -WARN_UNUSED_RESULT static bool SetMaxIndexId(LevelDBTransaction* transaction, - int64 database_id, - int64 object_store_id, - int64 index_id) { - int64 max_index_id = -1; - const std::vector<char> max_index_id_key = ObjectStoreMetaDataKey::Encode( - database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID); - bool found = false; - bool ok = - GetInt(transaction, LevelDBSlice(max_index_id_key), max_index_id, found); - if (!ok) { - INTERNAL_READ_ERROR(SET_MAX_INDEX_ID); - return false; - } - if (!found) - max_index_id = kMinimumIndexId; - - if (index_id <= max_index_id) { - INTERNAL_CONSISTENCY_ERROR(SET_MAX_INDEX_ID); - return false; - } - - PutInt(transaction, LevelDBSlice(max_index_id_key), index_id); - return true; -} - -bool IndexedDBBackingStore::CreateIndex( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - int64 index_id, - const string16& name, - const IndexedDBKeyPath& key_path, - bool is_unique, - bool is_multi_entry) { - IDB_TRACE("IndexedDBBackingStore::create_index"); - if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id)) - return false; - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - if (!SetMaxIndexId( - leveldb_transaction, database_id, object_store_id, index_id)) - return false; - - const std::vector<char> name_key = IndexMetaDataKey::Encode( - database_id, object_store_id, index_id, IndexMetaDataKey::NAME); - const std::vector<char> unique_key = IndexMetaDataKey::Encode( - database_id, object_store_id, index_id, IndexMetaDataKey::UNIQUE); - const std::vector<char> key_path_key = IndexMetaDataKey::Encode( - database_id, object_store_id, index_id, IndexMetaDataKey::KEY_PATH); - const std::vector<char> multi_entry_key = IndexMetaDataKey::Encode( - database_id, object_store_id, index_id, IndexMetaDataKey::MULTI_ENTRY); - - PutString(leveldb_transaction, LevelDBSlice(name_key), name); - PutBool(leveldb_transaction, LevelDBSlice(unique_key), is_unique); - PutIDBKeyPath(leveldb_transaction, LevelDBSlice(key_path_key), key_path); - PutBool(leveldb_transaction, LevelDBSlice(multi_entry_key), is_multi_entry); - return true; -} - -bool IndexedDBBackingStore::DeleteIndex( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - int64 index_id) { - IDB_TRACE("IndexedDBBackingStore::delete_index"); - if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id)) - return false; - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - - const std::vector<char> index_meta_data_start = - IndexMetaDataKey::Encode(database_id, object_store_id, index_id, 0); - const std::vector<char> index_meta_data_end = - IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id, index_id); - DeleteRange(leveldb_transaction, index_meta_data_start, index_meta_data_end); - - const std::vector<char> index_data_start = - IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id); - const std::vector<char> index_data_end = - IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id); - DeleteRange(leveldb_transaction, index_data_start, index_data_end); - return true; -} - -bool IndexedDBBackingStore::PutIndexDataForRecord( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - int64 index_id, - const IndexedDBKey& key, - const RecordIdentifier& record_identifier) { - IDB_TRACE("IndexedDBBackingStore::put_index_data_for_record"); - DCHECK(key.IsValid()); - if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id)) - return false; - - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - const std::vector<char> index_data_key = - IndexDataKey::Encode(database_id, - object_store_id, - index_id, - EncodeIDBKey(key), - record_identifier.primary_key()); - - std::vector<char> data(EncodeVarInt(record_identifier.version())); - const std::vector<char>& primary_key = record_identifier.primary_key(); - data.insert(data.end(), primary_key.begin(), primary_key.end()); - - leveldb_transaction->Put(LevelDBSlice(index_data_key), data); - return true; -} - -static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction, - const std::vector<char>& target, - std::vector<char>& found_key) { - scoped_ptr<LevelDBIterator> it = transaction->CreateIterator(); - it->Seek(LevelDBSlice(target)); - - if (!it->IsValid()) { - it->SeekToLast(); - if (!it->IsValid()) - return false; - } - - while (CompareIndexKeys(LevelDBSlice(it->Key()), LevelDBSlice(target)) > 0) { - it->Prev(); - if (!it->IsValid()) - return false; - } - - do { - found_key.assign(it->Key().begin(), it->Key().end()); - - // There can be several index keys that compare equal. We want the last one. - it->Next(); - } while (it->IsValid() && !CompareIndexKeys(it->Key(), LevelDBSlice(target))); - - return true; -} - -static bool VersionExists(LevelDBTransaction* transaction, - int64 database_id, - int64 object_store_id, - int64 version, - const std::vector<char>& encoded_primary_key, - bool& exists) { - const std::vector<char> key = - ExistsEntryKey::Encode(database_id, object_store_id, encoded_primary_key); - std::vector<char> data; - - bool ok = transaction->Get(LevelDBSlice(key), data, exists); - if (!ok) { - INTERNAL_READ_ERROR(VERSION_EXISTS); - return false; - } - if (!exists) - return true; - - exists = (DecodeInt(data.begin(), data.end()) == version); - return true; -} - -bool IndexedDBBackingStore::FindKeyInIndex( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - int64 index_id, - const IndexedDBKey& key, - std::vector<char>& found_encoded_primary_key, - bool& found) { - IDB_TRACE("IndexedDBBackingStore::find_key_in_index"); - DCHECK(KeyPrefix::ValidIds(database_id, object_store_id, index_id)); - - DCHECK(found_encoded_primary_key.empty()); - found = false; - - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - const std::vector<char> leveldb_key = - IndexDataKey::Encode(database_id, object_store_id, index_id, key); - scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator(); - it->Seek(LevelDBSlice(leveldb_key)); - - for (;;) { - if (!it->IsValid()) - return true; - if (CompareIndexKeys(it->Key(), LevelDBSlice(leveldb_key)) > 0) - return true; - - int64 version; - const char* p = - DecodeVarInt(it->Value().begin(), it->Value().end(), version); - if (!p) { - INTERNAL_READ_ERROR(FIND_KEY_IN_INDEX); - return false; - } - found_encoded_primary_key.insert( - found_encoded_primary_key.end(), p, it->Value().end()); - - bool exists = false; - bool ok = VersionExists(leveldb_transaction, - database_id, - object_store_id, - version, - found_encoded_primary_key, - exists); - if (!ok) - return false; - if (!exists) { - // Delete stale index data entry and continue. - leveldb_transaction->Remove(it->Key()); - it->Next(); - continue; - } - found = true; - return true; - } -} - -bool IndexedDBBackingStore::GetPrimaryKeyViaIndex( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - int64 index_id, - const IndexedDBKey& key, - scoped_ptr<IndexedDBKey>* primary_key) { - IDB_TRACE("IndexedDBBackingStore::get_primary_key_via_index"); - if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id)) - return false; - - bool found = false; - std::vector<char> found_encoded_primary_key; - bool ok = FindKeyInIndex(transaction, - database_id, - object_store_id, - index_id, - key, - found_encoded_primary_key, - found); - if (!ok) { - INTERNAL_READ_ERROR(GET_PRIMARY_KEY_VIA_INDEX); - return false; - } - if (!found) - return true; - if (!found_encoded_primary_key.size()) { - INTERNAL_READ_ERROR(GET_PRIMARY_KEY_VIA_INDEX); - return false; - } - - DecodeIDBKey(&*found_encoded_primary_key.begin(), - &*found_encoded_primary_key.rbegin() + 1, - primary_key); - return true; -} - -bool IndexedDBBackingStore::KeyExistsInIndex( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - int64 index_id, - const IndexedDBKey& index_key, - scoped_ptr<IndexedDBKey>* found_primary_key, - bool& exists) { - IDB_TRACE("IndexedDBBackingStore::key_exists_in_index"); - if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id)) - return false; - - exists = false; - std::vector<char> found_encoded_primary_key; - bool ok = FindKeyInIndex(transaction, - database_id, - object_store_id, - index_id, - index_key, - found_encoded_primary_key, - exists); - if (!ok) { - INTERNAL_READ_ERROR(KEY_EXISTS_IN_INDEX); - return false; - } - if (!exists) - return true; - if (!found_encoded_primary_key.size()) { - INTERNAL_READ_ERROR(KEY_EXISTS_IN_INDEX); - return false; - } - - DecodeIDBKey(&*found_encoded_primary_key.begin(), - &*found_encoded_primary_key.rbegin() + 1, - found_primary_key); - return true; -} - -IndexedDBBackingStore::Cursor::Cursor( - const IndexedDBBackingStore::Cursor* other) - : transaction_(other->transaction_), - cursor_options_(other->cursor_options_), - current_key_(new IndexedDBKey(*other->current_key_)) { - if (other->iterator_) { - iterator_ = transaction_->CreateIterator(); - - if (other->iterator_->IsValid()) { - iterator_->Seek(other->iterator_->Key()); - DCHECK(iterator_->IsValid()); - } - } -} - -IndexedDBBackingStore::Cursor::Cursor(LevelDBTransaction* transaction, - const CursorOptions& cursor_options) - : transaction_(transaction), cursor_options_(cursor_options) {} -IndexedDBBackingStore::Cursor::~Cursor() {} - -bool IndexedDBBackingStore::Cursor::FirstSeek() { - iterator_ = transaction_->CreateIterator(); - if (cursor_options_.forward) - iterator_->Seek(LevelDBSlice(cursor_options_.low_key)); - else - iterator_->Seek(LevelDBSlice(cursor_options_.high_key)); - - return ContinueFunction(0, READY); -} - -bool IndexedDBBackingStore::Cursor::Advance(unsigned long count) { - while (count--) { - if (!ContinueFunction()) - return false; - } - return true; -} - -bool IndexedDBBackingStore::Cursor::ContinueFunction(const IndexedDBKey* key, - IteratorState next_state) { - // TODO(alecflett): avoid a copy here? - IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey(); - - bool first_iteration = true; - - // When iterating with PrevNoDuplicate, spec requires that the - // value we yield for each key is the first duplicate in forwards - // order. - IndexedDBKey last_duplicate_key; - - bool forward = cursor_options_.forward; - - for (;;) { - if (next_state == SEEK) { - // TODO(jsbell): Optimize seeking for reverse cursors as well. - if (first_iteration && key && key->IsValid() && forward) { - iterator_->Seek(LevelDBSlice(EncodeKey(*key))); - first_iteration = false; - } else if (forward) { - iterator_->Next(); - } else { - iterator_->Prev(); - } - } else { - next_state = SEEK; // for subsequent iterations - } - - if (!iterator_->IsValid()) { - if (!forward && last_duplicate_key.IsValid()) { - // We need to walk forward because we hit the end of - // the data. - forward = true; - continue; - } - - return false; - } - - if (IsPastBounds()) { - if (!forward && last_duplicate_key.IsValid()) { - // We need to walk forward because now we're beyond the - // bounds defined by the cursor. - forward = true; - continue; - } - - return false; - } - - if (!HaveEnteredRange()) - continue; - - // The row may not load because there's a stale entry in the - // index. This is not fatal. - if (!LoadCurrentRow()) - continue; - - if (key && key->IsValid()) { - if (forward) { - if (current_key_->IsLessThan(*key)) - continue; - } else { - if (key->IsLessThan(*current_key_)) - continue; - } - } - - if (cursor_options_.unique) { - if (previous_key.IsValid() && current_key_->IsEqual(previous_key)) { - // We should never be able to walk forward all the way - // to the previous key. - DCHECK(!last_duplicate_key.IsValid()); - continue; - } - - if (!forward) { - if (!last_duplicate_key.IsValid()) { - last_duplicate_key = *current_key_; - continue; - } - - // We need to walk forward because we hit the boundary - // between key ranges. - if (!last_duplicate_key.IsEqual(*current_key_)) { - forward = true; - continue; - } - - continue; - } - } - break; - } - - DCHECK(!last_duplicate_key.IsValid() || - (forward && last_duplicate_key.IsEqual(*current_key_))); - return true; -} - -bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const { - if (cursor_options_.forward) { - int compare = CompareIndexKeys(iterator_->Key(), - LevelDBSlice(cursor_options_.low_key)); - if (cursor_options_.low_open) { - return compare > 0; - } - return compare >= 0; - } - int compare = CompareIndexKeys(iterator_->Key(), - LevelDBSlice(cursor_options_.high_key)); - if (cursor_options_.high_open) { - return compare < 0; - } - return compare <= 0; -} - -bool IndexedDBBackingStore::Cursor::IsPastBounds() const { - if (cursor_options_.forward) { - int compare = CompareIndexKeys(iterator_->Key(), - LevelDBSlice(cursor_options_.high_key)); - if (cursor_options_.high_open) { - return compare >= 0; - } - return compare > 0; - } - int compare = - CompareIndexKeys(iterator_->Key(), LevelDBSlice(cursor_options_.low_key)); - if (cursor_options_.low_open) { - return compare <= 0; - } - return compare < 0; -} - -const IndexedDBKey& IndexedDBBackingStore::Cursor::primary_key() const { - return *current_key_; -} - -const IndexedDBBackingStore::RecordIdentifier& -IndexedDBBackingStore::Cursor::record_identifier() const { - return record_identifier_; -} - -class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor { - public: - ObjectStoreKeyCursorImpl( - LevelDBTransaction* transaction, - const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) - : IndexedDBBackingStore::Cursor(transaction, cursor_options) {} - - virtual Cursor* Clone() OVERRIDE { - return new ObjectStoreKeyCursorImpl(this); - } - - // IndexedDBBackingStore::Cursor - virtual std::vector<char>* Value() OVERRIDE { - NOTREACHED(); - return NULL; - } - virtual bool LoadCurrentRow() OVERRIDE; - - protected: - virtual std::vector<char> EncodeKey(const IndexedDBKey& key) OVERRIDE { - return ObjectStoreDataKey::Encode( - cursor_options_.database_id, cursor_options_.object_store_id, key); - } - - private: - explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other) - : IndexedDBBackingStore::Cursor(other) {} -}; - -bool ObjectStoreKeyCursorImpl::LoadCurrentRow() { - const char* key_position = iterator_->Key().begin(); - const char* key_limit = iterator_->Key().end(); - - ObjectStoreDataKey object_store_data_key; - key_position = ObjectStoreDataKey::Decode( - key_position, key_limit, &object_store_data_key); - if (!key_position) { - INTERNAL_READ_ERROR(LOAD_CURRENT_ROW); - return false; - } - - current_key_ = object_store_data_key.user_key(); - - int64 version; - const char* value_position = DecodeVarInt( - iterator_->Value().begin(), iterator_->Value().end(), version); - if (!value_position) { - INTERNAL_READ_ERROR(LOAD_CURRENT_ROW); - return false; - } - - // TODO(jsbell): This re-encodes what was just decoded; try and optimize. - record_identifier_.Reset(EncodeIDBKey(*current_key_), version); - - return true; -} - -class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor { - public: - ObjectStoreCursorImpl( - LevelDBTransaction* transaction, - const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) - : IndexedDBBackingStore::Cursor(transaction, cursor_options) {} - - virtual Cursor* Clone() OVERRIDE { return new ObjectStoreCursorImpl(this); } - - // IndexedDBBackingStore::Cursor - virtual std::vector<char>* Value() OVERRIDE { return ¤t_value_; } - virtual bool LoadCurrentRow() OVERRIDE; - - protected: - virtual std::vector<char> EncodeKey(const IndexedDBKey& key) OVERRIDE { - return ObjectStoreDataKey::Encode( - cursor_options_.database_id, cursor_options_.object_store_id, key); - } - - private: - explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl* other) - : IndexedDBBackingStore::Cursor(other), - current_value_(other->current_value_) {} - - std::vector<char> current_value_; -}; - -bool ObjectStoreCursorImpl::LoadCurrentRow() { - const char* key_position = iterator_->Key().begin(); - const char* key_limit = iterator_->Key().end(); - - ObjectStoreDataKey object_store_data_key; - key_position = ObjectStoreDataKey::Decode( - key_position, key_limit, &object_store_data_key); - if (!key_position) { - INTERNAL_READ_ERROR(LOAD_CURRENT_ROW); - return false; - } - - current_key_ = object_store_data_key.user_key(); - - int64 version; - const char* value_position = DecodeVarInt( - iterator_->Value().begin(), iterator_->Value().end(), version); - if (!value_position) { - INTERNAL_READ_ERROR(LOAD_CURRENT_ROW); - return false; - } - - // TODO(jsbell): This re-encodes what was just decoded; try and optimize. - record_identifier_.Reset(EncodeIDBKey(*current_key_), version); - - std::vector<char> value; - value.insert(value.end(), value_position, iterator_->Value().end()); - current_value_.swap(value); - return true; -} - -class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor { - public: - IndexKeyCursorImpl( - LevelDBTransaction* transaction, - const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) - : IndexedDBBackingStore::Cursor(transaction, cursor_options) {} - - virtual Cursor* Clone() OVERRIDE { return new IndexKeyCursorImpl(this); } - - // IndexedDBBackingStore::Cursor - virtual std::vector<char>* Value() OVERRIDE { - NOTREACHED(); - return NULL; - } - virtual const IndexedDBKey& primary_key() const OVERRIDE { - return *primary_key_; - } - virtual const IndexedDBBackingStore::RecordIdentifier& RecordIdentifier() - const { - NOTREACHED(); - return record_identifier_; - } - virtual bool LoadCurrentRow() OVERRIDE; - - protected: - virtual std::vector<char> EncodeKey(const IndexedDBKey& key) OVERRIDE { - return IndexDataKey::Encode(cursor_options_.database_id, - cursor_options_.object_store_id, - cursor_options_.index_id, - key); - } - - private: - explicit IndexKeyCursorImpl(const IndexKeyCursorImpl* other) - : IndexedDBBackingStore::Cursor(other), - primary_key_(new IndexedDBKey(*other->primary_key_)) {} - - scoped_ptr<IndexedDBKey> primary_key_; -}; - -bool IndexKeyCursorImpl::LoadCurrentRow() { - const char* key_position = iterator_->Key().begin(); - const char* key_limit = iterator_->Key().end(); - - IndexDataKey index_data_key; - key_position = IndexDataKey::Decode(key_position, key_limit, &index_data_key); - - current_key_ = index_data_key.user_key(); - DCHECK(current_key_); - - int64 index_data_version; - const char* value_position = DecodeVarInt( - iterator_->Value().begin(), iterator_->Value().end(), index_data_version); - if (!value_position) { - INTERNAL_READ_ERROR(LOAD_CURRENT_ROW); - return false; - } - - value_position = - DecodeIDBKey(value_position, iterator_->Value().end(), &primary_key_); - if (!value_position) { - INTERNAL_READ_ERROR(LOAD_CURRENT_ROW); - return false; - } - - std::vector<char> primary_leveldb_key = - ObjectStoreDataKey::Encode(index_data_key.DatabaseId(), - index_data_key.ObjectStoreId(), - *primary_key_); - - std::vector<char> result; - bool found = false; - bool ok = transaction_->Get(LevelDBSlice(primary_leveldb_key), result, found); - if (!ok) { - INTERNAL_READ_ERROR(LOAD_CURRENT_ROW); - return false; - } - if (!found) { - transaction_->Remove(iterator_->Key()); - return false; - } - if (!result.size()) { - INTERNAL_READ_ERROR(LOAD_CURRENT_ROW); - return false; - } - - int64 object_store_data_version; - const char* t = DecodeVarInt( - &*result.begin(), &*result.rbegin() + 1, object_store_data_version); - if (!t) { - INTERNAL_READ_ERROR(LOAD_CURRENT_ROW); - return false; - } - - if (object_store_data_version != index_data_version) { - transaction_->Remove(iterator_->Key()); - return false; - } - - return true; -} - -class IndexCursorImpl : public IndexedDBBackingStore::Cursor { - public: - IndexCursorImpl( - LevelDBTransaction* transaction, - const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) - : IndexedDBBackingStore::Cursor(transaction, cursor_options) {} - - virtual Cursor* Clone() OVERRIDE { return new IndexCursorImpl(this); } - - // IndexedDBBackingStore::Cursor - virtual std::vector<char>* Value() OVERRIDE { return ¤t_value_; } - virtual const IndexedDBKey& primary_key() const OVERRIDE { - return *primary_key_; - } - virtual const IndexedDBBackingStore::RecordIdentifier& RecordIdentifier() - const { - NOTREACHED(); - return record_identifier_; - } - virtual bool LoadCurrentRow() OVERRIDE; - - protected: - virtual std::vector<char> EncodeKey(const IndexedDBKey& key) OVERRIDE { - return IndexDataKey::Encode(cursor_options_.database_id, - cursor_options_.object_store_id, - cursor_options_.index_id, - key); - } - - private: - explicit IndexCursorImpl(const IndexCursorImpl* other) - : IndexedDBBackingStore::Cursor(other), - primary_key_(new IndexedDBKey(*other->primary_key_)), - current_value_(other->current_value_), - primary_leveldb_key_(other->primary_leveldb_key_) {} - - scoped_ptr<IndexedDBKey> primary_key_; - std::vector<char> current_value_; - std::vector<char> primary_leveldb_key_; -}; - -bool IndexCursorImpl::LoadCurrentRow() { - const char* key_position = iterator_->Key().begin(); - const char* key_limit = iterator_->Key().end(); - - IndexDataKey index_data_key; - key_position = IndexDataKey::Decode(key_position, key_limit, &index_data_key); - - current_key_ = index_data_key.user_key(); - DCHECK(current_key_); - - const char* value_position = iterator_->Value().begin(); - const char* value_limit = iterator_->Value().end(); - - int64 index_data_version; - value_position = - DecodeVarInt(value_position, value_limit, index_data_version); - if (!value_position) { - INTERNAL_READ_ERROR(LOAD_CURRENT_ROW); - return false; - } - value_position = DecodeIDBKey(value_position, value_limit, &primary_key_); - if (!value_position) { - INTERNAL_READ_ERROR(LOAD_CURRENT_ROW); - return false; - } - - primary_leveldb_key_ = - ObjectStoreDataKey::Encode(index_data_key.DatabaseId(), - index_data_key.ObjectStoreId(), - *primary_key_); - - std::vector<char> result; - bool found = false; - bool ok = - transaction_->Get(LevelDBSlice(primary_leveldb_key_), result, found); - if (!ok) { - INTERNAL_READ_ERROR(LOAD_CURRENT_ROW); - return false; - } - if (!found) { - transaction_->Remove(iterator_->Key()); - return false; - } - if (!result.size()) { - INTERNAL_READ_ERROR(LOAD_CURRENT_ROW); - return false; - } - - int64 object_store_data_version; - value_position = DecodeVarInt( - &*result.begin(), &*result.rbegin() + 1, object_store_data_version); - if (!value_position) { - INTERNAL_READ_ERROR(LOAD_CURRENT_ROW); - return false; - } - - if (object_store_data_version != index_data_version) { - transaction_->Remove(iterator_->Key()); - return false; - } - - // TODO(jsbell): Make value_position an iterator. - std::vector<char> value; - value.insert(value.end(), - value_position, - static_cast<const char*>(&*result.rbegin()) + 1); - current_value_.swap(value); - return true; -} - -bool ObjectStoreCursorOptions( - LevelDBTransaction* transaction, - int64 database_id, - int64 object_store_id, - const IndexedDBKeyRange& range, - indexed_db::CursorDirection direction, - IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) { - cursor_options.database_id = database_id; - cursor_options.object_store_id = object_store_id; - - bool lower_bound = range.lower().IsValid(); - bool upper_bound = range.upper().IsValid(); - cursor_options.forward = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE || - direction == indexed_db::CURSOR_NEXT); - cursor_options.unique = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE || - direction == indexed_db::CURSOR_PREV_NO_DUPLICATE); - - if (!lower_bound) { - cursor_options.low_key = - ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey()); - cursor_options.low_open = true; // Not included. - } else { - cursor_options.low_key = - ObjectStoreDataKey::Encode(database_id, object_store_id, range.lower()); - cursor_options.low_open = range.lowerOpen(); - } - - if (!upper_bound) { - cursor_options.high_key = - ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey()); - - if (cursor_options.forward) { - cursor_options.high_open = true; // Not included. - } else { - // We need a key that exists. - if (!FindGreatestKeyLessThanOrEqual( - transaction, cursor_options.high_key, cursor_options.high_key)) - return false; - cursor_options.high_open = false; - } - } else { - cursor_options.high_key = - ObjectStoreDataKey::Encode(database_id, object_store_id, range.upper()); - cursor_options.high_open = range.upperOpen(); - - if (!cursor_options.forward) { - // For reverse cursors, we need a key that exists. - std::vector<char> found_high_key; - if (!FindGreatestKeyLessThanOrEqual( - transaction, cursor_options.high_key, found_high_key)) - return false; - - // If the target key should not be included, but we end up with a smaller - // key, we should include that. - if (cursor_options.high_open && - CompareIndexKeys(LevelDBSlice(found_high_key), - LevelDBSlice(cursor_options.high_key)) < - 0) - cursor_options.high_open = false; - - cursor_options.high_key = found_high_key; - } - } - - return true; -} - -bool IndexCursorOptions( - LevelDBTransaction* transaction, - int64 database_id, - int64 object_store_id, - int64 index_id, - const IndexedDBKeyRange& range, - indexed_db::CursorDirection direction, - IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) { - DCHECK(transaction); - if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id)) - return false; - - cursor_options.database_id = database_id; - cursor_options.object_store_id = object_store_id; - cursor_options.index_id = index_id; - - bool lower_bound = range.lower().IsValid(); - bool upper_bound = range.upper().IsValid(); - cursor_options.forward = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE || - direction == indexed_db::CURSOR_NEXT); - cursor_options.unique = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE || - direction == indexed_db::CURSOR_PREV_NO_DUPLICATE); - - if (!lower_bound) { - cursor_options.low_key = - IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id); - cursor_options.low_open = false; // Included. - } else { - cursor_options.low_key = IndexDataKey::Encode( - database_id, object_store_id, index_id, range.lower()); - cursor_options.low_open = range.lowerOpen(); - } - - if (!upper_bound) { - cursor_options.high_key = - IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id); - cursor_options.high_open = false; // Included. - - if (!cursor_options.forward) { // We need a key that exists. - if (!FindGreatestKeyLessThanOrEqual( - transaction, cursor_options.high_key, cursor_options.high_key)) - return false; - cursor_options.high_open = false; - } - } else { - cursor_options.high_key = IndexDataKey::Encode( - database_id, object_store_id, index_id, range.upper()); - cursor_options.high_open = range.upperOpen(); - - std::vector<char> found_high_key; - if (!FindGreatestKeyLessThanOrEqual( - transaction, - cursor_options.high_key, - found_high_key)) // 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 (cursor_options.high_open && - CompareIndexKeys(LevelDBSlice(found_high_key), - LevelDBSlice(cursor_options.high_key)) < - 0) - cursor_options.high_open = false; - - cursor_options.high_key = found_high_key; - } - - return true; -} - -scoped_ptr<IndexedDBBackingStore::Cursor> -IndexedDBBackingStore::OpenObjectStoreCursor( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - const IndexedDBKeyRange& range, - indexed_db::CursorDirection direction) { - IDB_TRACE("IndexedDBBackingStore::open_object_store_cursor"); - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - IndexedDBBackingStore::Cursor::CursorOptions cursor_options; - if (!ObjectStoreCursorOptions(leveldb_transaction, - database_id, - object_store_id, - range, - direction, - cursor_options)) - return scoped_ptr<IndexedDBBackingStore::Cursor>(); - scoped_ptr<ObjectStoreCursorImpl> cursor( - new ObjectStoreCursorImpl(leveldb_transaction, cursor_options)); - if (!cursor->FirstSeek()) - return scoped_ptr<IndexedDBBackingStore::Cursor>(); - - return cursor.PassAs<IndexedDBBackingStore::Cursor>(); -} - -scoped_ptr<IndexedDBBackingStore::Cursor> -IndexedDBBackingStore::OpenObjectStoreKeyCursor( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - const IndexedDBKeyRange& range, - indexed_db::CursorDirection direction) { - IDB_TRACE("IndexedDBBackingStore::open_object_store_key_cursor"); - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - IndexedDBBackingStore::Cursor::CursorOptions cursor_options; - if (!ObjectStoreCursorOptions(leveldb_transaction, - database_id, - object_store_id, - range, - direction, - cursor_options)) - return scoped_ptr<IndexedDBBackingStore::Cursor>(); - scoped_ptr<ObjectStoreKeyCursorImpl> cursor( - new ObjectStoreKeyCursorImpl(leveldb_transaction, cursor_options)); - if (!cursor->FirstSeek()) - return scoped_ptr<IndexedDBBackingStore::Cursor>(); - - return cursor.PassAs<IndexedDBBackingStore::Cursor>(); -} - -scoped_ptr<IndexedDBBackingStore::Cursor> -IndexedDBBackingStore::OpenIndexKeyCursor( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - int64 index_id, - const IndexedDBKeyRange& range, - indexed_db::CursorDirection direction) { - IDB_TRACE("IndexedDBBackingStore::open_index_key_cursor"); - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - IndexedDBBackingStore::Cursor::CursorOptions cursor_options; - if (!IndexCursorOptions(leveldb_transaction, - database_id, - object_store_id, - index_id, - range, - direction, - cursor_options)) - return scoped_ptr<IndexedDBBackingStore::Cursor>(); - scoped_ptr<IndexKeyCursorImpl> cursor( - new IndexKeyCursorImpl(leveldb_transaction, cursor_options)); - if (!cursor->FirstSeek()) - return scoped_ptr<IndexedDBBackingStore::Cursor>(); - - return cursor.PassAs<IndexedDBBackingStore::Cursor>(); -} - -scoped_ptr<IndexedDBBackingStore::Cursor> -IndexedDBBackingStore::OpenIndexCursor( - IndexedDBBackingStore::Transaction* transaction, - int64 database_id, - int64 object_store_id, - int64 index_id, - const IndexedDBKeyRange& range, - indexed_db::CursorDirection direction) { - IDB_TRACE("IndexedDBBackingStore::open_index_cursor"); - LevelDBTransaction* leveldb_transaction = - IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); - IndexedDBBackingStore::Cursor::CursorOptions cursor_options; - if (!IndexCursorOptions(leveldb_transaction, - database_id, - object_store_id, - index_id, - range, - direction, - cursor_options)) - return scoped_ptr<IndexedDBBackingStore::Cursor>(); - scoped_ptr<IndexCursorImpl> cursor( - new IndexCursorImpl(leveldb_transaction, cursor_options)); - if (!cursor->FirstSeek()) - return scoped_ptr<IndexedDBBackingStore::Cursor>(); - - return cursor.PassAs<IndexedDBBackingStore::Cursor>(); -} - -IndexedDBBackingStore::Transaction::Transaction( - IndexedDBBackingStore* backing_store) - : backing_store_(backing_store) {} - -IndexedDBBackingStore::Transaction::~Transaction() {} - -void IndexedDBBackingStore::Transaction::begin() { - IDB_TRACE("IndexedDBBackingStore::Transaction::begin"); - DCHECK(!transaction_); - transaction_ = LevelDBTransaction::Create(backing_store_->db_.get()); -} - -bool IndexedDBBackingStore::Transaction::Commit() { - IDB_TRACE("IndexedDBBackingStore::Transaction::commit"); - DCHECK(transaction_); - bool result = transaction_->Commit(); - transaction_ = NULL; - if (!result) - INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD); - return result; -} - -void IndexedDBBackingStore::Transaction::Rollback() { - IDB_TRACE("IndexedDBBackingStore::Transaction::rollback"); - DCHECK(transaction_); - transaction_->Rollback(); - transaction_ = NULL; -} - -} // namespace content |