OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "webkit/dom_storage/session_storage_database.h" | |
6 | |
7 #include "base/file_util.h" | |
8 #include "base/stringprintf.h" | |
9 #include "base/string_number_conversions.h" | |
10 #include "base/utf_string_conversions.h" | |
11 #include "googleurl/src/gurl.h" | |
12 #include "third_party/leveldatabase/src/include/leveldb/db.h" | |
13 #include "third_party/leveldatabase/src/include/leveldb/iterator.h" | |
14 #include "third_party/leveldatabase/src/include/leveldb/status.h" | |
15 #include "third_party/leveldatabase/src/include/leveldb/options.h" | |
16 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | |
17 | |
18 // Layout of the database: | |
19 // | key | value | | |
20 // ----------------------------------------------------------------------- | |
21 // | map-1 | 2 (refcount, start of map-1-* keys)| | |
22 // | map-1-a | b (a = b in map 1) | | |
23 // | ... | | | |
24 // | namespace-1 (1 = namespace id) | dummy (start of namespace-1-* keys)| | |
25 // | namespace-1-origin1 | 1 (mapid) | | |
26 // | namespace-1-origin2 | 2 | | |
27 // | namespace-2 | dummy | | |
28 // | namespace-2-origin1 | 1 (shallow copy) | | |
29 // | namespace-2-origin2 | 2 (shallow copy) | | |
30 // | namespace-3 | dummy | | |
31 // | namespace-3-origin1 | 3 (deep copy) | | |
32 // | namespace-3-origin2 | 2 (shallow copy) | | |
33 // | next-namespace-id | 4 | | |
34 // | next-map-id | 4 | | |
35 | |
36 namespace { | |
37 | |
38 // Helper functions for creating the keys needed for the schema. | |
39 std::string NamespaceStartKey(const std::string& namespace_id) { | |
40 return base::StringPrintf("namespace-%s", namespace_id.c_str()); | |
41 } | |
42 | |
43 std::string NamespaceStartKey(int64 namespace_id, int64 namespace_offset) { | |
44 return NamespaceStartKey( | |
45 base::Int64ToString(namespace_id + namespace_offset)); | |
46 } | |
47 | |
48 std::string NamespaceKey(const std::string& namespace_id, | |
49 const std::string& origin) { | |
50 return base::StringPrintf("namespace-%s-%s", namespace_id.c_str(), | |
51 origin.c_str()); | |
52 } | |
53 | |
54 std::string NamespaceKey(int64 namespace_id, int64 namespace_offset, | |
55 const GURL& origin) { | |
56 return NamespaceKey(base::Int64ToString(namespace_id + namespace_offset), | |
57 origin.spec()); | |
58 } | |
59 | |
60 std::string NamespacePrefix() { | |
61 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 ).
| |
62 } | |
63 | |
64 std::string MapRefCountKey(const std::string& map_id) { | |
65 return base::StringPrintf("map-%s", map_id.c_str()); | |
66 } | |
67 | |
68 std::string MapKey(const std::string& map_id, const std::string& key) { | |
69 return base::StringPrintf("map-%s-%s", map_id.c_str(), key.c_str()); | |
70 } | |
71 | |
72 std::string MapPrefix() { | |
73 return std::string("map-"); | |
74 } | |
75 | |
76 std::string NextNamespaceIdKey() { | |
77 return "next-namespace-id"; | |
78 } | |
79 | |
80 std::string NextMapIdKey() { | |
81 return "next-map-id"; | |
82 } | |
83 | |
84 } // namespace | |
85 | |
86 namespace dom_storage { | |
87 | |
88 SessionStorageDatabase::SessionStorageDatabase(const FilePath& file_path) | |
89 : file_path_(file_path), | |
90 failed_to_open_(false), | |
91 namespace_offset_(0) { } | |
92 | |
93 SessionStorageDatabase::~SessionStorageDatabase() { } | |
94 | |
95 void SessionStorageDatabase::ReadAllValues(int64 namespace_id, | |
96 const GURL& origin, | |
97 ValuesMap* result) { | |
98 // We don't create a database if it doesn't exist. In that case, there is | |
99 // nothing to be added to the result. | |
100 if (!LazyOpen(false)) | |
101 return; | |
102 // Check if there is map for |namespace_id| and |origin|. | |
103 std::string namespace_key = | |
104 NamespaceKey(namespace_id, namespace_offset_, origin); | |
105 std::string map_id; | |
106 leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id); | |
107 if (!s.ok() || map_id.empty()) | |
108 return; | |
109 ReadMap(map_id, result, false); | |
110 } | |
111 | |
112 bool SessionStorageDatabase::CommitChanges(int64 namespace_id, | |
113 const GURL& origin, | |
114 bool clear_all_first, | |
115 const ValuesMap& changes) { | |
116 // Even if |changes| is empty, we need to write the appropriate placeholders | |
117 // in the database, so that it can be later shallow-copied succssfully. | |
118 if (!LazyOpen(true)) | |
119 return false; | |
120 leveldb::WriteBatch batch; | |
121 | |
122 // Ensure that the key namespace-N (see the schema above) exists. | |
123 batch.Put(NamespaceStartKey(namespace_id, namespace_offset_), ""); | |
124 | |
125 // Ensure that the next namespace id is up to date. | |
126 UpdateNextNamespaceId(namespace_id, &batch); | |
127 | |
128 // Write the data into the map. | |
129 std::string namespace_key = | |
130 NamespaceKey(namespace_id, namespace_offset_, origin); | |
131 std::string map_id; | |
132 leveldb::Status s; | |
133 s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id); | |
134 DCHECK(s.ok() || s.IsNotFound()); | |
135 // The map for |namespace_id| and |origin| needs to be created if there is | |
136 // data to be written to it. | |
137 if (s.IsNotFound() && !changes.empty()) | |
138 CreateNewMap(namespace_key, &batch, &map_id); | |
139 else if (clear_all_first) | |
140 ClearMap(map_id, &batch); | |
141 | |
142 WriteValuesToMap(map_id, changes, &batch); | |
143 | |
144 s = db_->Write(leveldb::WriteOptions(), &batch); | |
145 DCHECK(s.ok()); | |
146 return true; | |
147 } | |
148 | |
149 bool SessionStorageDatabase::ShallowCopyNamespace(int64 namespace_id, | |
150 int64 new_namespace_id) { | |
151 // Go through all origins in the namespace |namespace_id|, create placeholders | |
152 // for them in |new_namespace_id|, and associate them with the existing maps. | |
153 | |
154 // Example, data before shallow copy: | |
155 // | map-1 | 1 (refcount) | | |
156 // | map-1-a | b | | |
157 // | namespace-1 (1 = namespace id) | dummy | | |
158 // | namespace-1-origin1 | 1 (mapid) | | |
159 | |
160 // Example, data after shallow copy: | |
161 // | map-1 | 2 (inc. refcount) | | |
162 // | map-1-a | b | | |
163 // | namespace-1 (1 = namespace id) | dummy | | |
164 // | namespace-1-origin1 | 1 (mapid) | | |
165 // | namespace-2 | dummy | | |
166 // | namespace-2-origin1 | 1 (mapid) << references the same map | |
167 | |
168 if (!LazyOpen(true)) { | |
169 return false; | |
170 } | |
171 leveldb::WriteBatch batch; | |
172 batch.Put(NamespaceStartKey(new_namespace_id, namespace_offset_), ""); | |
173 | |
174 // Ensure that the next namespace id is up to date. | |
175 UpdateNextNamespaceId(new_namespace_id, &batch); | |
176 | |
177 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
178 std::string namespace_start_key = | |
179 NamespaceStartKey(namespace_id, namespace_offset_); | |
180 it->Seek(namespace_start_key); | |
181 if (!it->Valid()) { | |
182 // It's possible that the namespace doesn't contain any data. (This happens | |
183 // when the namespace doesn't contain any areas.) We don't need to do | |
184 // anything. | |
185 return true; | |
186 } | |
187 | |
188 // Skip the dummy entry "namespace-<namespaceid>" and iterate the origins. | |
189 for (it->Next(); it->Valid(); it->Next()) { | |
190 std::string key = it->key().ToString(); | |
191 if (key.find(namespace_start_key) != 0) { | |
192 // Iterated past the origins for this namespace. | |
193 break; | |
194 } | |
195 std::string map_id = it->value().ToString(); | |
196 // Increase the ref count for the map. | |
197 std::string map_key = MapRefCountKey(map_id); | |
198 int64 old_ref_count = 0; | |
199 bool success = GetRefCount(map_key, &old_ref_count); | |
200 if (!success) | |
201 return false; | |
202 batch.Put(map_key, base::Int64ToString(++old_ref_count)); | |
203 // Associate the map with the new namespace. | |
204 size_t second_dash = key.find('-', namespace_start_key.length()); | |
205 std::string origin = key.substr(second_dash + 1); | |
206 std::string new_namespace_key = NamespaceKey( | |
207 base::Int64ToString(new_namespace_id + namespace_offset_), origin); | |
208 batch.Put(new_namespace_key, map_id); | |
209 } | |
210 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
211 DCHECK(s.ok()); | |
212 return true; | |
213 } | |
214 | |
215 bool SessionStorageDatabase::DeepCopy(int64 namespace_id, | |
216 const GURL& origin) { | |
217 // Example, data before deep copy: | |
218 // | namespace-1 (1 = namespace id) | dummy | | |
219 // | namespace-1-origin1 | 1 (mapid) | | |
220 // | namespace-2 | dummy | | |
221 // | namespace-2-origin1 | 1 (mapid) << references the same map | |
222 // | map-1 | 2 (refcount) | | |
223 // | map-1-a | b | | |
224 | |
225 // Example, data after deep copy copy: | |
226 // | namespace-1 (1 = namespace id) | dummy | | |
227 // | namespace-1-origin1 | 1 (mapid) | | |
228 // | namespace-2 | dummy | | |
229 // | namespace-2-origin1 | 2 (mapid) << references the new map | |
230 // | map-1 | 1 (dec. refcount) | | |
231 // | map-1-a | b | | |
232 // | map-2 | 1 (refcount) | | |
233 // | map-2-a | b | | |
234 | |
235 if (!LazyOpen(true)) { | |
236 return false; | |
237 } | |
238 leveldb::Status s; | |
239 std::string namespace_key = | |
240 NamespaceKey(namespace_id, namespace_offset_, origin); | |
241 leveldb::WriteBatch batch; | |
242 std::string old_map_id; | |
243 s = db_->Get(leveldb::ReadOptions(), namespace_key, &old_map_id); | |
244 DCHECK(s.ok()); | |
245 | |
246 // If this copy is the the only "shallow" copy of the map, no deep copying is | |
247 // needed. | |
248 int64 ref_count = 0; | |
249 bool success = GetRefCount(MapRefCountKey(old_map_id), &ref_count); | |
250 if (!success) | |
251 return false; | |
252 if (ref_count == 1) | |
253 return true; | |
254 | |
255 std::string new_map_id; | |
256 CreateNewMap(namespace_key, &batch, &new_map_id); | |
257 | |
258 // Copy the values in the map. | |
259 ValuesMap values; | |
260 ReadMap(old_map_id, &values, false); | |
261 WriteValuesToMap(new_map_id, values, &batch); | |
262 | |
263 DecreaseRefCount(old_map_id, &batch); | |
264 | |
265 s = db_->Write(leveldb::WriteOptions(), &batch); | |
266 DCHECK(s.ok()); | |
267 return true; | |
268 } | |
269 | |
270 bool SessionStorageDatabase::DisassociateMap(int64 namespace_id, | |
271 const GURL& origin) { | |
272 if (!LazyOpen(true)) { | |
273 return false; | |
274 } | |
275 | |
276 leveldb::Status s; | |
277 std::string namespace_key = | |
278 NamespaceKey(namespace_id, namespace_offset_, origin); | |
279 leveldb::WriteBatch batch; | |
280 std::string old_map_id; | |
281 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
| |
282 DCHECK(s.ok() || s.IsNotFound()); | |
283 if (s.ok()) | |
284 DecreaseRefCount(old_map_id, &batch); | |
285 s = db_->Write(leveldb::WriteOptions(), &batch); | |
286 DCHECK(s.ok()); | |
287 return true; | |
288 } | |
289 | |
290 void SessionStorageDatabase::DeleteOrigin(int64 namespace_id, | |
291 const GURL& origin) { | |
292 if (!LazyOpen(false)) { | |
293 // No need to create the database if it doesn't exist. | |
294 return; | |
295 } | |
296 leveldb::WriteBatch batch; | |
297 DeleteOrigin(base::Int64ToString(namespace_id), origin.spec(), &batch); | |
298 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
299 DCHECK(s.ok()); | |
300 } | |
301 | |
302 void SessionStorageDatabase::DeleteNamespace(int64 namespace_id) { | |
303 if (!LazyOpen(false)) { | |
304 // No need to create the database if it doesn't exist. | |
305 return; | |
306 } | |
307 leveldb::WriteBatch batch; | |
308 DeleteNamespace(base::Int64ToString(namespace_id), &batch); | |
309 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
310 DCHECK(s.ok()); | |
311 } | |
312 | |
313 void SessionStorageDatabase::DeleteLeftoverData() { | |
314 if (!LazyOpen(false)) { | |
315 // No need to create the database if it doesn't exist. | |
316 return; | |
317 } | |
318 leveldb::WriteBatch batch; | |
319 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 ).
| |
320 for (it->SeekToFirst(); it->Valid(); it->Next()) { | |
321 // If the key is of the form "namespace-<namespaceid>", delete the | |
322 // corresponding namespace. | |
323 std::string key = it->key().ToString(); | |
324 std::string namespace_prefix = NamespacePrefix(); | |
325 if (key.find(namespace_prefix) != 0) { | |
326 continue; | |
327 } | |
328 size_t second_dash = key.find('-', namespace_prefix.length()); | |
329 if (second_dash == std::string::npos) { | |
330 std::string namespace_id = key.substr(namespace_prefix.length()); | |
331 DeleteNamespace(namespace_id, &batch); | |
332 } | |
333 } | |
334 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
335 DCHECK(s.ok()); | |
336 } | |
337 | |
338 bool SessionStorageDatabase::LazyOpen(bool create_if_needed) { | |
339 if (failed_to_open_) { | |
340 // Don't try to open a database that we know has failed | |
341 // already. | |
342 return false; | |
343 } | |
344 | |
345 if (IsOpen()) | |
346 return true; | |
347 | |
348 bool directory_exists = file_util::PathExists(file_path_); | |
349 | |
350 if (!directory_exists && !create_if_needed) { | |
351 // If the directory doesn't exist already and we haven't been asked to | |
352 // create a file on disk, then we don't bother opening the database. This | |
353 // means we wait until we absolutely need to put something onto disk before | |
354 // we do so. | |
355 return false; | |
356 } | |
357 | |
358 leveldb::DB* db; | |
359 leveldb::Status s = TryToOpen(file_path_, &db); | |
360 if (!s.ok()) { | |
361 LOG(WARNING) << "Failed to open leveldb in " << file_path_.value() | |
362 << ", error: " << s.ToString(); | |
363 DCHECK(db == NULL); | |
364 | |
365 // Clear the directory and try again. | |
366 file_util::Delete(file_path_, true); | |
367 s = TryToOpen(file_path_, &db); | |
368 if (!s.ok()) { | |
369 LOG(WARNING) << "Failed to open leveldb in " << file_path_.value() | |
370 << ", error: " << s.ToString(); | |
371 DCHECK(db == NULL); | |
372 failed_to_open_ = true; | |
373 return false; | |
374 } | |
375 } | |
376 | |
377 db_.reset(db); | |
378 | |
379 bool success = GetNextNamespaceId(&namespace_offset_); | |
380 DCHECK(success); | |
381 | |
382 return true; | |
383 } | |
384 | |
385 leveldb::Status SessionStorageDatabase::TryToOpen(const FilePath& file_path, | |
386 leveldb::DB** db) { | |
387 leveldb::Options options; | |
388 // The directory exists but a valid leveldb database might not exist inside it | |
389 // (e.g., a subset of the needed files might be missing). Handle this | |
390 // situation gracefully by creating the database now. | |
391 options.create_if_missing = true; | |
392 #if defined(OS_WIN) | |
393 return leveldb::DB::Open(options, WideToUTF8(file_path.value()), db); | |
394 #elif defined(OS_POSIX) | |
395 return leveldb::DB::Open(options, file_path.value(), db); | |
396 #endif | |
397 } | |
398 | |
399 bool SessionStorageDatabase::IsOpen() const { | |
400 return db_.get(); | |
401 } | |
402 | |
403 void SessionStorageDatabase::ReadMap(const std::string& map_id, | |
404 ValuesMap* result, | |
405 bool only_keys) { | |
406 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
407 std::string map_start_key = MapRefCountKey(map_id); | |
408 it->Seek(map_start_key); | |
409 if (!it->Valid()) | |
410 return; | |
411 // Skip the dummy entry "map-<mapid>". | |
412 for (it->Next(); it->Valid(); it->Next()) { | |
413 // Key is of the form "map-<mapid>-<key>". | |
414 std::string key = it->key().ToString(); | |
415 int prefix_length = MapPrefix().length(); | |
416 size_t second_dash = key.find('-', prefix_length); | |
417 if (second_dash == std::string::npos || | |
418 key.substr(prefix_length, second_dash - prefix_length) != map_id) { | |
419 // Iterated beyond the keys in this map. | |
420 break; | |
421 } | |
422 string16 key16 = UTF8ToUTF16(key.substr(second_dash + 1)); | |
423 if (only_keys) { | |
424 (*result)[key16] = NullableString16(true); | |
425 } else { | |
426 // Convert the raw data stored in std::string (it->value()) to raw data | |
427 // stored in string16. | |
428 // FIXME(marja): Add tests. | |
429 string16 value; | |
430 size_t len = it->value().size() / sizeof(char16); | |
431 value.resize(len); | |
432 value.assign(reinterpret_cast<const char16*>(it->value().data()), len); | |
433 (*result)[key16] = NullableString16(value, false); | |
434 } | |
435 } | |
436 } | |
437 | |
438 bool SessionStorageDatabase::CreateNewMap(const std::string& namespace_key, | |
439 leveldb::WriteBatch* batch, | |
440 std::string* map_id) { | |
441 // Create a new map. | |
442 std::string next_map_id_key = NextMapIdKey(); | |
443 leveldb::Status s = db_->Get(leveldb::ReadOptions(), next_map_id_key, map_id); | |
444 DCHECK(s.ok() || s.IsNotFound()); | |
445 int64 next_map_id = 0; | |
446 if (s.IsNotFound()) { | |
447 *map_id = "0"; | |
448 } else { | |
449 bool conversion_ok = base::StringToInt64(*map_id, &next_map_id); | |
450 DCHECK(conversion_ok); | |
451 // FIXME(marja): What to do if the database is corrupt? | |
452 } | |
453 batch->Put(next_map_id_key, base::Int64ToString(++next_map_id)); | |
454 batch->Put(namespace_key, *map_id); | |
455 batch->Put(MapRefCountKey(*map_id), base::Int64ToString(1)); | |
456 return true; | |
457 } | |
458 | |
459 void SessionStorageDatabase::WriteValuesToMap(const std::string& map_id, | |
460 const ValuesMap& values, | |
461 leveldb::WriteBatch* batch) { | |
462 for(ValuesMap::const_iterator it = values.begin(); it != values.end(); ++it) { | |
463 NullableString16 value = it->second; | |
464 std::string key = MapKey(map_id, UTF16ToUTF8(it->first)); | |
465 if (value.is_null()) { | |
466 batch->Delete(key); | |
467 } else { | |
468 // Convert the raw data stored in string16 to raw data stored in | |
469 // std::string. | |
470 // FIXME(marja): Add tests. | |
471 const char* data = reinterpret_cast<const char*>(value.string().data()); | |
472 size_t size = value.string().size() * 2; | |
473 batch->Put(key, std::string(data, size)); | |
474 } | |
475 } | |
476 } | |
477 | |
478 bool SessionStorageDatabase::GetNextNamespaceId(int64* next_namespace_id) { | |
479 std::string next_namespace_id_string; | |
480 leveldb::Status s = db_->Get(leveldb::ReadOptions(), NextNamespaceIdKey(), | |
481 &next_namespace_id_string); | |
482 DCHECK(s.ok() || s.IsNotFound()); | |
483 if (s.IsNotFound()) { | |
484 *next_namespace_id = 0; | |
485 return true; | |
486 } | |
487 bool conversion_ok = | |
488 base::StringToInt64(next_namespace_id_string, next_namespace_id); | |
489 DCHECK(conversion_ok); | |
490 return conversion_ok; | |
491 } | |
492 | |
493 void SessionStorageDatabase::UpdateNextNamespaceId(int64 namespace_id, | |
494 leveldb::WriteBatch* batch) { | |
495 int64 next_namespace_id; | |
496 bool success = GetNextNamespaceId(&next_namespace_id); | |
497 DCHECK(success); | |
498 if (next_namespace_id < namespace_id + namespace_offset_ + 1) { | |
499 next_namespace_id = namespace_id + namespace_offset_ + 1; | |
500 batch->Put(NextNamespaceIdKey(), base::Int64ToString(next_namespace_id)); | |
501 } | |
502 } | |
503 | |
504 bool SessionStorageDatabase::GetRefCount(const std::string& map_key, | |
505 int64* ref_count) { | |
506 std::string ref_count_string; | |
507 leveldb::Status s = | |
508 db_->Get(leveldb::ReadOptions(), map_key, &ref_count_string); | |
509 DCHECK(s.ok()); | |
510 if (s.ok()) { | |
511 bool conversion_ok = base::StringToInt64(ref_count_string, ref_count); | |
512 DCHECK(conversion_ok); | |
513 return conversion_ok; | |
514 } | |
515 return false; | |
516 } | |
517 | |
518 void SessionStorageDatabase::DecreaseRefCount(const std::string& map_id, | |
519 leveldb::WriteBatch* batch) { | |
520 // Decrease the ref count for the map. | |
521 std::string map_key = MapRefCountKey(map_id); | |
522 int64 ref_count = 0; | |
523 bool success = GetRefCount(map_key, &ref_count); | |
524 DCHECK(success); | |
525 if (!success) | |
526 return; | |
527 if (--ref_count > 0) { | |
528 batch->Put(map_key, base::Int64ToString(ref_count)); | |
529 } else { | |
530 // Clear all keys in the map. | |
531 ClearMap(map_id, batch); | |
532 batch->Delete(map_key); | |
533 } | |
534 } | |
535 | |
536 void SessionStorageDatabase::ClearMap(const std::string& map_id, | |
537 leveldb::WriteBatch* batch) { | |
538 ValuesMap values; | |
539 ReadMap(map_id, &values, true); | |
540 for (ValuesMap::const_iterator it = values.begin(); it != values.end(); ++it) | |
541 batch->Delete(MapKey(map_id, UTF16ToUTF8(it->first))); | |
542 } | |
543 | |
544 void SessionStorageDatabase::DeleteOrigin(const std::string& namespace_id, | |
545 const std::string& origin, | |
546 leveldb::WriteBatch* batch) { | |
547 std::string namespace_key = NamespaceKey(namespace_id, origin); | |
548 std::string map_id; | |
549 leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id); | |
550 DCHECK(s.ok() || s.IsNotFound()); | |
551 if (s.IsNotFound()) | |
552 return; // Nothing to delete. | |
553 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
| |
554 batch->Delete(namespace_key); | |
555 } | |
556 | |
557 void SessionStorageDatabase::DeleteNamespace(const std::string& namespace_id, | |
558 leveldb::WriteBatch* batch) { | |
559 std::string namespace_start_key = NamespaceStartKey(namespace_id); | |
560 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
561 it->Seek(namespace_start_key); | |
562 if (!it->Valid()) | |
563 return; | |
564 // Skip the dummy entry "namespace-<namespaceid>"; then iterate the origins in | |
565 // the namespace. | |
566 for (it->Next(); it->Valid(); it->Next()) { | |
567 // Key is of the form "namespace-<namespaceid>-<origin>". | |
568 std::string key = it->key().ToString(); | |
569 std::string namespace_prefix = NamespacePrefix(); | |
570 if (key.find(namespace_prefix) != 0) { | |
571 // Iterated beyond the keys for this namespace. | |
572 break; | |
573 } | |
574 int prefix_length = namespace_prefix.length(); | |
575 size_t second_dash = key.find('-', prefix_length); | |
576 if (second_dash == std::string::npos || | |
577 key.substr(prefix_length, | |
578 second_dash - prefix_length) != namespace_id) { | |
579 // Iterated beyond the keys for this namespace. | |
580 break; | |
581 } | |
582 std::string origin = key.substr(second_dash + 1); | |
583 DeleteOrigin(namespace_id, origin, batch); | |
584 } | |
585 batch->Delete(namespace_start_key); | |
586 } | |
587 | |
588 // FIXME(marja): Remove this (or dump more intelligently). | |
589 void SessionStorageDatabase::DumpData() { | |
590 LOG(WARNING) << "Dumping session storage"; | |
591 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
592 if (LazyOpen(false)) { | |
593 for (it->SeekToFirst(); it->Valid(); it->Next()) | |
594 LOG(WARNING) << it->key().ToString() << ": " << it->value().ToString(); | |
595 } | |
596 LOG(WARNING) << "Dumping session storage complete"; | |
597 } | |
598 | |
599 bool SessionStorageDatabase::CheckConsistency() { | |
600 if (!LazyOpen(false)) | |
601 return true; | |
602 std::map<int64, int64> found_map_references; | |
603 std::string namespace_prefix = NamespacePrefix(); | |
604 std::set<int64> found_namespace_ids; | |
605 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
606 int64 max_map_id = -1; | |
607 int64 max_namespace_id = -1; | |
608 // First iterate "namespace-" keys. | |
609 for (it->SeekToFirst(); it->Valid(); it->Next()) { | |
610 std::string key = it->key().ToString(); | |
611 if (key.find(namespace_prefix) != 0) | |
612 continue; | |
613 size_t second_dash = key.find('-', namespace_prefix.length()); | |
614 if (second_dash == std::string::npos) { | |
615 // Key is of the form "namespace-<namespaceid>". | |
616 std::string namespace_id_str = key.substr(namespace_prefix.length()); | |
617 int64 namespace_id; | |
618 bool conversion_ok = base::StringToInt64(namespace_id_str, &namespace_id); | |
619 if (!conversion_ok) { | |
620 LOG(WARNING) << "Invalid key: " << key; | |
621 LOG(WARNING) << "Cannot convert " << namespace_id_str << " to int64"; | |
622 return false; | |
623 } | |
624 found_namespace_ids.insert(namespace_id); | |
625 if (namespace_id > max_namespace_id) | |
626 max_namespace_id = namespace_id; | |
627 } else { | |
628 // Key is of the form "namespace-<namespaceid>-<origin>", and the value | |
629 // is the map id. | |
630 std::string namespace_id_str = | |
631 key.substr(namespace_prefix.length(), | |
632 second_dash - namespace_prefix.length()); | |
633 int64 namespace_id; | |
634 bool conversion_ok = base::StringToInt64(namespace_id_str, &namespace_id); | |
635 if (!conversion_ok) { | |
636 LOG(WARNING) << "Invalid key: " << key; | |
637 LOG(WARNING) << "Cannot convert " << namespace_id_str << " to int64"; | |
638 return false; | |
639 } | |
640 if (found_namespace_ids.find(namespace_id) == found_namespace_ids.end()) { | |
641 LOG(WARNING) << "Key " << key << " not attached to a namespace entry"; | |
642 return false; | |
643 } | |
644 | |
645 int64 map_id; | |
646 conversion_ok = base::StringToInt64(it->value().ToString(), &map_id); | |
647 if (!conversion_ok) { | |
648 LOG(WARNING) << "Invalid data: " << key << ": " | |
649 << it->value().ToString(); | |
650 LOG(WARNING) << "Cannot convert the value to map id."; | |
651 return false; | |
652 } | |
653 ++found_map_references[map_id]; | |
654 if (map_id > max_map_id) | |
655 max_map_id = map_id; | |
656 } | |
657 } | |
658 // Then iterate "map-" keys. | |
659 std::string map_prefix = MapPrefix(); | |
660 std::set<int64> found_map_ids; | |
661 for (it->SeekToFirst(); it->Valid(); it->Next()) { | |
662 std::string key = it->key().ToString(); | |
663 if (key.find(map_prefix) != 0) | |
664 continue; | |
665 size_t second_dash = key.find('-', map_prefix.length()); | |
666 if (second_dash == std::string::npos) { | |
667 // Key is of the form "map-<mapid>" and the value is the ref count. | |
668 std::string map_id_str = key.substr(map_prefix.length(), second_dash); | |
669 int64 map_id; | |
670 bool conversion_ok = base::StringToInt64(map_id_str, &map_id); | |
671 if (!conversion_ok) { | |
672 LOG(WARNING) << "Invalid key: " << key; | |
673 LOG(WARNING) << "Cannot convert " << map_id_str << " to int64"; | |
674 return false; | |
675 } | |
676 // Check that the map is not stale. | |
677 if (found_map_references.find(map_id) == found_map_references.end()) { | |
678 LOG(WARNING) << "Unattached map: " << key; | |
679 return false; | |
680 } | |
681 int64 ref_count; | |
682 conversion_ok = base::StringToInt64( | |
683 it->value().ToString(), &ref_count); | |
684 if (!conversion_ok) { | |
685 LOG(WARNING) << "Invalid data: " << key << ": " | |
686 << it->value().ToString(); | |
687 LOG(WARNING) << "Cannot convert the value to int64."; | |
688 return false; | |
689 } | |
690 if (found_map_references[map_id] != ref_count) { | |
691 LOG(WARNING) << "Invalid ref count: " << key << ": " | |
692 << it->value().ToString(); | |
693 LOG(WARNING) << "Should be " << found_map_references[map_id]; | |
694 } | |
695 // Mark the map as existing. | |
696 found_map_references[map_id] = -1; | |
697 found_map_ids.insert(map_id); | |
698 } else { | |
699 // Key is of the form "map-<mapid>-key". Check that the entry for | |
700 // "map-<mapid>" exists. | |
701 std::string map_id_str = | |
702 key.substr(map_prefix.length(), second_dash - map_prefix.length()); | |
703 int64 map_id; | |
704 bool conversion_ok = base::StringToInt64(map_id_str, &map_id); | |
705 if (!conversion_ok) { | |
706 LOG(WARNING) << "Invalid key: " << key; | |
707 LOG(WARNING) << "Cannot convert " << map_id_str << " to int64"; | |
708 return false; | |
709 } | |
710 if (found_map_ids.find(map_id) == found_map_ids.end()) { | |
711 LOG(WARNING) << "Key " << key << " not attached to a map entry"; | |
712 return false; | |
713 } | |
714 } | |
715 } | |
716 // Check the next map id key. | |
717 std::string next_map_id_key = NextMapIdKey(); | |
718 std::string next_map_id_str; | |
719 leveldb::Status s = | |
720 db_->Get(leveldb::ReadOptions(), next_map_id_key, &next_map_id_str); | |
721 if (!s.ok() && !s.IsNotFound()) { | |
722 LOG(WARNING) << "Error reading " << next_map_id_key; | |
723 return false; | |
724 } | |
725 int64 next_map_id = 0; | |
726 if (s.ok()) { | |
727 bool conversion_ok = base::StringToInt64(next_map_id_str, &next_map_id); | |
728 if (!conversion_ok) { | |
729 LOG(WARNING) << "Invalid next map id: " << next_map_id_key << ": " | |
730 << next_map_id_str; | |
731 LOG(WARNING) << "Cannot convert the value to map id."; | |
732 return false; | |
733 } | |
734 if (next_map_id <= max_map_id) { | |
735 LOG(WARNING) << "Invalid next map id: " << next_map_id_key << ": " | |
736 << next_map_id_str; | |
737 LOG(WARNING) << "Should be at least " << max_map_id + 1; | |
738 return false; | |
739 } | |
740 } else { | |
741 // Not having the next map id key is only ok if there are no maps. | |
742 if (max_map_id != -1) { | |
743 LOG(WARNING) << "No next map id key, but maps exist."; | |
744 return false; | |
745 } | |
746 } | |
747 // Check the next namespace id key. | |
748 std::string next_namespace_id_key = NextNamespaceIdKey(); | |
749 std::string next_namespace_id_str; | |
750 s = db_->Get(leveldb::ReadOptions(), next_namespace_id_key, | |
751 &next_namespace_id_str); | |
752 if (!s.ok() && !s.IsNotFound()) { | |
753 LOG(WARNING) << "Error reading " << next_namespace_id_key; | |
754 return false; | |
755 } | |
756 int64 next_namespace_id = 0; | |
757 if (s.ok()) { | |
758 bool conversion_ok = | |
759 base::StringToInt64(next_namespace_id_str, &next_namespace_id); | |
760 if (!conversion_ok) { | |
761 LOG(WARNING) << "Invalid next namespace id: " << next_namespace_id_key | |
762 << ": " << next_namespace_id_str; | |
763 LOG(WARNING) << "Cannot convert the value to namespace id."; | |
764 return false; | |
765 } | |
766 if (next_namespace_id <= max_namespace_id) { | |
767 LOG(WARNING) << "Invalid next namespace id: " << next_namespace_id_key | |
768 << ": " << next_namespace_id_str; | |
769 LOG(WARNING) << "Should be at least " << max_namespace_id + 1; | |
770 return false; | |
771 } | |
772 } else { | |
773 // Not having the next namespace id key is only ok if there are no | |
774 // namespaces. | |
775 if (max_namespace_id != -1) { | |
776 LOG(WARNING) << "No next namespace id key, but namespaces exist."; | |
777 return false; | |
778 } | |
779 } | |
780 | |
781 // Check that all maps referred to exist. | |
782 for (std::map<int64, int64>::const_iterator it = found_map_references.begin(); | |
783 it != found_map_references.end(); ++it) { | |
784 if (it->second != -1) { | |
785 LOG(WARNING) << "Nonexisting map: " << it->first; | |
786 return false; | |
787 } | |
788 } | |
789 return true; | |
790 } | |
791 | |
792 } // namespace dom_storage | |
OLD | NEW |