Index: webkit/dom_storage/session_storage_database.cc |
diff --git a/webkit/dom_storage/session_storage_database.cc b/webkit/dom_storage/session_storage_database.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b8a50b23dae3792832e41884000012bea29b1ec1 |
--- /dev/null |
+++ b/webkit/dom_storage/session_storage_database.cc |
@@ -0,0 +1,792 @@ |
+// Copyright (c) 2012 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 "webkit/dom_storage/session_storage_database.h" |
+ |
+#include "base/file_util.h" |
+#include "base/stringprintf.h" |
+#include "base/string_number_conversions.h" |
+#include "base/utf_string_conversions.h" |
+#include "googleurl/src/gurl.h" |
+#include "third_party/leveldatabase/src/include/leveldb/db.h" |
+#include "third_party/leveldatabase/src/include/leveldb/iterator.h" |
+#include "third_party/leveldatabase/src/include/leveldb/status.h" |
+#include "third_party/leveldatabase/src/include/leveldb/options.h" |
+#include "third_party/leveldatabase/src/include/leveldb/write_batch.h" |
+ |
+// Layout of the database: |
+// | key | value | |
+// ----------------------------------------------------------------------- |
+// | map-1 | 2 (refcount, start of map-1-* keys)| |
+// | map-1-a | b (a = b in map 1) | |
+// | ... | | |
+// | namespace-1 (1 = namespace id) | dummy (start of namespace-1-* keys)| |
+// | namespace-1-origin1 | 1 (mapid) | |
+// | namespace-1-origin2 | 2 | |
+// | namespace-2 | dummy | |
+// | namespace-2-origin1 | 1 (shallow copy) | |
+// | namespace-2-origin2 | 2 (shallow copy) | |
+// | namespace-3 | dummy | |
+// | namespace-3-origin1 | 3 (deep copy) | |
+// | namespace-3-origin2 | 2 (shallow copy) | |
+// | next-namespace-id | 4 | |
+// | next-map-id | 4 | |
+ |
+namespace { |
+ |
+// Helper functions for creating the keys needed for the schema. |
+std::string NamespaceStartKey(const std::string& namespace_id) { |
+ return base::StringPrintf("namespace-%s", namespace_id.c_str()); |
+} |
+ |
+std::string NamespaceStartKey(int64 namespace_id, int64 namespace_offset) { |
+ return NamespaceStartKey( |
+ base::Int64ToString(namespace_id + namespace_offset)); |
+} |
+ |
+std::string NamespaceKey(const std::string& namespace_id, |
+ const std::string& origin) { |
+ return base::StringPrintf("namespace-%s-%s", namespace_id.c_str(), |
+ origin.c_str()); |
+} |
+ |
+std::string NamespaceKey(int64 namespace_id, int64 namespace_offset, |
+ const GURL& origin) { |
+ return NamespaceKey(base::Int64ToString(namespace_id + namespace_offset), |
+ origin.spec()); |
+} |
+ |
+std::string NamespacePrefix() { |
+return std::string("namespace-"); |
michaeln
2012/04/22 22:50:01
nit: indent
marja
2012/04/23 14:38:25
Done (in codereview.chromium.org/10176005 ).
|
+} |
+ |
+std::string MapRefCountKey(const std::string& map_id) { |
+ return base::StringPrintf("map-%s", map_id.c_str()); |
+} |
+ |
+std::string MapKey(const std::string& map_id, const std::string& key) { |
+ return base::StringPrintf("map-%s-%s", map_id.c_str(), key.c_str()); |
+} |
+ |
+std::string MapPrefix() { |
+ return std::string("map-"); |
+} |
+ |
+std::string NextNamespaceIdKey() { |
+ return "next-namespace-id"; |
+} |
+ |
+std::string NextMapIdKey() { |
+ return "next-map-id"; |
+} |
+ |
+} // namespace |
+ |
+namespace dom_storage { |
+ |
+SessionStorageDatabase::SessionStorageDatabase(const FilePath& file_path) |
+ : file_path_(file_path), |
+ failed_to_open_(false), |
+ namespace_offset_(0) { } |
+ |
+SessionStorageDatabase::~SessionStorageDatabase() { } |
+ |
+void SessionStorageDatabase::ReadAllValues(int64 namespace_id, |
+ const GURL& origin, |
+ ValuesMap* result) { |
+ // We don't create a database if it doesn't exist. In that case, there is |
+ // nothing to be added to the result. |
+ if (!LazyOpen(false)) |
+ return; |
+ // Check if there is map for |namespace_id| and |origin|. |
+ std::string namespace_key = |
+ NamespaceKey(namespace_id, namespace_offset_, origin); |
+ std::string map_id; |
+ leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id); |
+ if (!s.ok() || map_id.empty()) |
+ return; |
+ ReadMap(map_id, result, false); |
+} |
+ |
+bool SessionStorageDatabase::CommitChanges(int64 namespace_id, |
+ const GURL& origin, |
+ bool clear_all_first, |
+ const ValuesMap& changes) { |
+ // Even if |changes| is empty, we need to write the appropriate placeholders |
+ // in the database, so that it can be later shallow-copied succssfully. |
+ if (!LazyOpen(true)) |
+ return false; |
+ leveldb::WriteBatch batch; |
+ |
+ // Ensure that the key namespace-N (see the schema above) exists. |
+ batch.Put(NamespaceStartKey(namespace_id, namespace_offset_), ""); |
+ |
+ // Ensure that the next namespace id is up to date. |
+ UpdateNextNamespaceId(namespace_id, &batch); |
+ |
+ // Write the data into the map. |
+ std::string namespace_key = |
+ NamespaceKey(namespace_id, namespace_offset_, origin); |
+ std::string map_id; |
+ leveldb::Status s; |
+ s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id); |
+ DCHECK(s.ok() || s.IsNotFound()); |
+ // The map for |namespace_id| and |origin| needs to be created if there is |
+ // data to be written to it. |
+ if (s.IsNotFound() && !changes.empty()) |
+ CreateNewMap(namespace_key, &batch, &map_id); |
+ else if (clear_all_first) |
+ ClearMap(map_id, &batch); |
+ |
+ WriteValuesToMap(map_id, changes, &batch); |
+ |
+ s = db_->Write(leveldb::WriteOptions(), &batch); |
+ DCHECK(s.ok()); |
+ return true; |
+} |
+ |
+bool SessionStorageDatabase::ShallowCopyNamespace(int64 namespace_id, |
+ int64 new_namespace_id) { |
+ // Go through all origins in the namespace |namespace_id|, create placeholders |
+ // for them in |new_namespace_id|, and associate them with the existing maps. |
+ |
+ // Example, data before shallow copy: |
+ // | map-1 | 1 (refcount) | |
+ // | map-1-a | b | |
+ // | namespace-1 (1 = namespace id) | dummy | |
+ // | namespace-1-origin1 | 1 (mapid) | |
+ |
+ // Example, data after shallow copy: |
+ // | map-1 | 2 (inc. refcount) | |
+ // | map-1-a | b | |
+ // | namespace-1 (1 = namespace id) | dummy | |
+ // | namespace-1-origin1 | 1 (mapid) | |
+ // | namespace-2 | dummy | |
+ // | namespace-2-origin1 | 1 (mapid) << references the same map |
+ |
+ if (!LazyOpen(true)) { |
+ return false; |
+ } |
+ leveldb::WriteBatch batch; |
+ batch.Put(NamespaceStartKey(new_namespace_id, namespace_offset_), ""); |
+ |
+ // Ensure that the next namespace id is up to date. |
+ UpdateNextNamespaceId(new_namespace_id, &batch); |
+ |
+ scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); |
+ std::string namespace_start_key = |
+ NamespaceStartKey(namespace_id, namespace_offset_); |
+ it->Seek(namespace_start_key); |
+ if (!it->Valid()) { |
+ // It's possible that the namespace doesn't contain any data. (This happens |
+ // when the namespace doesn't contain any areas.) We don't need to do |
+ // anything. |
+ return true; |
+ } |
+ |
+ // Skip the dummy entry "namespace-<namespaceid>" and iterate the origins. |
+ for (it->Next(); it->Valid(); it->Next()) { |
+ std::string key = it->key().ToString(); |
+ if (key.find(namespace_start_key) != 0) { |
+ // Iterated past the origins for this namespace. |
+ break; |
+ } |
+ std::string map_id = it->value().ToString(); |
+ // Increase the ref count for the map. |
+ std::string map_key = MapRefCountKey(map_id); |
+ int64 old_ref_count = 0; |
+ bool success = GetRefCount(map_key, &old_ref_count); |
+ if (!success) |
+ return false; |
+ batch.Put(map_key, base::Int64ToString(++old_ref_count)); |
+ // Associate the map with the new namespace. |
+ size_t second_dash = key.find('-', namespace_start_key.length()); |
+ std::string origin = key.substr(second_dash + 1); |
+ std::string new_namespace_key = NamespaceKey( |
+ base::Int64ToString(new_namespace_id + namespace_offset_), origin); |
+ batch.Put(new_namespace_key, map_id); |
+ } |
+ leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); |
+ DCHECK(s.ok()); |
+ return true; |
+} |
+ |
+bool SessionStorageDatabase::DeepCopy(int64 namespace_id, |
+ const GURL& origin) { |
+ // Example, data before deep copy: |
+ // | namespace-1 (1 = namespace id) | dummy | |
+ // | namespace-1-origin1 | 1 (mapid) | |
+ // | namespace-2 | dummy | |
+ // | namespace-2-origin1 | 1 (mapid) << references the same map |
+ // | map-1 | 2 (refcount) | |
+ // | map-1-a | b | |
+ |
+ // Example, data after deep copy copy: |
+ // | namespace-1 (1 = namespace id) | dummy | |
+ // | namespace-1-origin1 | 1 (mapid) | |
+ // | namespace-2 | dummy | |
+ // | namespace-2-origin1 | 2 (mapid) << references the new map |
+ // | map-1 | 1 (dec. refcount) | |
+ // | map-1-a | b | |
+ // | map-2 | 1 (refcount) | |
+ // | map-2-a | b | |
+ |
+ if (!LazyOpen(true)) { |
+ return false; |
+ } |
+ leveldb::Status s; |
+ std::string namespace_key = |
+ NamespaceKey(namespace_id, namespace_offset_, origin); |
+ leveldb::WriteBatch batch; |
+ std::string old_map_id; |
+ s = db_->Get(leveldb::ReadOptions(), namespace_key, &old_map_id); |
+ DCHECK(s.ok()); |
+ |
+ // If this copy is the the only "shallow" copy of the map, no deep copying is |
+ // needed. |
+ int64 ref_count = 0; |
+ bool success = GetRefCount(MapRefCountKey(old_map_id), &ref_count); |
+ if (!success) |
+ return false; |
+ if (ref_count == 1) |
+ return true; |
+ |
+ std::string new_map_id; |
+ CreateNewMap(namespace_key, &batch, &new_map_id); |
+ |
+ // Copy the values in the map. |
+ ValuesMap values; |
+ ReadMap(old_map_id, &values, false); |
+ WriteValuesToMap(new_map_id, values, &batch); |
+ |
+ DecreaseRefCount(old_map_id, &batch); |
+ |
+ s = db_->Write(leveldb::WriteOptions(), &batch); |
+ DCHECK(s.ok()); |
+ return true; |
+} |
+ |
+bool SessionStorageDatabase::DisassociateMap(int64 namespace_id, |
+ const GURL& origin) { |
+ if (!LazyOpen(true)) { |
+ return false; |
+ } |
+ |
+ leveldb::Status s; |
+ std::string namespace_key = |
+ NamespaceKey(namespace_id, namespace_offset_, origin); |
+ leveldb::WriteBatch batch; |
+ std::string old_map_id; |
+ s = db_->Get(leveldb::ReadOptions(), namespace_key, &old_map_id); |
michaeln
2012/04/22 22:50:01
why is it ok to not update the value is namespace_
marja
2012/04/23 15:11:36
This was wrong, fixed in codereview.chromium.org/1
|
+ DCHECK(s.ok() || s.IsNotFound()); |
+ if (s.ok()) |
+ DecreaseRefCount(old_map_id, &batch); |
+ s = db_->Write(leveldb::WriteOptions(), &batch); |
+ DCHECK(s.ok()); |
+ return true; |
+} |
+ |
+void SessionStorageDatabase::DeleteOrigin(int64 namespace_id, |
+ const GURL& origin) { |
+ if (!LazyOpen(false)) { |
+ // No need to create the database if it doesn't exist. |
+ return; |
+ } |
+ leveldb::WriteBatch batch; |
+ DeleteOrigin(base::Int64ToString(namespace_id), origin.spec(), &batch); |
+ leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); |
+ DCHECK(s.ok()); |
+} |
+ |
+void SessionStorageDatabase::DeleteNamespace(int64 namespace_id) { |
+ if (!LazyOpen(false)) { |
+ // No need to create the database if it doesn't exist. |
+ return; |
+ } |
+ leveldb::WriteBatch batch; |
+ DeleteNamespace(base::Int64ToString(namespace_id), &batch); |
+ leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); |
+ DCHECK(s.ok()); |
+} |
+ |
+void SessionStorageDatabase::DeleteLeftoverData() { |
+ if (!LazyOpen(false)) { |
+ // No need to create the database if it doesn't exist. |
+ return; |
+ } |
+ leveldb::WriteBatch batch; |
+ scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); |
michaeln
2012/04/22 22:50:01
should we start the iteration at the 'namespace-'
marja
2012/04/23 14:38:25
Done (in codereview.chromium.org/10176005 ).
|
+ for (it->SeekToFirst(); it->Valid(); it->Next()) { |
+ // If the key is of the form "namespace-<namespaceid>", delete the |
+ // corresponding namespace. |
+ std::string key = it->key().ToString(); |
+ std::string namespace_prefix = NamespacePrefix(); |
+ if (key.find(namespace_prefix) != 0) { |
+ continue; |
+ } |
+ size_t second_dash = key.find('-', namespace_prefix.length()); |
+ if (second_dash == std::string::npos) { |
+ std::string namespace_id = key.substr(namespace_prefix.length()); |
+ DeleteNamespace(namespace_id, &batch); |
+ } |
+ } |
+ leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); |
+ DCHECK(s.ok()); |
+} |
+ |
+bool SessionStorageDatabase::LazyOpen(bool create_if_needed) { |
+ if (failed_to_open_) { |
+ // Don't try to open a database that we know has failed |
+ // already. |
+ return false; |
+ } |
+ |
+ if (IsOpen()) |
+ return true; |
+ |
+ bool directory_exists = file_util::PathExists(file_path_); |
+ |
+ if (!directory_exists && !create_if_needed) { |
+ // If the directory doesn't exist already and we haven't been asked to |
+ // create a file on disk, then we don't bother opening the database. This |
+ // means we wait until we absolutely need to put something onto disk before |
+ // we do so. |
+ return false; |
+ } |
+ |
+ leveldb::DB* db; |
+ leveldb::Status s = TryToOpen(file_path_, &db); |
+ if (!s.ok()) { |
+ LOG(WARNING) << "Failed to open leveldb in " << file_path_.value() |
+ << ", error: " << s.ToString(); |
+ DCHECK(db == NULL); |
+ |
+ // Clear the directory and try again. |
+ file_util::Delete(file_path_, true); |
+ s = TryToOpen(file_path_, &db); |
+ if (!s.ok()) { |
+ LOG(WARNING) << "Failed to open leveldb in " << file_path_.value() |
+ << ", error: " << s.ToString(); |
+ DCHECK(db == NULL); |
+ failed_to_open_ = true; |
+ return false; |
+ } |
+ } |
+ |
+ db_.reset(db); |
+ |
+ bool success = GetNextNamespaceId(&namespace_offset_); |
+ DCHECK(success); |
+ |
+ return true; |
+} |
+ |
+leveldb::Status SessionStorageDatabase::TryToOpen(const FilePath& file_path, |
+ leveldb::DB** db) { |
+ leveldb::Options options; |
+ // The directory exists but a valid leveldb database might not exist inside it |
+ // (e.g., a subset of the needed files might be missing). Handle this |
+ // situation gracefully by creating the database now. |
+ options.create_if_missing = true; |
+#if defined(OS_WIN) |
+ return leveldb::DB::Open(options, WideToUTF8(file_path.value()), db); |
+#elif defined(OS_POSIX) |
+ return leveldb::DB::Open(options, file_path.value(), db); |
+#endif |
+} |
+ |
+bool SessionStorageDatabase::IsOpen() const { |
+ return db_.get(); |
+} |
+ |
+void SessionStorageDatabase::ReadMap(const std::string& map_id, |
+ ValuesMap* result, |
+ bool only_keys) { |
+ scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); |
+ std::string map_start_key = MapRefCountKey(map_id); |
+ it->Seek(map_start_key); |
+ if (!it->Valid()) |
+ return; |
+ // Skip the dummy entry "map-<mapid>". |
+ for (it->Next(); it->Valid(); it->Next()) { |
+ // Key is of the form "map-<mapid>-<key>". |
+ std::string key = it->key().ToString(); |
+ int prefix_length = MapPrefix().length(); |
+ size_t second_dash = key.find('-', prefix_length); |
+ if (second_dash == std::string::npos || |
+ key.substr(prefix_length, second_dash - prefix_length) != map_id) { |
+ // Iterated beyond the keys in this map. |
+ break; |
+ } |
+ string16 key16 = UTF8ToUTF16(key.substr(second_dash + 1)); |
+ if (only_keys) { |
+ (*result)[key16] = NullableString16(true); |
+ } else { |
+ // Convert the raw data stored in std::string (it->value()) to raw data |
+ // stored in string16. |
+ // FIXME(marja): Add tests. |
+ string16 value; |
+ size_t len = it->value().size() / sizeof(char16); |
+ value.resize(len); |
+ value.assign(reinterpret_cast<const char16*>(it->value().data()), len); |
+ (*result)[key16] = NullableString16(value, false); |
+ } |
+ } |
+} |
+ |
+bool SessionStorageDatabase::CreateNewMap(const std::string& namespace_key, |
+ leveldb::WriteBatch* batch, |
+ std::string* map_id) { |
+ // Create a new map. |
+ std::string next_map_id_key = NextMapIdKey(); |
+ leveldb::Status s = db_->Get(leveldb::ReadOptions(), next_map_id_key, map_id); |
+ DCHECK(s.ok() || s.IsNotFound()); |
+ int64 next_map_id = 0; |
+ if (s.IsNotFound()) { |
+ *map_id = "0"; |
+ } else { |
+ bool conversion_ok = base::StringToInt64(*map_id, &next_map_id); |
+ DCHECK(conversion_ok); |
+ // FIXME(marja): What to do if the database is corrupt? |
+ } |
+ batch->Put(next_map_id_key, base::Int64ToString(++next_map_id)); |
+ batch->Put(namespace_key, *map_id); |
+ batch->Put(MapRefCountKey(*map_id), base::Int64ToString(1)); |
+ return true; |
+} |
+ |
+void SessionStorageDatabase::WriteValuesToMap(const std::string& map_id, |
+ const ValuesMap& values, |
+ leveldb::WriteBatch* batch) { |
+ for(ValuesMap::const_iterator it = values.begin(); it != values.end(); ++it) { |
+ NullableString16 value = it->second; |
+ std::string key = MapKey(map_id, UTF16ToUTF8(it->first)); |
+ if (value.is_null()) { |
+ batch->Delete(key); |
+ } else { |
+ // Convert the raw data stored in string16 to raw data stored in |
+ // std::string. |
+ // FIXME(marja): Add tests. |
+ const char* data = reinterpret_cast<const char*>(value.string().data()); |
+ size_t size = value.string().size() * 2; |
+ batch->Put(key, std::string(data, size)); |
+ } |
+ } |
+} |
+ |
+bool SessionStorageDatabase::GetNextNamespaceId(int64* next_namespace_id) { |
+ std::string next_namespace_id_string; |
+ leveldb::Status s = db_->Get(leveldb::ReadOptions(), NextNamespaceIdKey(), |
+ &next_namespace_id_string); |
+ DCHECK(s.ok() || s.IsNotFound()); |
+ if (s.IsNotFound()) { |
+ *next_namespace_id = 0; |
+ return true; |
+ } |
+ bool conversion_ok = |
+ base::StringToInt64(next_namespace_id_string, next_namespace_id); |
+ DCHECK(conversion_ok); |
+ return conversion_ok; |
+} |
+ |
+void SessionStorageDatabase::UpdateNextNamespaceId(int64 namespace_id, |
+ leveldb::WriteBatch* batch) { |
+ int64 next_namespace_id; |
+ bool success = GetNextNamespaceId(&next_namespace_id); |
+ DCHECK(success); |
+ if (next_namespace_id < namespace_id + namespace_offset_ + 1) { |
+ next_namespace_id = namespace_id + namespace_offset_ + 1; |
+ batch->Put(NextNamespaceIdKey(), base::Int64ToString(next_namespace_id)); |
+ } |
+} |
+ |
+bool SessionStorageDatabase::GetRefCount(const std::string& map_key, |
+ int64* ref_count) { |
+ std::string ref_count_string; |
+ leveldb::Status s = |
+ db_->Get(leveldb::ReadOptions(), map_key, &ref_count_string); |
+ DCHECK(s.ok()); |
+ if (s.ok()) { |
+ bool conversion_ok = base::StringToInt64(ref_count_string, ref_count); |
+ DCHECK(conversion_ok); |
+ return conversion_ok; |
+ } |
+ return false; |
+} |
+ |
+void SessionStorageDatabase::DecreaseRefCount(const std::string& map_id, |
+ leveldb::WriteBatch* batch) { |
+ // Decrease the ref count for the map. |
+ std::string map_key = MapRefCountKey(map_id); |
+ int64 ref_count = 0; |
+ bool success = GetRefCount(map_key, &ref_count); |
+ DCHECK(success); |
+ if (!success) |
+ return; |
+ if (--ref_count > 0) { |
+ batch->Put(map_key, base::Int64ToString(ref_count)); |
+ } else { |
+ // Clear all keys in the map. |
+ ClearMap(map_id, batch); |
+ batch->Delete(map_key); |
+ } |
+} |
+ |
+void SessionStorageDatabase::ClearMap(const std::string& map_id, |
+ leveldb::WriteBatch* batch) { |
+ ValuesMap values; |
+ ReadMap(map_id, &values, true); |
+ for (ValuesMap::const_iterator it = values.begin(); it != values.end(); ++it) |
+ batch->Delete(MapKey(map_id, UTF16ToUTF8(it->first))); |
+} |
+ |
+void SessionStorageDatabase::DeleteOrigin(const std::string& namespace_id, |
+ const std::string& origin, |
+ leveldb::WriteBatch* batch) { |
+ std::string namespace_key = NamespaceKey(namespace_id, origin); |
+ std::string map_id; |
+ leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id); |
+ DCHECK(s.ok() || s.IsNotFound()); |
+ if (s.IsNotFound()) |
+ return; // Nothing to delete. |
+ DecreaseRefCount(map_id, batch); |
michaeln
2012/04/22 22:50:01
not sure this will do what you want it to do, a le
marja
2012/04/23 14:38:25
Ahh, I see, thanks for catching this. Things go wr
|
+ batch->Delete(namespace_key); |
+} |
+ |
+void SessionStorageDatabase::DeleteNamespace(const std::string& namespace_id, |
+ leveldb::WriteBatch* batch) { |
+ std::string namespace_start_key = NamespaceStartKey(namespace_id); |
+ scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); |
+ it->Seek(namespace_start_key); |
+ if (!it->Valid()) |
+ return; |
+ // Skip the dummy entry "namespace-<namespaceid>"; then iterate the origins in |
+ // the namespace. |
+ for (it->Next(); it->Valid(); it->Next()) { |
+ // Key is of the form "namespace-<namespaceid>-<origin>". |
+ std::string key = it->key().ToString(); |
+ std::string namespace_prefix = NamespacePrefix(); |
+ if (key.find(namespace_prefix) != 0) { |
+ // Iterated beyond the keys for this namespace. |
+ break; |
+ } |
+ int prefix_length = namespace_prefix.length(); |
+ size_t second_dash = key.find('-', prefix_length); |
+ if (second_dash == std::string::npos || |
+ key.substr(prefix_length, |
+ second_dash - prefix_length) != namespace_id) { |
+ // Iterated beyond the keys for this namespace. |
+ break; |
+ } |
+ std::string origin = key.substr(second_dash + 1); |
+ DeleteOrigin(namespace_id, origin, batch); |
+ } |
+ batch->Delete(namespace_start_key); |
+} |
+ |
+// FIXME(marja): Remove this (or dump more intelligently). |
+void SessionStorageDatabase::DumpData() { |
+ LOG(WARNING) << "Dumping session storage"; |
+ scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); |
+ if (LazyOpen(false)) { |
+ for (it->SeekToFirst(); it->Valid(); it->Next()) |
+ LOG(WARNING) << it->key().ToString() << ": " << it->value().ToString(); |
+ } |
+ LOG(WARNING) << "Dumping session storage complete"; |
+} |
+ |
+bool SessionStorageDatabase::CheckConsistency() { |
+ if (!LazyOpen(false)) |
+ return true; |
+ std::map<int64, int64> found_map_references; |
+ std::string namespace_prefix = NamespacePrefix(); |
+ std::set<int64> found_namespace_ids; |
+ scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); |
+ int64 max_map_id = -1; |
+ int64 max_namespace_id = -1; |
+ // First iterate "namespace-" keys. |
+ for (it->SeekToFirst(); it->Valid(); it->Next()) { |
+ std::string key = it->key().ToString(); |
+ if (key.find(namespace_prefix) != 0) |
+ continue; |
+ size_t second_dash = key.find('-', namespace_prefix.length()); |
+ if (second_dash == std::string::npos) { |
+ // Key is of the form "namespace-<namespaceid>". |
+ std::string namespace_id_str = key.substr(namespace_prefix.length()); |
+ int64 namespace_id; |
+ bool conversion_ok = base::StringToInt64(namespace_id_str, &namespace_id); |
+ if (!conversion_ok) { |
+ LOG(WARNING) << "Invalid key: " << key; |
+ LOG(WARNING) << "Cannot convert " << namespace_id_str << " to int64"; |
+ return false; |
+ } |
+ found_namespace_ids.insert(namespace_id); |
+ if (namespace_id > max_namespace_id) |
+ max_namespace_id = namespace_id; |
+ } else { |
+ // Key is of the form "namespace-<namespaceid>-<origin>", and the value |
+ // is the map id. |
+ std::string namespace_id_str = |
+ key.substr(namespace_prefix.length(), |
+ second_dash - namespace_prefix.length()); |
+ int64 namespace_id; |
+ bool conversion_ok = base::StringToInt64(namespace_id_str, &namespace_id); |
+ if (!conversion_ok) { |
+ LOG(WARNING) << "Invalid key: " << key; |
+ LOG(WARNING) << "Cannot convert " << namespace_id_str << " to int64"; |
+ return false; |
+ } |
+ if (found_namespace_ids.find(namespace_id) == found_namespace_ids.end()) { |
+ LOG(WARNING) << "Key " << key << " not attached to a namespace entry"; |
+ return false; |
+ } |
+ |
+ int64 map_id; |
+ conversion_ok = base::StringToInt64(it->value().ToString(), &map_id); |
+ if (!conversion_ok) { |
+ LOG(WARNING) << "Invalid data: " << key << ": " |
+ << it->value().ToString(); |
+ LOG(WARNING) << "Cannot convert the value to map id."; |
+ return false; |
+ } |
+ ++found_map_references[map_id]; |
+ if (map_id > max_map_id) |
+ max_map_id = map_id; |
+ } |
+ } |
+ // Then iterate "map-" keys. |
+ std::string map_prefix = MapPrefix(); |
+ std::set<int64> found_map_ids; |
+ for (it->SeekToFirst(); it->Valid(); it->Next()) { |
+ std::string key = it->key().ToString(); |
+ if (key.find(map_prefix) != 0) |
+ continue; |
+ size_t second_dash = key.find('-', map_prefix.length()); |
+ if (second_dash == std::string::npos) { |
+ // Key is of the form "map-<mapid>" and the value is the ref count. |
+ std::string map_id_str = key.substr(map_prefix.length(), second_dash); |
+ int64 map_id; |
+ bool conversion_ok = base::StringToInt64(map_id_str, &map_id); |
+ if (!conversion_ok) { |
+ LOG(WARNING) << "Invalid key: " << key; |
+ LOG(WARNING) << "Cannot convert " << map_id_str << " to int64"; |
+ return false; |
+ } |
+ // Check that the map is not stale. |
+ if (found_map_references.find(map_id) == found_map_references.end()) { |
+ LOG(WARNING) << "Unattached map: " << key; |
+ return false; |
+ } |
+ int64 ref_count; |
+ conversion_ok = base::StringToInt64( |
+ it->value().ToString(), &ref_count); |
+ if (!conversion_ok) { |
+ LOG(WARNING) << "Invalid data: " << key << ": " |
+ << it->value().ToString(); |
+ LOG(WARNING) << "Cannot convert the value to int64."; |
+ return false; |
+ } |
+ if (found_map_references[map_id] != ref_count) { |
+ LOG(WARNING) << "Invalid ref count: " << key << ": " |
+ << it->value().ToString(); |
+ LOG(WARNING) << "Should be " << found_map_references[map_id]; |
+ } |
+ // Mark the map as existing. |
+ found_map_references[map_id] = -1; |
+ found_map_ids.insert(map_id); |
+ } else { |
+ // Key is of the form "map-<mapid>-key". Check that the entry for |
+ // "map-<mapid>" exists. |
+ std::string map_id_str = |
+ key.substr(map_prefix.length(), second_dash - map_prefix.length()); |
+ int64 map_id; |
+ bool conversion_ok = base::StringToInt64(map_id_str, &map_id); |
+ if (!conversion_ok) { |
+ LOG(WARNING) << "Invalid key: " << key; |
+ LOG(WARNING) << "Cannot convert " << map_id_str << " to int64"; |
+ return false; |
+ } |
+ if (found_map_ids.find(map_id) == found_map_ids.end()) { |
+ LOG(WARNING) << "Key " << key << " not attached to a map entry"; |
+ return false; |
+ } |
+ } |
+ } |
+ // Check the next map id key. |
+ std::string next_map_id_key = NextMapIdKey(); |
+ std::string next_map_id_str; |
+ leveldb::Status s = |
+ db_->Get(leveldb::ReadOptions(), next_map_id_key, &next_map_id_str); |
+ if (!s.ok() && !s.IsNotFound()) { |
+ LOG(WARNING) << "Error reading " << next_map_id_key; |
+ return false; |
+ } |
+ int64 next_map_id = 0; |
+ if (s.ok()) { |
+ bool conversion_ok = base::StringToInt64(next_map_id_str, &next_map_id); |
+ if (!conversion_ok) { |
+ LOG(WARNING) << "Invalid next map id: " << next_map_id_key << ": " |
+ << next_map_id_str; |
+ LOG(WARNING) << "Cannot convert the value to map id."; |
+ return false; |
+ } |
+ if (next_map_id <= max_map_id) { |
+ LOG(WARNING) << "Invalid next map id: " << next_map_id_key << ": " |
+ << next_map_id_str; |
+ LOG(WARNING) << "Should be at least " << max_map_id + 1; |
+ return false; |
+ } |
+ } else { |
+ // Not having the next map id key is only ok if there are no maps. |
+ if (max_map_id != -1) { |
+ LOG(WARNING) << "No next map id key, but maps exist."; |
+ return false; |
+ } |
+ } |
+ // Check the next namespace id key. |
+ std::string next_namespace_id_key = NextNamespaceIdKey(); |
+ std::string next_namespace_id_str; |
+ s = db_->Get(leveldb::ReadOptions(), next_namespace_id_key, |
+ &next_namespace_id_str); |
+ if (!s.ok() && !s.IsNotFound()) { |
+ LOG(WARNING) << "Error reading " << next_namespace_id_key; |
+ return false; |
+ } |
+ int64 next_namespace_id = 0; |
+ if (s.ok()) { |
+ bool conversion_ok = |
+ base::StringToInt64(next_namespace_id_str, &next_namespace_id); |
+ if (!conversion_ok) { |
+ LOG(WARNING) << "Invalid next namespace id: " << next_namespace_id_key |
+ << ": " << next_namespace_id_str; |
+ LOG(WARNING) << "Cannot convert the value to namespace id."; |
+ return false; |
+ } |
+ if (next_namespace_id <= max_namespace_id) { |
+ LOG(WARNING) << "Invalid next namespace id: " << next_namespace_id_key |
+ << ": " << next_namespace_id_str; |
+ LOG(WARNING) << "Should be at least " << max_namespace_id + 1; |
+ return false; |
+ } |
+ } else { |
+ // Not having the next namespace id key is only ok if there are no |
+ // namespaces. |
+ if (max_namespace_id != -1) { |
+ LOG(WARNING) << "No next namespace id key, but namespaces exist."; |
+ return false; |
+ } |
+ } |
+ |
+ // Check that all maps referred to exist. |
+ for (std::map<int64, int64>::const_iterator it = found_map_references.begin(); |
+ it != found_map_references.end(); ++it) { |
+ if (it->second != -1) { |
+ LOG(WARNING) << "Nonexisting map: " << it->first; |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+} // namespace dom_storage |