OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2013 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 "content/browser/indexed_db/indexed_db_backing_store.h" | |
6 | |
7 #include <public/Platform.h> | |
8 #include <public/WebIDBKey.h> | |
9 #include "base/file_util.h" | |
10 #include "base/files/file_path.h" | |
11 #include "base/logging.h" | |
12 #include "base/metrics/histogram.h" | |
13 #include "base/utf_string_conversions.h" | |
14 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h" | |
15 #include "content/browser/indexed_db/indexed_db_metadata.h" | |
16 #include "content/browser/indexed_db/indexed_db_tracing.h" | |
17 #include "content/browser/indexed_db/leveldb/leveldb_comparator.h" | |
18 #include "content/browser/indexed_db/leveldb/leveldb_database.h" | |
19 #include "content/browser/indexed_db/leveldb/leveldb_iterator.h" | |
20 #include "content/browser/indexed_db/leveldb/leveldb_slice.h" | |
21 #include "content/browser/indexed_db/leveldb/leveldb_transaction.h" | |
22 #include "content/common/indexed_db/indexed_db_key.h" | |
23 #include "content/common/indexed_db/indexed_db_key_path.h" | |
24 #include "content/common/indexed_db/indexed_db_key_range.h" | |
25 #include "third_party/WebKit/Source/Platform/chromium/public/WebIDBKeyPath.h" | |
26 | |
27 // TODO: Make blink push the version during the open() call. | |
28 static const uint32_t kWireVersion = 2; | |
29 | |
30 namespace content { | |
31 | |
32 using namespace IndexedDBLevelDBCoding; | |
33 | |
34 const int64_t KeyGeneratorInitialNumber = | |
jamesr
2013/05/21 23:56:06
constants in chromium should be named kFooBarBaz
jsbell
2013/05/22 17:54:44
Done.
| |
35 1; // From the IndexedDB specification. | |
36 | |
37 enum IndexedDBBackingStoreErrorSource { | |
38 // 0 - 2 are no longer used. | |
39 kFindKeyInIndex = 3, | |
jamesr
2013/05/21 23:56:06
enum values should be SHOUTY
jsbell
2013/05/22 17:54:44
Done.
| |
40 kGetIDBDatabaseMetaData, | |
41 kGetIndexes, | |
42 kGetKeyGeneratorCurrentNumber, | |
43 kGetObjectStores, | |
44 kGetRecord, | |
45 kKeyExistsInObjectStore, | |
46 kLoadCurrentRow, | |
47 kSetupMetadata, | |
48 kGetPrimaryKeyViaIndex, | |
49 kKeyExistsInIndex, | |
50 kVersionExists, | |
51 kDeleteObjectStore, | |
52 kSetMaxObjectStoreId, | |
53 kSetMaxIndexId, | |
54 kGetNewDatabaseId, | |
55 kGetNewVersionNumber, | |
56 kCreateIDBDatabaseMetaData, | |
57 kDeleteDatabase, | |
58 kTransactionCommit, | |
59 kIndexedDBLevelDBBackingStoreInternalErrorMax, | |
60 }; | |
61 | |
62 static void RecordInternalError(const char* type, | |
63 IndexedDBBackingStoreErrorSource location) { | |
64 string16 name = ASCIIToUTF16("WebCore.IndexedDB.BackingStore.") + | |
65 UTF8ToUTF16(type) + ASCIIToUTF16("Error"); | |
66 base::Histogram::FactoryGet(UTF16ToUTF8(name), | |
67 1, | |
68 kIndexedDBLevelDBBackingStoreInternalErrorMax, | |
69 kIndexedDBLevelDBBackingStoreInternalErrorMax + 1, | |
70 base::HistogramBase::kUmaTargetedHistogramFlag) | |
71 ->Add(location); | |
72 } | |
73 | |
74 // Use to signal conditions that usually indicate developer error, but | |
75 // could be caused by data corruption. A macro is used instead of an | |
76 // inline function so that the assert and log report the line number. | |
77 #define REPORT_ERROR(type, location) \ | |
78 do { \ | |
79 LOG(ERROR) << "IndexedDB " type " Error: " #location; \ | |
80 NOTREACHED(); \ | |
81 RecordInternalError(type, location); \ | |
82 } while (0) | |
83 | |
84 #define INTERNAL_READ_ERROR(location) REPORT_ERROR("Read", location) | |
85 #define INTERNAL_CONSISTENCY_ERROR(location) \ | |
86 REPORT_ERROR("Consistency", location) | |
87 #define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location) | |
88 | |
89 static void PutBool(LevelDBTransaction* transaction, | |
90 const LevelDBSlice& key, | |
91 bool value) { | |
92 transaction->Put(key, EncodeBool(value)); | |
93 } | |
94 | |
95 template <typename DBOrTransaction> | |
96 static bool | |
97 GetInt(DBOrTransaction* db, | |
jamesr
2013/05/21 23:56:06
seems overwrapped. did clang-format put this on a
jsbell
2013/05/22 17:54:44
Yeah, we're just letting clang-format do it's thin
jsbell
2013/05/23 21:10:49
A clang-format update appears to have addressed th
| |
98 const LevelDBSlice& key, | |
99 int64_t& found_int, | |
100 bool& found) { | |
101 std::vector<char> result; | |
102 bool ok = db->Get(key, result, found); | |
103 if (!ok) | |
104 return false; | |
105 if (!found) | |
106 return true; | |
107 | |
108 found_int = DecodeInt(result.begin(), result.end()); | |
109 return true; | |
110 } | |
111 | |
112 static void PutInt(LevelDBTransaction* transaction, | |
113 const LevelDBSlice& key, | |
114 int64_t value) { | |
115 DCHECK(value >= 0); | |
116 transaction->Put(key, EncodeInt(value)); | |
117 } | |
118 | |
119 template <typename DBOrTransaction> | |
120 WARN_UNUSED_RESULT static bool | |
121 GetVarInt(DBOrTransaction* db, | |
122 const LevelDBSlice& key, | |
123 int64_t& found_int, | |
124 bool& found) { | |
125 std::vector<char> result; | |
126 bool ok = db->Get(key, result, found); | |
127 if (!ok) | |
128 return false; | |
129 if (!found) | |
130 return true; | |
131 | |
132 found = DecodeVarInt(result.begin(), result.end(), found_int) == result.end(); | |
133 return true; | |
134 } | |
135 | |
136 static void PutVarInt(LevelDBTransaction* transaction, | |
137 const LevelDBSlice& key, | |
138 int64_t value) { | |
139 transaction->Put(key, EncodeVarInt(value)); | |
140 } | |
141 | |
142 template <typename DBOrTransaction> | |
143 WARN_UNUSED_RESULT static bool | |
144 GetString(DBOrTransaction* db, | |
145 const LevelDBSlice& key, | |
146 string16& found_string, | |
147 bool& found) { | |
148 std::vector<char> result; | |
149 found = false; | |
150 bool ok = db->Get(key, result, found); | |
151 if (!ok) | |
152 return false; | |
153 if (!found) | |
154 return true; | |
155 | |
156 found_string = DecodeString(&result[0], &result[0] + result.size()); | |
157 return true; | |
158 } | |
159 | |
160 static void PutString(LevelDBTransaction* transaction, | |
161 const LevelDBSlice& key, | |
162 const string16& value) { | |
163 transaction->Put(key, EncodeString(value)); | |
164 } | |
165 | |
166 static void PutIDBKeyPath(LevelDBTransaction* transaction, | |
167 const LevelDBSlice& key, | |
168 const IndexedDBKeyPath& value) { | |
169 transaction->Put(key, EncodeIDBKeyPath(value)); | |
170 } | |
171 | |
172 static int CompareKeys(const LevelDBSlice& a, const LevelDBSlice& b) { | |
173 return Compare(a, b); | |
174 } | |
175 | |
176 static int CompareIndexKeys(const LevelDBSlice& a, const LevelDBSlice& b) { | |
177 return Compare(a, b, true); | |
178 } | |
179 | |
180 class Comparator : public LevelDBComparator { | |
181 public: | |
182 virtual int Compare(const LevelDBSlice& a, const LevelDBSlice& b) const | |
183 OVERRIDE { | |
184 return IndexedDBLevelDBCoding::Compare(a, b); | |
185 } | |
186 virtual const char* Name() const OVERRIDE { return "idb_cmp1"; } | |
187 }; | |
188 | |
189 // 0 - Initial version. | |
190 // 1 - Adds UserIntVersion to DatabaseMetaData. | |
191 // 2 - Adds DataVersion to to global metadata. | |
192 const int64_t latest_known_schema_version = 2; | |
jamesr
2013/05/21 23:56:06
should be kLatest...., and probably static
jsbell
2013/05/22 17:54:44
Done.
| |
193 WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool& known) { | |
194 int64_t db_schema_version = 0; | |
195 bool found = false; | |
196 bool ok = GetInt( | |
197 db, LevelDBSlice(SchemaVersionKey::Encode()), db_schema_version, found); | |
198 if (!ok) | |
199 return false; | |
200 if (!found) { | |
201 known = true; | |
202 return true; | |
203 } | |
204 if (db_schema_version > latest_known_schema_version) { | |
205 known = false; | |
206 return true; | |
207 } | |
208 | |
209 const uint32_t latest_known_data_version = kWireVersion; | |
210 int64_t db_data_version = 0; | |
211 ok = GetInt( | |
212 db, LevelDBSlice(DataVersionKey::Encode()), db_data_version, found); | |
213 if (!ok) | |
214 return false; | |
215 if (!found) { | |
216 known = true; | |
217 return true; | |
218 } | |
219 | |
220 if (db_data_version > latest_known_data_version) { | |
221 known = false; | |
222 return true; | |
223 } | |
224 | |
225 known = true; | |
226 return true; | |
227 } | |
228 | |
229 WARN_UNUSED_RESULT static bool SetUpMetadata(LevelDBDatabase* db, | |
230 const string16& origin) { | |
231 const uint32_t latest_known_data_version = kWireVersion; | |
232 const std::vector<char> schema_version_key = SchemaVersionKey::Encode(); | |
233 const std::vector<char> data_version_key = DataVersionKey::Encode(); | |
234 | |
235 scoped_refptr<LevelDBTransaction> transaction = | |
236 LevelDBTransaction::Create(db); | |
237 | |
238 int64_t db_schema_version = 0; | |
239 int64_t db_data_version = 0; | |
240 bool found = false; | |
241 bool ok = GetInt(transaction.get(), | |
242 LevelDBSlice(schema_version_key), | |
243 db_schema_version, | |
244 found); | |
245 if (!ok) { | |
246 INTERNAL_READ_ERROR(kSetupMetadata); | |
247 return false; | |
248 } | |
249 if (!found) { | |
250 // Initialize new backing store. | |
251 db_schema_version = latest_known_schema_version; | |
252 PutInt( | |
253 transaction.get(), LevelDBSlice(schema_version_key), db_schema_version); | |
254 db_data_version = latest_known_data_version; | |
255 PutInt(transaction.get(), LevelDBSlice(data_version_key), db_data_version); | |
256 } else { | |
257 // Upgrade old backing store. | |
258 DCHECK(db_schema_version <= latest_known_schema_version); | |
259 if (db_schema_version < 1) { | |
260 db_schema_version = 1; | |
261 PutInt(transaction.get(), | |
262 LevelDBSlice(schema_version_key), | |
263 db_schema_version); | |
264 const std::vector<char> start_key = | |
265 DatabaseNameKey::EncodeMinKeyForOrigin(origin); | |
266 const std::vector<char> stop_key = | |
267 DatabaseNameKey::EncodeStopKeyForOrigin(origin); | |
268 scoped_ptr<LevelDBIterator> it = db->CreateIterator(); | |
269 for (it->Seek(LevelDBSlice(start_key)); | |
270 it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(stop_key)) < 0; | |
271 it->Next()) { | |
272 int64_t database_id = 0; | |
273 found = false; | |
274 bool ok = GetInt(transaction.get(), it->Key(), database_id, found); | |
275 if (!ok) { | |
276 INTERNAL_READ_ERROR(kSetupMetadata); | |
277 return false; | |
278 } | |
279 if (!found) { | |
280 INTERNAL_CONSISTENCY_ERROR(kSetupMetadata); | |
281 return false; | |
282 } | |
283 std::vector<char> int_version_key = DatabaseMetaDataKey::Encode( | |
284 database_id, DatabaseMetaDataKey::UserIntVersion); | |
285 PutVarInt(transaction.get(), | |
286 LevelDBSlice(int_version_key), | |
287 IndexedDBDatabaseMetadata::DefaultIntVersion); | |
288 } | |
289 } | |
290 if (db_schema_version < 2) { | |
291 db_schema_version = 2; | |
292 PutInt(transaction.get(), | |
293 LevelDBSlice(schema_version_key), | |
294 db_schema_version); | |
295 db_data_version = kWireVersion; | |
296 PutInt( | |
297 transaction.get(), LevelDBSlice(data_version_key), db_data_version); | |
298 } | |
299 } | |
300 | |
301 // All new values will be written using this serialization version. | |
302 found = false; | |
303 ok = GetInt(transaction.get(), | |
304 LevelDBSlice(data_version_key), | |
305 db_data_version, | |
306 found); | |
307 if (!ok) { | |
308 INTERNAL_READ_ERROR(kSetupMetadata); | |
309 return false; | |
310 } | |
311 if (!found) { | |
312 INTERNAL_CONSISTENCY_ERROR(kSetupMetadata); | |
313 return false; | |
314 } | |
315 if (db_data_version < latest_known_data_version) { | |
316 db_data_version = latest_known_data_version; | |
317 PutInt(transaction.get(), LevelDBSlice(data_version_key), db_data_version); | |
318 } | |
319 | |
320 DCHECK(db_schema_version == latest_known_schema_version); | |
321 DCHECK(db_data_version == latest_known_data_version); | |
322 | |
323 if (!transaction->Commit()) { | |
324 INTERNAL_WRITE_ERROR(kSetupMetadata); | |
325 return false; | |
326 } | |
327 return true; | |
328 } | |
329 | |
330 template <typename DBOrTransaction> | |
331 WARN_UNUSED_RESULT static bool | |
332 GetMaxObjectStoreId(DBOrTransaction* db, | |
333 int64_t database_id, | |
334 int64_t& max_object_store_id) { | |
335 const std::vector<char> max_object_store_id_key = DatabaseMetaDataKey::Encode( | |
336 database_id, DatabaseMetaDataKey::MaxObjectStoreId); | |
337 bool ok = | |
338 GetMaxObjectStoreId(db, max_object_store_id_key, max_object_store_id); | |
339 return ok; | |
340 } | |
341 | |
342 template <typename DBOrTransaction> | |
343 WARN_UNUSED_RESULT static bool | |
344 GetMaxObjectStoreId(DBOrTransaction* db, | |
345 const std::vector<char>& max_object_store_id_key, | |
346 int64_t& max_object_store_id) { | |
347 max_object_store_id = -1; | |
348 bool found = false; | |
349 bool ok = GetInt( | |
350 db, LevelDBSlice(max_object_store_id_key), max_object_store_id, found); | |
351 if (!ok) | |
352 return false; | |
353 if (!found) | |
354 max_object_store_id = 0; | |
355 | |
356 DCHECK(max_object_store_id >= 0); | |
357 return true; | |
358 } | |
359 | |
360 class DefaultLevelDBFactory : public LevelDBFactory { | |
361 public: | |
362 virtual scoped_ptr<LevelDBDatabase> OpenLevelDB( | |
363 const string16& file_name, | |
364 const LevelDBComparator* comparator) OVERRIDE { | |
365 return LevelDBDatabase::Open( | |
366 base::FilePath::FromUTF8Unsafe(UTF16ToUTF8(file_name)), comparator); | |
367 } | |
368 virtual bool DestroyLevelDB(const string16& file_name) OVERRIDE { | |
369 return LevelDBDatabase::Destroy( | |
370 base::FilePath::FromUTF8Unsafe(UTF16ToUTF8(file_name))); | |
371 } | |
372 }; | |
373 | |
374 IndexedDBBackingStore::IndexedDBBackingStore( | |
375 const string16& identifier, | |
376 scoped_ptr<LevelDBDatabase> db, | |
377 scoped_ptr<LevelDBComparator> comparator) | |
378 : identifier_(identifier), | |
379 db_(db.Pass()), | |
380 comparator_(comparator.Pass()), | |
381 weak_factory_(this) {} | |
382 | |
383 IndexedDBBackingStore::~IndexedDBBackingStore() { | |
384 // db_'s destructor uses comparator_. The order of destruction is important. | |
385 db_.reset(); | |
386 comparator_.reset(); | |
387 } | |
388 | |
389 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier( | |
390 const std::vector<char>& primary_key, | |
391 int64_t version) | |
392 : primary_key_(primary_key), version_(version) { | |
393 DCHECK(!primary_key.empty()); | |
394 } | |
395 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier() | |
396 : primary_key_(), version_(-1) {} | |
397 IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {} | |
398 | |
399 IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions() {} | |
400 IndexedDBBackingStore::Cursor::CursorOptions::~CursorOptions() {} | |
401 | |
402 enum IndexedDBLevelDBBackingStoreOpenResult { | |
403 IndexedDBLevelDBBackingStoreOpenMemorySuccess, | |
jamesr
2013/05/21 23:56:06
should be SHOUTY
jsbell
2013/05/22 17:54:44
Done.
| |
404 IndexedDBLevelDBBackingStoreOpenSuccess, | |
405 IndexedDBLevelDBBackingStoreOpenFailedDirectory, | |
406 IndexedDBLevelDBBackingStoreOpenFailedUnknownSchema, | |
407 IndexedDBLevelDBBackingStoreOpenCleanupDestroyFailed, | |
408 IndexedDBLevelDBBackingStoreOpenCleanupReopenFailed, | |
409 IndexedDBLevelDBBackingStoreOpenCleanupReopenSuccess, | |
410 IndexedDBLevelDBBackingStoreOpenFailedIOErrCheckingSchema, | |
411 IndexedDBLevelDBBackingStoreOpenFailedUnknownErr, | |
412 IndexedDBLevelDBBackingStoreOpenMemoryFailed, | |
413 IndexedDBLevelDBBackingStoreOpenAttemptNonASCII, | |
414 IndexedDBLevelDBBackingStoreOpenMax, | |
415 }; | |
416 | |
417 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open( | |
418 const string16& database_identifier, | |
419 const string16& path_base_arg, | |
420 const string16& file_identifier) { | |
421 DefaultLevelDBFactory leveldb_factory; | |
422 return IndexedDBBackingStore::Open( | |
423 database_identifier, path_base_arg, file_identifier, &leveldb_factory); | |
424 } | |
425 | |
426 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open( | |
427 const string16& database_identifier, | |
428 const string16& path_base_arg, | |
429 const string16& file_identifier, | |
430 LevelDBFactory* leveldb_factory) { | |
431 IDB_TRACE("IndexedDBBackingStore::open"); | |
432 DCHECK(!path_base_arg.empty()); | |
433 string16 path_base = path_base_arg; | |
434 | |
435 scoped_ptr<LevelDBComparator> comparator(new Comparator()); | |
436 scoped_ptr<LevelDBDatabase> db; | |
437 | |
438 if (!IsStringASCII(path_base)) { | |
439 base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", | |
440 1, | |
441 IndexedDBLevelDBBackingStoreOpenMax, | |
442 IndexedDBLevelDBBackingStoreOpenMax + 1, | |
443 base::HistogramBase::kUmaTargetedHistogramFlag) | |
444 ->Add(IndexedDBLevelDBBackingStoreOpenAttemptNonASCII); | |
445 } | |
446 base::FilePath file_path_base(UTF16ToUTF8(path_base)); | |
447 if (!file_util::CreateDirectory(file_path_base)) { | |
448 LOG(ERROR) << "Unable to create IndexedDB database path " << path_base; | |
449 base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", | |
450 1, | |
451 IndexedDBLevelDBBackingStoreOpenMax, | |
452 IndexedDBLevelDBBackingStoreOpenMax + 1, | |
453 base::HistogramBase::kUmaTargetedHistogramFlag) | |
454 ->Add(IndexedDBLevelDBBackingStoreOpenFailedDirectory); | |
455 return scoped_refptr<IndexedDBBackingStore>(); | |
456 } | |
457 | |
458 base::FilePath path_utf8 = file_path_base.Append( | |
459 UTF16ToUTF8(database_identifier) + ".indexeddb.leveldb"); | |
460 string16 path_utf16 = UTF8ToUTF16(path_utf8.value()); | |
461 | |
462 db = leveldb_factory->OpenLevelDB(path_utf16, comparator.get()); | |
463 | |
464 if (db) { | |
465 bool known = false; | |
466 bool ok = IsSchemaKnown(db.get(), known); | |
467 if (!ok) { | |
468 LOG(ERROR) << "IndexedDB had IO error checking schema, treating it as " | |
469 "failure to open"; | |
470 base::Histogram::FactoryGet( | |
471 "WebCore.IndexedDB.BackingStore.OpenStatus", | |
472 1, | |
473 IndexedDBLevelDBBackingStoreOpenMax, | |
474 IndexedDBLevelDBBackingStoreOpenMax + 1, | |
475 base::HistogramBase::kUmaTargetedHistogramFlag) | |
476 ->Add(IndexedDBLevelDBBackingStoreOpenFailedIOErrCheckingSchema); | |
477 db.reset(); | |
478 } else if (!known) { | |
479 LOG(ERROR) << "IndexedDB backing store had unknown schema, treating it " | |
480 "as failure to open"; | |
481 base::Histogram::FactoryGet( | |
482 "WebCore.IndexedDB.BackingStore.OpenStatus", | |
483 1, | |
484 IndexedDBLevelDBBackingStoreOpenMax, | |
485 IndexedDBLevelDBBackingStoreOpenMax + 1, | |
486 base::HistogramBase::kUmaTargetedHistogramFlag) | |
487 ->Add(IndexedDBLevelDBBackingStoreOpenFailedUnknownSchema); | |
488 db.reset(); | |
489 } | |
490 } | |
491 | |
492 if (db) { | |
493 base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", | |
494 1, | |
495 IndexedDBLevelDBBackingStoreOpenMax, | |
496 IndexedDBLevelDBBackingStoreOpenMax + 1, | |
497 base::HistogramBase::kUmaTargetedHistogramFlag) | |
498 ->Add(IndexedDBLevelDBBackingStoreOpenSuccess); | |
499 } else { | |
500 LOG(ERROR) << "IndexedDB backing store open failed, attempting cleanup"; | |
501 bool success = leveldb_factory->DestroyLevelDB(path_utf16); | |
502 if (!success) { | |
503 LOG(ERROR) << "IndexedDB backing store cleanup failed"; | |
504 base::Histogram::FactoryGet( | |
505 "WebCore.IndexedDB.BackingStore.OpenStatus", | |
506 1, | |
507 IndexedDBLevelDBBackingStoreOpenMax, | |
508 IndexedDBLevelDBBackingStoreOpenMax + 1, | |
509 base::HistogramBase::kUmaTargetedHistogramFlag) | |
510 ->Add(IndexedDBLevelDBBackingStoreOpenCleanupDestroyFailed); | |
511 return scoped_refptr<IndexedDBBackingStore>(); | |
512 } | |
513 | |
514 LOG(ERROR) << "IndexedDB backing store cleanup succeeded, reopening"; | |
515 db = leveldb_factory->OpenLevelDB(path_utf16, comparator.get()); | |
516 if (!db) { | |
517 LOG(ERROR) << "IndexedDB backing store reopen after recovery failed"; | |
518 base::Histogram::FactoryGet( | |
519 "WebCore.IndexedDB.BackingStore.OpenStatus", | |
520 1, | |
521 IndexedDBLevelDBBackingStoreOpenMax, | |
522 IndexedDBLevelDBBackingStoreOpenMax + 1, | |
523 base::HistogramBase::kUmaTargetedHistogramFlag) | |
524 ->Add(IndexedDBLevelDBBackingStoreOpenCleanupReopenFailed); | |
525 return scoped_refptr<IndexedDBBackingStore>(); | |
526 } | |
527 base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", | |
528 1, | |
529 IndexedDBLevelDBBackingStoreOpenMax, | |
530 IndexedDBLevelDBBackingStoreOpenMax + 1, | |
531 base::HistogramBase::kUmaTargetedHistogramFlag) | |
532 ->Add(IndexedDBLevelDBBackingStoreOpenCleanupReopenSuccess); | |
533 } | |
534 | |
535 if (!db) { | |
536 NOTREACHED(); | |
537 base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", | |
538 1, | |
539 IndexedDBLevelDBBackingStoreOpenMax, | |
540 IndexedDBLevelDBBackingStoreOpenMax + 1, | |
541 base::HistogramBase::kUmaTargetedHistogramFlag) | |
542 ->Add(IndexedDBLevelDBBackingStoreOpenFailedUnknownErr); | |
543 return scoped_refptr<IndexedDBBackingStore>(); | |
544 } | |
545 | |
546 return Create(file_identifier, db.Pass(), comparator.Pass()); | |
547 } | |
548 | |
549 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory( | |
550 const string16& identifier) { | |
551 DefaultLevelDBFactory leveldb_factory; | |
552 return IndexedDBBackingStore::OpenInMemory(identifier, &leveldb_factory); | |
553 } | |
554 | |
555 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory( | |
556 const string16& identifier, | |
557 LevelDBFactory* leveldb_factory) { | |
558 IDB_TRACE("IndexedDBBackingStore::open_in_memory"); | |
559 | |
560 scoped_ptr<LevelDBComparator> comparator(new Comparator()); | |
561 scoped_ptr<LevelDBDatabase> db = | |
562 LevelDBDatabase::OpenInMemory(comparator.get()); | |
563 if (!db) { | |
564 LOG(ERROR) << "LevelDBDatabase::open_in_memory failed."; | |
565 base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", | |
566 1, | |
567 IndexedDBLevelDBBackingStoreOpenMax, | |
568 IndexedDBLevelDBBackingStoreOpenMax + 1, | |
569 base::HistogramBase::kUmaTargetedHistogramFlag) | |
570 ->Add(IndexedDBLevelDBBackingStoreOpenMemoryFailed); | |
571 return scoped_refptr<IndexedDBBackingStore>(); | |
572 } | |
573 base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", | |
574 1, | |
575 IndexedDBLevelDBBackingStoreOpenMax, | |
576 IndexedDBLevelDBBackingStoreOpenMax + 1, | |
577 base::HistogramBase::kUmaTargetedHistogramFlag) | |
578 ->Add(IndexedDBLevelDBBackingStoreOpenMemorySuccess); | |
579 | |
580 return Create(identifier, db.Pass(), comparator.Pass()); | |
581 } | |
582 | |
583 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create( | |
584 const string16& identifier, | |
585 scoped_ptr<LevelDBDatabase> db, | |
586 scoped_ptr<LevelDBComparator> comparator) { | |
587 // TODO: Handle comparator name changes. | |
588 scoped_refptr<IndexedDBBackingStore> backing_store( | |
589 new IndexedDBBackingStore(identifier, db.Pass(), comparator.Pass())); | |
590 | |
591 if (!SetUpMetadata(backing_store->db_.get(), identifier)) | |
592 return scoped_refptr<IndexedDBBackingStore>(); | |
593 | |
594 return backing_store; | |
595 } | |
596 | |
597 std::vector<string16> IndexedDBBackingStore::GetDatabaseNames() { | |
598 std::vector<string16> found_names; | |
599 const std::vector<char> start_key = | |
600 DatabaseNameKey::EncodeMinKeyForOrigin(identifier_); | |
601 const std::vector<char> stop_key = | |
602 DatabaseNameKey::EncodeStopKeyForOrigin(identifier_); | |
603 | |
604 DCHECK(found_names.empty()); | |
605 | |
606 scoped_ptr<LevelDBIterator> it = db_->CreateIterator(); | |
607 for (it->Seek(LevelDBSlice(start_key)); | |
608 it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(stop_key)) < 0; | |
609 it->Next()) { | |
610 const char* p = it->Key().begin(); | |
611 const char* limit = it->Key().end(); | |
612 | |
613 DatabaseNameKey database_name_key; | |
614 p = DatabaseNameKey::Decode(p, limit, &database_name_key); | |
615 DCHECK(p); | |
616 | |
617 found_names.push_back(database_name_key.database_name()); | |
618 } | |
619 return found_names; | |
620 } | |
621 | |
622 bool IndexedDBBackingStore::GetIDBDatabaseMetaData( | |
623 const string16& name, | |
624 IndexedDBDatabaseMetadata* metadata, | |
625 bool& found) { | |
626 const std::vector<char> key = DatabaseNameKey::Encode(identifier_, name); | |
627 found = false; | |
628 | |
629 bool ok = GetInt(db_.get(), LevelDBSlice(key), metadata->id, found); | |
630 if (!ok) { | |
631 INTERNAL_READ_ERROR(kGetIDBDatabaseMetaData); | |
632 return false; | |
633 } | |
634 if (!found) | |
635 return true; | |
636 | |
637 ok = GetString(db_.get(), | |
638 LevelDBSlice(DatabaseMetaDataKey::Encode( | |
639 metadata->id, DatabaseMetaDataKey::UserVersion)), | |
640 metadata->version, | |
641 found); | |
642 if (!ok) { | |
643 INTERNAL_READ_ERROR(kGetIDBDatabaseMetaData); | |
644 return false; | |
645 } | |
646 if (!found) { | |
647 INTERNAL_CONSISTENCY_ERROR(kGetIDBDatabaseMetaData); | |
648 return false; | |
649 } | |
650 | |
651 ok = GetVarInt(db_.get(), | |
652 LevelDBSlice(DatabaseMetaDataKey::Encode( | |
653 metadata->id, DatabaseMetaDataKey::UserIntVersion)), | |
654 metadata->int_version, | |
655 found); | |
656 if (!ok) { | |
657 INTERNAL_READ_ERROR(kGetIDBDatabaseMetaData); | |
658 return false; | |
659 } | |
660 if (!found) { | |
661 INTERNAL_CONSISTENCY_ERROR(kGetIDBDatabaseMetaData); | |
662 return false; | |
663 } | |
664 | |
665 if (metadata->int_version == IndexedDBDatabaseMetadata::DefaultIntVersion) | |
666 metadata->int_version = IndexedDBDatabaseMetadata::NoIntVersion; | |
667 | |
668 ok = GetMaxObjectStoreId( | |
669 db_.get(), metadata->id, metadata->max_object_store_id); | |
670 if (!ok) { | |
671 INTERNAL_READ_ERROR(kGetIDBDatabaseMetaData); | |
672 return false; | |
673 } | |
674 | |
675 return true; | |
676 } | |
677 | |
678 WARN_UNUSED_RESULT static bool GetNewDatabaseId(LevelDBDatabase* db, | |
679 int64_t& new_id) { | |
680 scoped_refptr<LevelDBTransaction> transaction = | |
681 LevelDBTransaction::Create(db); | |
682 | |
683 new_id = -1; | |
684 int64_t max_database_id = -1; | |
685 bool found = false; | |
686 bool ok = GetInt(transaction.get(), | |
687 LevelDBSlice(MaxDatabaseIdKey::Encode()), | |
688 max_database_id, | |
689 found); | |
690 if (!ok) { | |
691 INTERNAL_READ_ERROR(kGetNewDatabaseId); | |
692 return false; | |
693 } | |
694 if (!found) | |
695 max_database_id = 0; | |
696 | |
697 DCHECK(max_database_id >= 0); | |
698 | |
699 int64_t database_id = max_database_id + 1; | |
700 PutInt( | |
701 transaction.get(), LevelDBSlice(MaxDatabaseIdKey::Encode()), database_id); | |
702 if (!transaction->Commit()) { | |
703 INTERNAL_WRITE_ERROR(kGetNewDatabaseId); | |
704 return false; | |
705 } | |
706 new_id = database_id; | |
707 return true; | |
708 } | |
709 | |
710 bool IndexedDBBackingStore::CreateIDBDatabaseMetaData(const string16& name, | |
711 const string16& version, | |
712 int64_t int_version, | |
713 int64_t& row_id) { | |
714 bool ok = GetNewDatabaseId(db_.get(), row_id); | |
715 if (!ok) | |
716 return false; | |
717 DCHECK(row_id >= 0); | |
718 | |
719 if (int_version == IndexedDBDatabaseMetadata::NoIntVersion) | |
720 int_version = IndexedDBDatabaseMetadata::DefaultIntVersion; | |
721 | |
722 scoped_refptr<LevelDBTransaction> transaction = | |
723 LevelDBTransaction::Create(db_.get()); | |
724 PutInt(transaction.get(), | |
725 LevelDBSlice(DatabaseNameKey::Encode(identifier_, name)), | |
726 row_id); | |
727 PutString(transaction.get(), | |
728 LevelDBSlice(DatabaseMetaDataKey::Encode( | |
729 row_id, DatabaseMetaDataKey::UserVersion)), | |
730 version); | |
731 PutVarInt(transaction.get(), | |
732 LevelDBSlice(DatabaseMetaDataKey::Encode( | |
733 row_id, DatabaseMetaDataKey::UserIntVersion)), | |
734 int_version); | |
735 if (!transaction->Commit()) { | |
736 INTERNAL_WRITE_ERROR(kCreateIDBDatabaseMetaData); | |
737 return false; | |
738 } | |
739 return true; | |
740 } | |
741 | |
742 bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion( | |
743 IndexedDBBackingStore::Transaction* transaction, | |
744 int64_t row_id, | |
745 int64_t int_version) { | |
746 if (int_version == IndexedDBDatabaseMetadata::NoIntVersion) | |
747 int_version = IndexedDBDatabaseMetadata::DefaultIntVersion; | |
748 DCHECK(int_version >= 0) << "int_version was " << int_version; | |
749 PutVarInt(Transaction::LevelDBTransactionFrom(transaction), | |
750 LevelDBSlice(DatabaseMetaDataKey::Encode( | |
751 row_id, DatabaseMetaDataKey::UserIntVersion)), | |
752 int_version); | |
753 return true; | |
754 } | |
755 | |
756 bool IndexedDBBackingStore::UpdateIDBDatabaseMetaData( | |
757 IndexedDBBackingStore::Transaction* transaction, | |
758 int64_t row_id, | |
759 const string16& version) { | |
760 PutString(Transaction::LevelDBTransactionFrom(transaction), | |
761 LevelDBSlice(DatabaseMetaDataKey::Encode( | |
762 row_id, DatabaseMetaDataKey::UserVersion)), | |
763 version); | |
764 return true; | |
765 } | |
766 | |
767 static void DeleteRange(LevelDBTransaction* transaction, | |
768 const std::vector<char>& begin, | |
769 const std::vector<char>& end) { | |
770 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator(); | |
771 for (it->Seek(LevelDBSlice(begin)); | |
772 it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(end)) < 0; | |
773 it->Next()) | |
774 transaction->Remove(it->Key()); | |
775 } | |
776 | |
777 bool IndexedDBBackingStore::DeleteDatabase(const string16& name) { | |
778 IDB_TRACE("IndexedDBBackingStore::delete_database"); | |
779 scoped_ptr<LevelDBWriteOnlyTransaction> transaction = | |
780 LevelDBWriteOnlyTransaction::Create(db_.get()); | |
781 | |
782 IndexedDBDatabaseMetadata metadata; | |
783 bool success = false; | |
784 bool ok = GetIDBDatabaseMetaData(name, &metadata, success); | |
785 if (!ok) | |
786 return false; | |
787 if (!success) | |
788 return true; | |
789 | |
790 const std::vector<char> start_key = | |
791 DatabaseMetaDataKey::Encode(metadata.id, DatabaseMetaDataKey::OriginName); | |
792 const std::vector<char> stop_key = DatabaseMetaDataKey::Encode( | |
793 metadata.id + 1, DatabaseMetaDataKey::OriginName); | |
794 scoped_ptr<LevelDBIterator> it = db_->CreateIterator(); | |
795 for (it->Seek(LevelDBSlice(start_key)); | |
796 it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(stop_key)) < 0; | |
797 it->Next()) | |
798 transaction->Remove(it->Key()); | |
799 | |
800 const std::vector<char> key = DatabaseNameKey::Encode(identifier_, name); | |
801 transaction->Remove(LevelDBSlice(key)); | |
802 | |
803 if (!transaction->Commit()) { | |
804 INTERNAL_WRITE_ERROR(kDeleteDatabase); | |
805 return false; | |
806 } | |
807 return true; | |
808 } | |
809 | |
810 static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator* it, | |
811 const std::vector<char>& stop_key, | |
812 int64_t object_store_id, | |
813 int64_t meta_data_type) { | |
814 if (!it->IsValid() || CompareKeys(it->Key(), LevelDBSlice(stop_key)) >= 0) | |
815 return false; | |
816 | |
817 ObjectStoreMetaDataKey meta_data_key; | |
818 const char* p = ObjectStoreMetaDataKey::Decode( | |
819 it->Key().begin(), it->Key().end(), &meta_data_key); | |
820 DCHECK(p); | |
821 if (meta_data_key.ObjectStoreId() != object_store_id) | |
822 return false; | |
823 if (meta_data_key.MetaDataType() != meta_data_type) | |
824 return false; | |
825 return true; | |
826 } | |
827 | |
828 // TODO: This should do some error handling rather than plowing ahead when bad | |
829 // data is encountered. | |
830 bool IndexedDBBackingStore::GetObjectStores( | |
831 int64_t database_id, | |
832 IndexedDBDatabaseMetadata::ObjectStoreMap* object_stores) { | |
833 IDB_TRACE("IndexedDBBackingStore::get_object_stores"); | |
834 if (!KeyPrefix::IsValidDatabaseId(database_id)) | |
835 return false; | |
836 const std::vector<char> start_key = | |
837 ObjectStoreMetaDataKey::Encode(database_id, 1, 0); | |
838 const std::vector<char> stop_key = | |
839 ObjectStoreMetaDataKey::EncodeMaxKey(database_id); | |
840 | |
841 DCHECK(object_stores->empty()); | |
842 | |
843 scoped_ptr<LevelDBIterator> it = db_->CreateIterator(); | |
844 it->Seek(LevelDBSlice(start_key)); | |
845 while (it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(stop_key)) < 0) { | |
846 const char* p = it->Key().begin(); | |
847 const char* limit = it->Key().end(); | |
848 | |
849 ObjectStoreMetaDataKey meta_data_key; | |
850 p = ObjectStoreMetaDataKey::Decode(p, limit, &meta_data_key); | |
851 DCHECK(p); | |
852 if (meta_data_key.MetaDataType() != ObjectStoreMetaDataKey::Name) { | |
853 INTERNAL_CONSISTENCY_ERROR(kGetObjectStores); | |
854 // Possible stale metadata, but don't fail the load. | |
855 it->Next(); | |
856 continue; | |
857 } | |
858 | |
859 int64_t object_store_id = meta_data_key.ObjectStoreId(); | |
860 | |
861 // TODO: Do this by direct key lookup rather than iteration, to simplify. | |
862 string16 object_store_name = | |
863 DecodeString(it->Value().begin(), it->Value().end()); | |
864 | |
865 it->Next(); | |
866 if (!CheckObjectStoreAndMetaDataType(it.get(), | |
867 stop_key, | |
868 object_store_id, | |
869 ObjectStoreMetaDataKey::KeyPath)) { | |
870 INTERNAL_CONSISTENCY_ERROR(kGetObjectStores); | |
871 break; | |
872 } | |
873 IndexedDBKeyPath key_path = | |
874 DecodeIDBKeyPath(it->Value().begin(), it->Value().end()); | |
875 | |
876 it->Next(); | |
877 if (!CheckObjectStoreAndMetaDataType( | |
878 it.get(), | |
879 stop_key, | |
880 object_store_id, | |
881 ObjectStoreMetaDataKey::AutoIncrement)) { | |
882 INTERNAL_CONSISTENCY_ERROR(kGetObjectStores); | |
883 break; | |
884 } | |
885 bool auto_increment = DecodeBool(it->Value().begin(), it->Value().end()); | |
886 | |
887 it->Next(); // Is evicatble. | |
888 if (!CheckObjectStoreAndMetaDataType(it.get(), | |
889 stop_key, | |
890 object_store_id, | |
891 ObjectStoreMetaDataKey::Evictable)) { | |
892 INTERNAL_CONSISTENCY_ERROR(kGetObjectStores); | |
893 break; | |
894 } | |
895 | |
896 it->Next(); // Last version. | |
897 if (!CheckObjectStoreAndMetaDataType(it.get(), | |
898 stop_key, | |
899 object_store_id, | |
900 ObjectStoreMetaDataKey::LastVersion)) { | |
901 INTERNAL_CONSISTENCY_ERROR(kGetObjectStores); | |
902 break; | |
903 } | |
904 | |
905 it->Next(); // Maximum index id allocated. | |
906 if (!CheckObjectStoreAndMetaDataType(it.get(), | |
907 stop_key, | |
908 object_store_id, | |
909 ObjectStoreMetaDataKey::MaxIndexId)) { | |
910 INTERNAL_CONSISTENCY_ERROR(kGetObjectStores); | |
911 break; | |
912 } | |
913 int64_t max_index_id = DecodeInt(it->Value().begin(), it->Value().end()); | |
914 | |
915 it->Next(); // [optional] has key path (is not null) | |
916 if (CheckObjectStoreAndMetaDataType(it.get(), | |
917 stop_key, | |
918 object_store_id, | |
919 ObjectStoreMetaDataKey::HasKeyPath)) { | |
920 bool has_key_path = DecodeBool(it->Value().begin(), it->Value().end()); | |
921 // This check accounts for two layers of legacy coding: | |
922 // (1) Initially, has_key_path was added to distinguish null vs. string. | |
923 // (2) Later, null vs. string vs. array was stored in the key_path itself. | |
924 // So this check is only relevant for string-type key_paths. | |
925 if (!has_key_path && | |
926 (key_path.type() == WebKit::WebIDBKeyPath::StringType && | |
927 !key_path.string().empty())) { | |
928 INTERNAL_CONSISTENCY_ERROR(kGetObjectStores); | |
929 break; | |
930 } | |
931 if (!has_key_path) | |
932 key_path = IndexedDBKeyPath(); | |
933 it->Next(); | |
934 } | |
935 | |
936 int64_t key_generator_current_number = -1; | |
937 if (CheckObjectStoreAndMetaDataType( | |
938 it.get(), | |
939 stop_key, | |
940 object_store_id, | |
941 ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber)) { | |
942 key_generator_current_number = | |
943 DecodeInt(it->Value().begin(), it->Value().end()); | |
944 // TODO: Return key_generator_current_number, cache in object store, and | |
945 // write lazily to backing store. | |
946 // For now, just assert that if it was written it was valid. | |
947 DCHECK(key_generator_current_number >= KeyGeneratorInitialNumber); | |
948 it->Next(); | |
949 } | |
950 | |
951 IndexedDBObjectStoreMetadata metadata(object_store_name, | |
952 object_store_id, | |
953 key_path, | |
954 auto_increment, | |
955 max_index_id); | |
956 if (!GetIndexes(database_id, object_store_id, &metadata.indexes)) | |
957 return false; | |
958 (*object_stores)[object_store_id] = metadata; | |
959 } | |
960 return true; | |
961 } | |
962 | |
963 WARN_UNUSED_RESULT static bool SetMaxObjectStoreId( | |
964 LevelDBTransaction* transaction, | |
965 int64_t database_id, | |
966 int64_t object_store_id) { | |
967 const std::vector<char> max_object_store_id_key = DatabaseMetaDataKey::Encode( | |
968 database_id, DatabaseMetaDataKey::MaxObjectStoreId); | |
969 int64_t max_object_store_id = -1; | |
970 bool ok = GetMaxObjectStoreId( | |
971 transaction, max_object_store_id_key, max_object_store_id); | |
972 if (!ok) { | |
973 INTERNAL_READ_ERROR(kSetMaxObjectStoreId); | |
974 return false; | |
975 } | |
976 | |
977 if (object_store_id <= max_object_store_id) { | |
978 INTERNAL_CONSISTENCY_ERROR(kSetMaxObjectStoreId); | |
979 return false; | |
980 } | |
981 PutInt(transaction, LevelDBSlice(max_object_store_id_key), object_store_id); | |
982 return true; | |
983 } | |
984 | |
985 bool IndexedDBBackingStore::CreateObjectStore( | |
986 IndexedDBBackingStore::Transaction* transaction, | |
987 int64_t database_id, | |
988 int64_t object_store_id, | |
989 const string16& name, | |
990 const IndexedDBKeyPath& key_path, | |
991 bool auto_increment) { | |
992 IDB_TRACE("IndexedDBBackingStore::create_object_store"); | |
993 if (!KeyPrefix::ValidIds(database_id, object_store_id)) | |
994 return false; | |
995 LevelDBTransaction* leveldb_transaction = | |
996 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
997 if (!SetMaxObjectStoreId(leveldb_transaction, database_id, object_store_id)) | |
998 return false; | |
999 | |
1000 const std::vector<char> name_key = ObjectStoreMetaDataKey::Encode( | |
1001 database_id, object_store_id, ObjectStoreMetaDataKey::Name); | |
1002 const std::vector<char> key_path_key = ObjectStoreMetaDataKey::Encode( | |
1003 database_id, object_store_id, ObjectStoreMetaDataKey::KeyPath); | |
1004 const std::vector<char> auto_increment_key = ObjectStoreMetaDataKey::Encode( | |
1005 database_id, object_store_id, ObjectStoreMetaDataKey::AutoIncrement); | |
1006 const std::vector<char> evictable_key = ObjectStoreMetaDataKey::Encode( | |
1007 database_id, object_store_id, ObjectStoreMetaDataKey::Evictable); | |
1008 const std::vector<char> last_version_key = ObjectStoreMetaDataKey::Encode( | |
1009 database_id, object_store_id, ObjectStoreMetaDataKey::LastVersion); | |
1010 const std::vector<char> max_index_id_key = ObjectStoreMetaDataKey::Encode( | |
1011 database_id, object_store_id, ObjectStoreMetaDataKey::MaxIndexId); | |
1012 const std::vector<char> has_key_path_key = ObjectStoreMetaDataKey::Encode( | |
1013 database_id, object_store_id, ObjectStoreMetaDataKey::HasKeyPath); | |
1014 const std::vector<char> key_generator_current_number_key = | |
1015 ObjectStoreMetaDataKey::Encode( | |
1016 database_id, | |
1017 object_store_id, | |
1018 ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber); | |
1019 const std::vector<char> names_key = | |
1020 ObjectStoreNamesKey::Encode(database_id, name); | |
1021 | |
1022 PutString(leveldb_transaction, LevelDBSlice(name_key), name); | |
1023 PutIDBKeyPath(leveldb_transaction, LevelDBSlice(key_path_key), key_path); | |
1024 PutInt(leveldb_transaction, LevelDBSlice(auto_increment_key), auto_increment); | |
1025 PutInt(leveldb_transaction, LevelDBSlice(evictable_key), false); | |
1026 PutInt(leveldb_transaction, LevelDBSlice(last_version_key), 1); | |
1027 PutInt(leveldb_transaction, LevelDBSlice(max_index_id_key), MinimumIndexId); | |
1028 PutBool( | |
1029 leveldb_transaction, LevelDBSlice(has_key_path_key), !key_path.IsNull()); | |
1030 PutInt(leveldb_transaction, | |
1031 LevelDBSlice(key_generator_current_number_key), | |
1032 KeyGeneratorInitialNumber); | |
1033 PutInt(leveldb_transaction, LevelDBSlice(names_key), object_store_id); | |
1034 return true; | |
1035 } | |
1036 | |
1037 bool IndexedDBBackingStore::DeleteObjectStore( | |
1038 IndexedDBBackingStore::Transaction* transaction, | |
1039 int64_t database_id, | |
1040 int64_t object_store_id) { | |
1041 IDB_TRACE("IndexedDBBackingStore::delete_object_store"); | |
1042 if (!KeyPrefix::ValidIds(database_id, object_store_id)) | |
1043 return false; | |
1044 LevelDBTransaction* leveldb_transaction = | |
1045 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
1046 | |
1047 string16 object_store_name; | |
1048 bool found = false; | |
1049 bool ok = GetString( | |
1050 leveldb_transaction, | |
1051 LevelDBSlice(ObjectStoreMetaDataKey::Encode( | |
1052 database_id, object_store_id, ObjectStoreMetaDataKey::Name)), | |
1053 object_store_name, | |
1054 found); | |
1055 if (!ok) { | |
1056 INTERNAL_READ_ERROR(kDeleteObjectStore); | |
1057 return false; | |
1058 } | |
1059 if (!found) { | |
1060 INTERNAL_CONSISTENCY_ERROR(kDeleteObjectStore); | |
1061 return false; | |
1062 } | |
1063 | |
1064 DeleteRange( | |
1065 leveldb_transaction, | |
1066 ObjectStoreMetaDataKey::Encode(database_id, object_store_id, 0), | |
1067 ObjectStoreMetaDataKey::EncodeMaxKey(database_id, object_store_id)); | |
1068 | |
1069 leveldb_transaction->Remove(LevelDBSlice( | |
1070 ObjectStoreNamesKey::Encode(database_id, object_store_name))); | |
1071 | |
1072 DeleteRange(leveldb_transaction, | |
1073 IndexFreeListKey::Encode(database_id, object_store_id, 0), | |
1074 IndexFreeListKey::EncodeMaxKey(database_id, object_store_id)); | |
1075 DeleteRange(leveldb_transaction, | |
1076 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0), | |
1077 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id)); | |
1078 | |
1079 return ClearObjectStore(transaction, database_id, object_store_id); | |
1080 } | |
1081 | |
1082 bool IndexedDBBackingStore::GetRecord( | |
1083 IndexedDBBackingStore::Transaction* transaction, | |
1084 int64_t database_id, | |
1085 int64_t object_store_id, | |
1086 const IndexedDBKey& key, | |
1087 std::vector<char>& record) { | |
1088 IDB_TRACE("IndexedDBBackingStore::get_record"); | |
1089 if (!KeyPrefix::ValidIds(database_id, object_store_id)) | |
1090 return false; | |
1091 LevelDBTransaction* leveldb_transaction = | |
1092 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
1093 | |
1094 const std::vector<char> leveldb_key = | |
1095 ObjectStoreDataKey::Encode(database_id, object_store_id, key); | |
1096 std::vector<char> data; | |
1097 | |
1098 record.clear(); | |
1099 | |
1100 bool found = false; | |
1101 bool ok = leveldb_transaction->Get(LevelDBSlice(leveldb_key), data, found); | |
1102 if (!ok) { | |
1103 INTERNAL_READ_ERROR(kGetRecord); | |
1104 return false; | |
1105 } | |
1106 if (!found) | |
1107 return true; | |
1108 | |
1109 int64_t version; | |
1110 std::vector<char>::iterator p = | |
1111 DecodeVarInt(data.begin(), data.end(), version); | |
1112 if (p == data.begin()) { | |
1113 INTERNAL_READ_ERROR(kGetRecord); | |
1114 return false; | |
1115 } | |
1116 | |
1117 record.assign(p, data.end()); | |
1118 return true; | |
1119 } | |
1120 | |
1121 WARN_UNUSED_RESULT static bool GetNewVersionNumber( | |
1122 LevelDBTransaction* transaction, | |
1123 int64_t database_id, | |
1124 int64_t object_store_id, | |
1125 int64_t& new_version_number) { | |
1126 const std::vector<char> last_version_key = ObjectStoreMetaDataKey::Encode( | |
1127 database_id, object_store_id, ObjectStoreMetaDataKey::LastVersion); | |
1128 | |
1129 new_version_number = -1; | |
1130 int64_t last_version = -1; | |
1131 bool found = false; | |
1132 bool ok = | |
1133 GetInt(transaction, LevelDBSlice(last_version_key), last_version, found); | |
1134 if (!ok) { | |
1135 INTERNAL_READ_ERROR(kGetNewVersionNumber); | |
1136 return false; | |
1137 } | |
1138 if (!found) | |
1139 last_version = 0; | |
1140 | |
1141 DCHECK(last_version >= 0); | |
1142 | |
1143 int64_t version = last_version + 1; | |
1144 PutInt(transaction, LevelDBSlice(last_version_key), version); | |
1145 | |
1146 DCHECK(version > last_version); // TODO: Think about how we want to handle | |
1147 // the overflow scenario. | |
1148 | |
1149 new_version_number = version; | |
1150 return true; | |
1151 } | |
1152 | |
1153 bool IndexedDBBackingStore::PutRecord( | |
1154 IndexedDBBackingStore::Transaction* transaction, | |
1155 int64_t database_id, | |
1156 int64_t object_store_id, | |
1157 const IndexedDBKey& key, | |
1158 const std::vector<char>& value, | |
1159 RecordIdentifier* record_identifier) { | |
1160 IDB_TRACE("IndexedDBBackingStore::put_record"); | |
1161 if (!KeyPrefix::ValidIds(database_id, object_store_id)) | |
1162 return false; | |
1163 DCHECK(key.IsValid()); | |
1164 | |
1165 LevelDBTransaction* leveldb_transaction = | |
1166 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
1167 int64_t version = -1; | |
1168 bool ok = GetNewVersionNumber( | |
1169 leveldb_transaction, database_id, object_store_id, version); | |
1170 if (!ok) | |
1171 return false; | |
1172 DCHECK(version >= 0); | |
1173 const std::vector<char> object_storedata_key = | |
1174 ObjectStoreDataKey::Encode(database_id, object_store_id, key); | |
1175 | |
1176 std::vector<char> v(EncodeVarInt(version)); | |
1177 | |
1178 v.insert(v.end(), value.begin(), value.end()); | |
1179 | |
1180 leveldb_transaction->Put(LevelDBSlice(object_storedata_key), v); | |
1181 | |
1182 const std::vector<char> exists_entry_key = | |
1183 ExistsEntryKey::Encode(database_id, object_store_id, key); | |
1184 leveldb_transaction->Put(LevelDBSlice(exists_entry_key), EncodeInt(version)); | |
1185 | |
1186 record_identifier->Reset(EncodeIDBKey(key), version); | |
1187 return true; | |
1188 } | |
1189 | |
1190 bool IndexedDBBackingStore::ClearObjectStore( | |
1191 IndexedDBBackingStore::Transaction* transaction, | |
1192 int64_t database_id, | |
1193 int64_t object_store_id) { | |
1194 IDB_TRACE("IndexedDBBackingStore::clear_object_store"); | |
1195 if (!KeyPrefix::ValidIds(database_id, object_store_id)) | |
1196 return false; | |
1197 LevelDBTransaction* leveldb_transaction = | |
1198 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
1199 const std::vector<char> start_key = | |
1200 KeyPrefix(database_id, object_store_id).Encode(); | |
1201 const std::vector<char> stop_key = | |
1202 KeyPrefix(database_id, object_store_id + 1).Encode(); | |
1203 | |
1204 DeleteRange(leveldb_transaction, start_key, stop_key); | |
1205 return true; | |
1206 } | |
1207 | |
1208 bool IndexedDBBackingStore::DeleteRecord( | |
1209 IndexedDBBackingStore::Transaction* transaction, | |
1210 int64_t database_id, | |
1211 int64_t object_store_id, | |
1212 const RecordIdentifier& record_identifier) { | |
1213 IDB_TRACE("IndexedDBBackingStore::delete_record"); | |
1214 if (!KeyPrefix::ValidIds(database_id, object_store_id)) | |
1215 return false; | |
1216 LevelDBTransaction* leveldb_transaction = | |
1217 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
1218 | |
1219 const std::vector<char> object_store_data_key = ObjectStoreDataKey::Encode( | |
1220 database_id, object_store_id, record_identifier.primary_key()); | |
1221 leveldb_transaction->Remove(LevelDBSlice(object_store_data_key)); | |
1222 | |
1223 const std::vector<char> exists_entry_key = ExistsEntryKey::Encode( | |
1224 database_id, object_store_id, record_identifier.primary_key()); | |
1225 leveldb_transaction->Remove(LevelDBSlice(exists_entry_key)); | |
1226 return true; | |
1227 } | |
1228 | |
1229 bool IndexedDBBackingStore::GetKeyGeneratorCurrentNumber( | |
1230 IndexedDBBackingStore::Transaction* transaction, | |
1231 int64_t database_id, | |
1232 int64_t object_store_id, | |
1233 int64_t& key_generator_current_number) { | |
1234 if (!KeyPrefix::ValidIds(database_id, object_store_id)) | |
1235 return false; | |
1236 LevelDBTransaction* leveldb_transaction = | |
1237 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
1238 | |
1239 const std::vector<char> key_generator_current_number_key = | |
1240 ObjectStoreMetaDataKey::Encode( | |
1241 database_id, | |
1242 object_store_id, | |
1243 ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber); | |
1244 | |
1245 key_generator_current_number = -1; | |
1246 std::vector<char> data; | |
1247 | |
1248 bool found = false; | |
1249 bool ok = leveldb_transaction->Get( | |
1250 LevelDBSlice(key_generator_current_number_key), data, found); | |
1251 if (!ok) { | |
1252 INTERNAL_READ_ERROR(kGetKeyGeneratorCurrentNumber); | |
1253 return false; | |
1254 } | |
1255 if (found) { | |
1256 key_generator_current_number = DecodeInt(data.begin(), data.end()); | |
1257 } else { | |
1258 // Previously, the key generator state was not stored explicitly | |
1259 // but derived from the maximum numeric key present in existing | |
1260 // data. This violates the spec as the data may be cleared but the | |
1261 // key generator state must be preserved. | |
1262 const std::vector<char> start_key = | |
1263 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey()); | |
1264 const std::vector<char> stop_key = | |
1265 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey()); | |
1266 | |
1267 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator(); | |
1268 int64_t max_numeric_key = 0; | |
1269 | |
1270 for (it->Seek(LevelDBSlice(start_key)); | |
1271 it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(stop_key)) < 0; | |
1272 it->Next()) { | |
1273 const char* p = it->Key().begin(); | |
1274 const char* limit = it->Key().end(); | |
1275 | |
1276 ObjectStoreDataKey data_key; | |
1277 p = ObjectStoreDataKey::Decode(p, limit, &data_key); | |
1278 DCHECK(p); | |
1279 | |
1280 scoped_ptr<IndexedDBKey> user_key = data_key.user_key(); | |
1281 if (user_key->type() == WebKit::WebIDBKey::NumberType) { | |
1282 int64_t n = static_cast<int64_t>(user_key->number()); | |
1283 if (n > max_numeric_key) | |
1284 max_numeric_key = n; | |
1285 } | |
1286 } | |
1287 | |
1288 key_generator_current_number = max_numeric_key + 1; | |
1289 } | |
1290 | |
1291 return key_generator_current_number; | |
1292 } | |
1293 | |
1294 bool IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber( | |
1295 IndexedDBBackingStore::Transaction* transaction, | |
1296 int64_t database_id, | |
1297 int64_t object_store_id, | |
1298 int64_t new_number, | |
1299 bool check_current) { | |
1300 if (!KeyPrefix::ValidIds(database_id, object_store_id)) | |
1301 return false; | |
1302 LevelDBTransaction* leveldb_transaction = | |
1303 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
1304 | |
1305 if (check_current) { | |
1306 int64_t current_number; | |
1307 bool ok = GetKeyGeneratorCurrentNumber( | |
1308 transaction, database_id, object_store_id, current_number); | |
1309 if (!ok) | |
1310 return false; | |
1311 if (new_number <= current_number) | |
1312 return true; | |
1313 } | |
1314 | |
1315 const std::vector<char> key_generator_current_number_key = | |
1316 ObjectStoreMetaDataKey::Encode( | |
1317 database_id, | |
1318 object_store_id, | |
1319 ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber); | |
1320 PutInt(leveldb_transaction, | |
1321 LevelDBSlice(key_generator_current_number_key), | |
1322 new_number); | |
1323 return true; | |
1324 } | |
1325 | |
1326 bool IndexedDBBackingStore::KeyExistsInObjectStore( | |
1327 IndexedDBBackingStore::Transaction* transaction, | |
1328 int64_t database_id, | |
1329 int64_t object_store_id, | |
1330 const IndexedDBKey& key, | |
1331 RecordIdentifier* found_record_identifier, | |
1332 bool& found) { | |
1333 IDB_TRACE("IndexedDBBackingStore::key_exists_in_object_store"); | |
1334 if (!KeyPrefix::ValidIds(database_id, object_store_id)) | |
1335 return false; | |
1336 found = false; | |
1337 LevelDBTransaction* leveldb_transaction = | |
1338 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
1339 const std::vector<char> leveldb_key = | |
1340 ObjectStoreDataKey::Encode(database_id, object_store_id, key); | |
1341 std::vector<char> data; | |
1342 | |
1343 bool ok = leveldb_transaction->Get(LevelDBSlice(leveldb_key), data, found); | |
1344 if (!ok) { | |
1345 INTERNAL_READ_ERROR(kKeyExistsInObjectStore); | |
1346 return false; | |
1347 } | |
1348 if (!found) | |
1349 return true; | |
1350 | |
1351 int64_t version; | |
1352 if (DecodeVarInt(data.begin(), data.end(), version) == data.begin()) | |
1353 return false; | |
1354 | |
1355 found_record_identifier->Reset(EncodeIDBKey(key), version); | |
1356 return true; | |
1357 } | |
1358 | |
1359 static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it, | |
1360 const std::vector<char>& stop_key, | |
1361 int64_t index_id, | |
1362 unsigned char meta_data_type) { | |
1363 if (!it->IsValid() || CompareKeys(it->Key(), LevelDBSlice(stop_key)) >= 0) | |
1364 return false; | |
1365 | |
1366 IndexMetaDataKey meta_data_key; | |
1367 const char* p = IndexMetaDataKey::Decode( | |
1368 it->Key().begin(), it->Key().end(), &meta_data_key); | |
1369 DCHECK(p); | |
1370 if (meta_data_key.IndexId() != index_id) | |
1371 return false; | |
1372 if (meta_data_key.meta_data_type() != meta_data_type) | |
1373 return false; | |
1374 return true; | |
1375 } | |
1376 | |
1377 // TODO: This should do some error handling rather than plowing ahead when bad | |
1378 // data is encountered. | |
1379 bool IndexedDBBackingStore::GetIndexes( | |
1380 int64_t database_id, | |
1381 int64_t object_store_id, | |
1382 IndexedDBObjectStoreMetadata::IndexMap* indexes) { | |
1383 IDB_TRACE("IndexedDBBackingStore::get_indexes"); | |
1384 if (!KeyPrefix::ValidIds(database_id, object_store_id)) | |
1385 return false; | |
1386 const std::vector<char> start_key = | |
1387 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0); | |
1388 const std::vector<char> stop_key = | |
1389 IndexMetaDataKey::Encode(database_id, object_store_id + 1, 0, 0); | |
1390 | |
1391 DCHECK(indexes->empty()); | |
1392 | |
1393 scoped_ptr<LevelDBIterator> it = db_->CreateIterator(); | |
1394 it->Seek(LevelDBSlice(start_key)); | |
1395 while (it->IsValid() && | |
1396 CompareKeys(LevelDBSlice(it->Key()), LevelDBSlice(stop_key)) < 0) { | |
1397 const char* p = it->Key().begin(); | |
1398 const char* limit = it->Key().end(); | |
1399 | |
1400 IndexMetaDataKey meta_data_key; | |
1401 p = IndexMetaDataKey::Decode(p, limit, &meta_data_key); | |
1402 DCHECK(p); | |
1403 if (meta_data_key.meta_data_type() != IndexMetaDataKey::Name) { | |
1404 INTERNAL_CONSISTENCY_ERROR(kGetIndexes); | |
1405 // Possible stale metadata due to http://webkit.org/b/85557 but don't fail | |
1406 // the load. | |
1407 it->Next(); | |
1408 continue; | |
1409 } | |
1410 | |
1411 // TODO: Do this by direct key lookup rather than iteration, to simplify. | |
1412 int64_t index_id = meta_data_key.IndexId(); | |
1413 string16 index_name = DecodeString(it->Value().begin(), it->Value().end()); | |
1414 | |
1415 it->Next(); // unique flag | |
1416 if (!CheckIndexAndMetaDataKey( | |
1417 it.get(), stop_key, index_id, IndexMetaDataKey::Unique)) { | |
1418 INTERNAL_CONSISTENCY_ERROR(kGetIndexes); | |
1419 break; | |
1420 } | |
1421 bool index_unique = DecodeBool(it->Value().begin(), it->Value().end()); | |
1422 | |
1423 it->Next(); // key_path | |
1424 if (!CheckIndexAndMetaDataKey( | |
1425 it.get(), stop_key, index_id, IndexMetaDataKey::KeyPath)) { | |
1426 INTERNAL_CONSISTENCY_ERROR(kGetIndexes); | |
1427 break; | |
1428 } | |
1429 IndexedDBKeyPath key_path = | |
1430 DecodeIDBKeyPath(it->Value().begin(), it->Value().end()); | |
1431 | |
1432 it->Next(); // [optional] multi_entry flag | |
1433 bool index_multi_entry = false; | |
1434 if (CheckIndexAndMetaDataKey( | |
1435 it.get(), stop_key, index_id, IndexMetaDataKey::MultiEntry)) { | |
1436 index_multi_entry = DecodeBool(it->Value().begin(), it->Value().end()); | |
1437 it->Next(); | |
1438 } | |
1439 | |
1440 (*indexes)[index_id] = IndexedDBIndexMetadata( | |
1441 index_name, index_id, key_path, index_unique, index_multi_entry); | |
1442 } | |
1443 return true; | |
1444 } | |
1445 | |
1446 WARN_UNUSED_RESULT static bool SetMaxIndexId(LevelDBTransaction* transaction, | |
1447 int64_t database_id, | |
1448 int64_t object_store_id, | |
1449 int64_t index_id) { | |
1450 int64_t max_index_id = -1; | |
1451 const std::vector<char> max_index_id_key = ObjectStoreMetaDataKey::Encode( | |
1452 database_id, object_store_id, ObjectStoreMetaDataKey::MaxIndexId); | |
1453 bool found = false; | |
1454 bool ok = | |
1455 GetInt(transaction, LevelDBSlice(max_index_id_key), max_index_id, found); | |
1456 if (!ok) { | |
1457 INTERNAL_READ_ERROR(kSetMaxIndexId); | |
1458 return false; | |
1459 } | |
1460 if (!found) | |
1461 max_index_id = MinimumIndexId; | |
1462 | |
1463 if (index_id <= max_index_id) { | |
1464 INTERNAL_CONSISTENCY_ERROR(kSetMaxIndexId); | |
1465 return false; | |
1466 } | |
1467 | |
1468 PutInt(transaction, LevelDBSlice(max_index_id_key), index_id); | |
1469 return true; | |
1470 } | |
1471 | |
1472 bool IndexedDBBackingStore::CreateIndex( | |
1473 IndexedDBBackingStore::Transaction* transaction, | |
1474 int64_t database_id, | |
1475 int64_t object_store_id, | |
1476 int64_t index_id, | |
1477 const string16& name, | |
1478 const IndexedDBKeyPath& key_path, | |
1479 bool is_unique, | |
1480 bool is_multi_entry) { | |
1481 IDB_TRACE("IndexedDBBackingStore::create_index"); | |
1482 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id)) | |
1483 return false; | |
1484 LevelDBTransaction* leveldb_transaction = | |
1485 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
1486 if (!SetMaxIndexId( | |
1487 leveldb_transaction, database_id, object_store_id, index_id)) | |
1488 return false; | |
1489 | |
1490 const std::vector<char> name_key = IndexMetaDataKey::Encode( | |
1491 database_id, object_store_id, index_id, IndexMetaDataKey::Name); | |
1492 const std::vector<char> unique_key = IndexMetaDataKey::Encode( | |
1493 database_id, object_store_id, index_id, IndexMetaDataKey::Unique); | |
1494 const std::vector<char> key_path_key = IndexMetaDataKey::Encode( | |
1495 database_id, object_store_id, index_id, IndexMetaDataKey::KeyPath); | |
1496 const std::vector<char> multi_entry_key = IndexMetaDataKey::Encode( | |
1497 database_id, object_store_id, index_id, IndexMetaDataKey::MultiEntry); | |
1498 | |
1499 PutString(leveldb_transaction, LevelDBSlice(name_key), name); | |
1500 PutBool(leveldb_transaction, LevelDBSlice(unique_key), is_unique); | |
1501 PutIDBKeyPath(leveldb_transaction, LevelDBSlice(key_path_key), key_path); | |
1502 PutBool(leveldb_transaction, LevelDBSlice(multi_entry_key), is_multi_entry); | |
1503 return true; | |
1504 } | |
1505 | |
1506 bool IndexedDBBackingStore::DeleteIndex( | |
1507 IndexedDBBackingStore::Transaction* transaction, | |
1508 int64_t database_id, | |
1509 int64_t object_store_id, | |
1510 int64_t index_id) { | |
1511 IDB_TRACE("IndexedDBBackingStore::delete_index"); | |
1512 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id)) | |
1513 return false; | |
1514 LevelDBTransaction* leveldb_transaction = | |
1515 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
1516 | |
1517 const std::vector<char> index_meta_data_start = | |
1518 IndexMetaDataKey::Encode(database_id, object_store_id, index_id, 0); | |
1519 const std::vector<char> index_meta_data_end = | |
1520 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id, index_id); | |
1521 DeleteRange(leveldb_transaction, index_meta_data_start, index_meta_data_end); | |
1522 | |
1523 const std::vector<char> index_data_start = | |
1524 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id); | |
1525 const std::vector<char> index_data_end = | |
1526 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id); | |
1527 DeleteRange(leveldb_transaction, index_data_start, index_data_end); | |
1528 return true; | |
1529 } | |
1530 | |
1531 bool IndexedDBBackingStore::PutIndexDataForRecord( | |
1532 IndexedDBBackingStore::Transaction* transaction, | |
1533 int64_t database_id, | |
1534 int64_t object_store_id, | |
1535 int64_t index_id, | |
1536 const IndexedDBKey& key, | |
1537 const RecordIdentifier& record_identifier) { | |
1538 IDB_TRACE("IndexedDBBackingStore::put_index_data_for_record"); | |
1539 DCHECK(key.IsValid()); | |
1540 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id)) | |
1541 return false; | |
1542 | |
1543 LevelDBTransaction* leveldb_transaction = | |
1544 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
1545 const std::vector<char> index_data_key = | |
1546 IndexDataKey::Encode(database_id, | |
1547 object_store_id, | |
1548 index_id, | |
1549 EncodeIDBKey(key), | |
1550 record_identifier.primary_key()); | |
1551 | |
1552 std::vector<char> data(EncodeVarInt(record_identifier.version())); | |
1553 const std::vector<char>& primary_key = record_identifier.primary_key(); | |
1554 data.insert(data.end(), primary_key.begin(), primary_key.end()); | |
1555 | |
1556 leveldb_transaction->Put(LevelDBSlice(index_data_key), data); | |
1557 return true; | |
1558 } | |
1559 | |
1560 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction, | |
1561 const std::vector<char>& target, | |
1562 std::vector<char>& found_key) { | |
1563 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator(); | |
1564 it->Seek(LevelDBSlice(target)); | |
1565 | |
1566 if (!it->IsValid()) { | |
1567 it->SeekToLast(); | |
1568 if (!it->IsValid()) | |
1569 return false; | |
1570 } | |
1571 | |
1572 while (CompareIndexKeys(LevelDBSlice(it->Key()), LevelDBSlice(target)) > 0) { | |
1573 it->Prev(); | |
1574 if (!it->IsValid()) | |
1575 return false; | |
1576 } | |
1577 | |
1578 do { | |
1579 found_key.assign(it->Key().begin(), it->Key().end()); | |
1580 | |
1581 // There can be several index keys that compare equal. We want the last one. | |
1582 it->Next(); | |
1583 } while (it->IsValid() && !CompareIndexKeys(it->Key(), LevelDBSlice(target))); | |
1584 | |
1585 return true; | |
1586 } | |
1587 | |
1588 static bool VersionExists(LevelDBTransaction* transaction, | |
1589 int64_t database_id, | |
1590 int64_t object_store_id, | |
1591 int64_t version, | |
1592 const std::vector<char>& encoded_primary_key, | |
1593 bool& exists) { | |
1594 const std::vector<char> key = | |
1595 ExistsEntryKey::Encode(database_id, object_store_id, encoded_primary_key); | |
1596 std::vector<char> data; | |
1597 | |
1598 bool ok = transaction->Get(LevelDBSlice(key), data, exists); | |
1599 if (!ok) { | |
1600 INTERNAL_READ_ERROR(kVersionExists); | |
1601 return false; | |
1602 } | |
1603 if (!exists) | |
1604 return true; | |
1605 | |
1606 exists = (DecodeInt(data.begin(), data.end()) == version); | |
1607 return true; | |
1608 } | |
1609 | |
1610 bool IndexedDBBackingStore::FindKeyInIndex( | |
1611 IndexedDBBackingStore::Transaction* transaction, | |
1612 int64_t database_id, | |
1613 int64_t object_store_id, | |
1614 int64_t index_id, | |
1615 const IndexedDBKey& key, | |
1616 std::vector<char>& found_encoded_primary_key, | |
1617 bool& found) { | |
1618 IDB_TRACE("IndexedDBBackingStore::find_key_in_index"); | |
1619 DCHECK(KeyPrefix::ValidIds(database_id, object_store_id, index_id)); | |
1620 | |
1621 DCHECK(found_encoded_primary_key.empty()); | |
1622 found = false; | |
1623 | |
1624 LevelDBTransaction* leveldb_transaction = | |
1625 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
1626 const std::vector<char> leveldb_key = | |
1627 IndexDataKey::Encode(database_id, object_store_id, index_id, key); | |
1628 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator(); | |
1629 it->Seek(LevelDBSlice(leveldb_key)); | |
1630 | |
1631 for (;;) { | |
1632 if (!it->IsValid()) | |
1633 return true; | |
1634 if (CompareIndexKeys(it->Key(), LevelDBSlice(leveldb_key)) > 0) | |
1635 return true; | |
1636 | |
1637 int64_t version; | |
1638 const char* p = | |
1639 DecodeVarInt(it->Value().begin(), it->Value().end(), version); | |
1640 if (!p) { | |
1641 INTERNAL_READ_ERROR(kFindKeyInIndex); | |
1642 return false; | |
1643 } | |
1644 found_encoded_primary_key.insert( | |
1645 found_encoded_primary_key.end(), p, it->Value().end()); | |
1646 | |
1647 bool exists = false; | |
1648 bool ok = VersionExists(leveldb_transaction, | |
1649 database_id, | |
1650 object_store_id, | |
1651 version, | |
1652 found_encoded_primary_key, | |
1653 exists); | |
1654 if (!ok) | |
1655 return false; | |
1656 if (!exists) { | |
1657 // Delete stale index data entry and continue. | |
1658 leveldb_transaction->Remove(it->Key()); | |
1659 it->Next(); | |
1660 continue; | |
1661 } | |
1662 found = true; | |
1663 return true; | |
1664 } | |
1665 } | |
1666 | |
1667 bool IndexedDBBackingStore::GetPrimaryKeyViaIndex( | |
1668 IndexedDBBackingStore::Transaction* transaction, | |
1669 int64_t database_id, | |
1670 int64_t object_store_id, | |
1671 int64_t index_id, | |
1672 const IndexedDBKey& key, | |
1673 scoped_ptr<IndexedDBKey>* primary_key) { | |
1674 IDB_TRACE("IndexedDBBackingStore::get_primary_key_via_index"); | |
1675 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id)) | |
1676 return false; | |
1677 | |
1678 bool found = false; | |
1679 std::vector<char> found_encoded_primary_key; | |
1680 bool ok = FindKeyInIndex(transaction, | |
1681 database_id, | |
1682 object_store_id, | |
1683 index_id, | |
1684 key, | |
1685 found_encoded_primary_key, | |
1686 found); | |
1687 if (!ok) { | |
1688 INTERNAL_READ_ERROR(kGetPrimaryKeyViaIndex); | |
1689 return false; | |
1690 } | |
1691 if (found) { | |
1692 DecodeIDBKey(&*found_encoded_primary_key.begin(), | |
1693 &*found_encoded_primary_key.end(), | |
1694 primary_key); | |
1695 return true; | |
1696 } | |
1697 | |
1698 return true; | |
1699 } | |
1700 | |
1701 bool IndexedDBBackingStore::KeyExistsInIndex( | |
1702 IndexedDBBackingStore::Transaction* transaction, | |
1703 int64_t database_id, | |
1704 int64_t object_store_id, | |
1705 int64_t index_id, | |
1706 const IndexedDBKey& index_key, | |
1707 scoped_ptr<IndexedDBKey>* found_primary_key, | |
1708 bool& exists) { | |
1709 IDB_TRACE("IndexedDBBackingStore::key_exists_in_index"); | |
1710 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id)) | |
1711 return false; | |
1712 | |
1713 exists = false; | |
1714 std::vector<char> found_encoded_primary_key; | |
1715 bool ok = FindKeyInIndex(transaction, | |
1716 database_id, | |
1717 object_store_id, | |
1718 index_id, | |
1719 index_key, | |
1720 found_encoded_primary_key, | |
1721 exists); | |
1722 if (!ok) { | |
1723 INTERNAL_READ_ERROR(kKeyExistsInIndex); | |
1724 return false; | |
1725 } | |
1726 if (!exists) | |
1727 return true; | |
1728 | |
1729 DecodeIDBKey(&*found_encoded_primary_key.begin(), | |
1730 &*found_encoded_primary_key.end(), | |
1731 found_primary_key); | |
1732 return true; | |
1733 } | |
1734 | |
1735 IndexedDBBackingStore::Cursor::Cursor( | |
1736 const IndexedDBBackingStore::Cursor* other) | |
1737 : transaction_(other->transaction_), | |
1738 cursor_options_(other->cursor_options_), | |
1739 current_key_(new IndexedDBKey(*other->current_key_)) { | |
1740 if (other->iterator_) { | |
1741 iterator_ = transaction_->CreateIterator(); | |
1742 | |
1743 if (other->iterator_->IsValid()) { | |
1744 iterator_->Seek(other->iterator_->Key()); | |
1745 DCHECK(iterator_->IsValid()); | |
1746 } | |
1747 } | |
1748 } | |
1749 | |
1750 IndexedDBBackingStore::Cursor::Cursor(LevelDBTransaction* transaction, | |
1751 const CursorOptions& cursor_options) | |
1752 : transaction_(transaction), cursor_options_(cursor_options) {} | |
1753 IndexedDBBackingStore::Cursor::~Cursor() {} | |
1754 | |
1755 bool IndexedDBBackingStore::Cursor::FirstSeek() { | |
1756 iterator_ = transaction_->CreateIterator(); | |
1757 if (cursor_options_.forward) | |
1758 iterator_->Seek(LevelDBSlice(cursor_options_.low_key)); | |
1759 else | |
1760 iterator_->Seek(LevelDBSlice(cursor_options_.high_key)); | |
1761 | |
1762 return ContinueFunction(0, Ready); | |
1763 } | |
1764 | |
1765 bool IndexedDBBackingStore::Cursor::Advance(unsigned long count) { | |
1766 while (count--) { | |
1767 if (!ContinueFunction()) | |
1768 return false; | |
1769 } | |
1770 return true; | |
1771 } | |
1772 | |
1773 bool IndexedDBBackingStore::Cursor::ContinueFunction(const IndexedDBKey* key, | |
1774 IteratorState next_state) { | |
1775 // TODO(alecflett): avoid a copy here? | |
1776 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey(); | |
1777 | |
1778 bool first_iteration = true; | |
1779 | |
1780 // When iterating with PrevNoDuplicate, spec requires that the | |
1781 // value we yield for each key is the first duplicate in forwards | |
1782 // order. | |
1783 IndexedDBKey last_duplicate_key; | |
1784 | |
1785 bool forward = cursor_options_.forward; | |
1786 | |
1787 for (;;) { | |
1788 if (next_state == Seek) { | |
1789 // TODO: Optimize seeking for reverse cursors as well. | |
1790 if (first_iteration && key && key->IsValid() && forward) { | |
1791 iterator_->Seek(LevelDBSlice(EncodeKey(*key))); | |
1792 first_iteration = false; | |
1793 } else if (forward) { | |
1794 iterator_->Next(); | |
1795 } else { | |
1796 iterator_->Prev(); | |
1797 } | |
1798 } else { | |
1799 next_state = Seek; // for subsequent iterations | |
1800 } | |
1801 | |
1802 if (!iterator_->IsValid()) { | |
1803 if (!forward && last_duplicate_key.IsValid()) { | |
1804 // We need to walk forward because we hit the end of | |
1805 // the data. | |
1806 forward = true; | |
1807 continue; | |
1808 } | |
1809 | |
1810 return false; | |
1811 } | |
1812 | |
1813 if (IsPastBounds()) { | |
1814 if (!forward && last_duplicate_key.IsValid()) { | |
1815 // We need to walk forward because now we're beyond the | |
1816 // bounds defined by the cursor. | |
1817 forward = true; | |
1818 continue; | |
1819 } | |
1820 | |
1821 return false; | |
1822 } | |
1823 | |
1824 if (!HaveEnteredRange()) | |
1825 continue; | |
1826 | |
1827 // The row may not load because there's a stale entry in the | |
1828 // index. This is not fatal. | |
1829 if (!LoadCurrentRow()) | |
1830 continue; | |
1831 | |
1832 if (key && key->IsValid()) { | |
1833 if (forward) { | |
1834 if (current_key_->IsLessThan(*key)) | |
1835 continue; | |
1836 } else { | |
1837 if (key->IsLessThan(*current_key_)) | |
1838 continue; | |
1839 } | |
1840 } | |
1841 | |
1842 if (cursor_options_.unique) { | |
1843 | |
1844 if (previous_key.IsValid() && current_key_->IsEqual(previous_key)) { | |
1845 // We should never be able to walk forward all the way | |
1846 // to the previous key. | |
1847 DCHECK(!last_duplicate_key.IsValid()); | |
1848 continue; | |
1849 } | |
1850 | |
1851 if (!forward) { | |
1852 if (!last_duplicate_key.IsValid()) { | |
1853 last_duplicate_key = *current_key_; | |
1854 continue; | |
1855 } | |
1856 | |
1857 // We need to walk forward because we hit the boundary | |
1858 // between key ranges. | |
1859 if (!last_duplicate_key.IsEqual(*current_key_)) { | |
1860 forward = true; | |
1861 continue; | |
1862 } | |
1863 | |
1864 continue; | |
1865 } | |
1866 } | |
1867 break; | |
1868 } | |
1869 | |
1870 DCHECK(!last_duplicate_key.IsValid() || | |
1871 (forward && last_duplicate_key.IsEqual(*current_key_))); | |
1872 return true; | |
1873 } | |
1874 | |
1875 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const { | |
1876 if (cursor_options_.forward) { | |
1877 if (cursor_options_.low_open) | |
1878 return CompareIndexKeys(iterator_->Key(), | |
1879 LevelDBSlice(cursor_options_.low_key)) > | |
1880 0; | |
1881 | |
1882 return CompareIndexKeys(iterator_->Key(), | |
1883 LevelDBSlice(cursor_options_.low_key)) >= | |
1884 0; | |
1885 } | |
1886 if (cursor_options_.high_open) | |
1887 return CompareIndexKeys(iterator_->Key(), | |
1888 LevelDBSlice(cursor_options_.high_key)) < | |
1889 0; | |
1890 | |
1891 return CompareIndexKeys(iterator_->Key(), | |
1892 LevelDBSlice(cursor_options_.high_key)) <= | |
1893 0; | |
1894 } | |
1895 | |
1896 bool IndexedDBBackingStore::Cursor::IsPastBounds() const { | |
1897 if (cursor_options_.forward) { | |
1898 if (cursor_options_.high_open) | |
1899 return CompareIndexKeys(iterator_->Key(), | |
1900 LevelDBSlice(cursor_options_.high_key)) >= | |
1901 0; | |
jamesr
2013/05/21 23:56:06
odd indentation here. if this is supposed to be a
jsbell
2013/05/22 17:54:44
clang-format. Unfortunately, if you move the 0; to
jsbell
2013/05/23 21:10:49
clang-format is still wrapping before the 0 which
| |
1902 return CompareIndexKeys(iterator_->Key(), | |
1903 LevelDBSlice(cursor_options_.high_key)) > | |
1904 0; | |
1905 } | |
1906 | |
1907 if (cursor_options_.low_open) | |
1908 return CompareIndexKeys(iterator_->Key(), | |
1909 LevelDBSlice(cursor_options_.low_key)) <= | |
1910 0; | |
1911 return CompareIndexKeys(iterator_->Key(), | |
1912 LevelDBSlice(cursor_options_.low_key)) < | |
1913 0; | |
1914 } | |
1915 | |
1916 const IndexedDBKey& IndexedDBBackingStore::Cursor::primary_key() const { | |
1917 return *current_key_; | |
1918 } | |
1919 | |
1920 const IndexedDBBackingStore::RecordIdentifier& | |
1921 IndexedDBBackingStore::Cursor::record_identifier() const { | |
1922 return record_identifier_; | |
1923 } | |
1924 | |
1925 class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor { | |
1926 public: | |
1927 ObjectStoreKeyCursorImpl( | |
1928 LevelDBTransaction* transaction, | |
1929 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) | |
1930 : IndexedDBBackingStore::Cursor(transaction, cursor_options) {} | |
1931 | |
1932 virtual Cursor* Clone() OVERRIDE { | |
1933 return new ObjectStoreKeyCursorImpl(this); | |
1934 } | |
1935 | |
1936 // IndexedDBBackingStore::Cursor | |
1937 virtual std::vector<char>* Value() OVERRIDE { | |
1938 NOTREACHED(); | |
1939 return NULL; | |
1940 } | |
1941 virtual bool LoadCurrentRow() OVERRIDE; | |
1942 | |
1943 protected: | |
1944 virtual std::vector<char> EncodeKey(const IndexedDBKey& key) OVERRIDE { | |
1945 return ObjectStoreDataKey::Encode( | |
1946 cursor_options_.database_id, cursor_options_.object_store_id, key); | |
1947 } | |
1948 | |
1949 private: | |
1950 ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other) | |
1951 : IndexedDBBackingStore::Cursor(other) {} | |
1952 }; | |
1953 | |
1954 bool ObjectStoreKeyCursorImpl::LoadCurrentRow() { | |
1955 const char* key_position = iterator_->Key().begin(); | |
1956 const char* key_limit = iterator_->Key().end(); | |
1957 | |
1958 ObjectStoreDataKey object_store_data_key; | |
1959 key_position = ObjectStoreDataKey::Decode( | |
1960 key_position, key_limit, &object_store_data_key); | |
1961 if (!key_position) { | |
1962 INTERNAL_READ_ERROR(kLoadCurrentRow); | |
1963 return false; | |
1964 } | |
1965 | |
1966 current_key_ = object_store_data_key.user_key(); | |
1967 | |
1968 int64_t version; | |
1969 const char* value_position = DecodeVarInt( | |
1970 iterator_->Value().begin(), iterator_->Value().end(), version); | |
1971 if (!value_position) { | |
1972 INTERNAL_READ_ERROR(kLoadCurrentRow); | |
1973 return false; | |
1974 } | |
1975 | |
1976 // TODO: This re-encodes what was just decoded; try and optimize. | |
1977 record_identifier_.Reset(EncodeIDBKey(*current_key_), version); | |
1978 | |
1979 return true; | |
1980 } | |
1981 | |
1982 class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor { | |
1983 public: | |
1984 ObjectStoreCursorImpl( | |
1985 LevelDBTransaction* transaction, | |
1986 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) | |
1987 : IndexedDBBackingStore::Cursor(transaction, cursor_options) {} | |
1988 | |
1989 virtual Cursor* Clone() OVERRIDE { return new ObjectStoreCursorImpl(this); } | |
1990 | |
1991 // IndexedDBBackingStore::Cursor | |
1992 virtual std::vector<char>* Value() OVERRIDE { return ¤t_value_; } | |
1993 virtual bool LoadCurrentRow() OVERRIDE; | |
1994 | |
1995 protected: | |
1996 virtual std::vector<char> EncodeKey(const IndexedDBKey& key) OVERRIDE { | |
1997 return ObjectStoreDataKey::Encode( | |
1998 cursor_options_.database_id, cursor_options_.object_store_id, key); | |
1999 } | |
2000 | |
2001 private: | |
2002 ObjectStoreCursorImpl(const ObjectStoreCursorImpl* other) | |
2003 : IndexedDBBackingStore::Cursor(other), | |
2004 current_value_(other->current_value_) {} | |
2005 | |
2006 std::vector<char> current_value_; | |
2007 }; | |
2008 | |
2009 bool ObjectStoreCursorImpl::LoadCurrentRow() { | |
2010 const char* key_position = iterator_->Key().begin(); | |
2011 const char* key_limit = iterator_->Key().end(); | |
2012 | |
2013 ObjectStoreDataKey object_store_data_key; | |
2014 key_position = ObjectStoreDataKey::Decode( | |
2015 key_position, key_limit, &object_store_data_key); | |
2016 if (!key_position) { | |
2017 INTERNAL_READ_ERROR(kLoadCurrentRow); | |
2018 return false; | |
2019 } | |
2020 | |
2021 current_key_ = object_store_data_key.user_key(); | |
2022 | |
2023 int64_t version; | |
2024 const char* value_position = DecodeVarInt( | |
2025 iterator_->Value().begin(), iterator_->Value().end(), version); | |
2026 if (!value_position) { | |
2027 INTERNAL_READ_ERROR(kLoadCurrentRow); | |
2028 return false; | |
2029 } | |
2030 | |
2031 // TODO: This re-encodes what was just decoded; try and optimize. | |
2032 record_identifier_.Reset(EncodeIDBKey(*current_key_), version); | |
2033 | |
2034 std::vector<char> value; | |
2035 value.insert(value.end(), value_position, iterator_->Value().end()); | |
2036 current_value_.swap(value); | |
2037 return true; | |
2038 } | |
2039 | |
2040 class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor { | |
2041 public: | |
2042 IndexKeyCursorImpl( | |
2043 LevelDBTransaction* transaction, | |
2044 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) | |
2045 : IndexedDBBackingStore::Cursor(transaction, cursor_options) {} | |
2046 | |
2047 virtual Cursor* Clone() OVERRIDE { return new IndexKeyCursorImpl(this); } | |
2048 | |
2049 // IndexedDBBackingStore::Cursor | |
2050 virtual std::vector<char>* Value() OVERRIDE { | |
2051 NOTREACHED(); | |
2052 return NULL; | |
2053 } | |
2054 virtual const IndexedDBKey& primary_key() const OVERRIDE { | |
2055 return *primary_key_; | |
2056 } | |
2057 virtual const IndexedDBBackingStore::RecordIdentifier& | |
2058 RecordIdentifier() const { | |
2059 NOTREACHED(); | |
2060 return record_identifier_; | |
2061 } | |
2062 virtual bool LoadCurrentRow() OVERRIDE; | |
2063 | |
2064 protected: | |
2065 virtual std::vector<char> EncodeKey(const IndexedDBKey& key) OVERRIDE { | |
2066 return IndexDataKey::Encode(cursor_options_.database_id, | |
2067 cursor_options_.object_store_id, | |
2068 cursor_options_.index_id, | |
2069 key); | |
2070 } | |
2071 | |
2072 private: | |
2073 explicit IndexKeyCursorImpl(const IndexKeyCursorImpl* other) | |
2074 : IndexedDBBackingStore::Cursor(other), | |
2075 primary_key_(other->primary_key_.get()) {} | |
2076 | |
2077 scoped_ptr<IndexedDBKey> primary_key_; | |
2078 }; | |
2079 | |
2080 bool IndexKeyCursorImpl::LoadCurrentRow() { | |
2081 const char* key_position = iterator_->Key().begin(); | |
2082 const char* key_limit = iterator_->Key().end(); | |
2083 | |
2084 IndexDataKey index_data_key; | |
2085 key_position = IndexDataKey::Decode(key_position, key_limit, &index_data_key); | |
2086 | |
2087 current_key_ = index_data_key.user_key(); | |
2088 DCHECK(current_key_); | |
2089 | |
2090 int64_t index_data_version; | |
2091 const char* value_position = DecodeVarInt( | |
2092 iterator_->Value().begin(), iterator_->Value().end(), index_data_version); | |
2093 if (!value_position) { | |
2094 INTERNAL_READ_ERROR(kLoadCurrentRow); | |
2095 return false; | |
2096 } | |
2097 | |
2098 value_position = | |
2099 DecodeIDBKey(value_position, iterator_->Value().end(), &primary_key_); | |
2100 if (!value_position) { | |
2101 INTERNAL_READ_ERROR(kLoadCurrentRow); | |
2102 return false; | |
2103 } | |
2104 | |
2105 std::vector<char> primary_leveldb_key = | |
2106 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(), | |
2107 index_data_key.ObjectStoreId(), | |
2108 *primary_key_); | |
2109 | |
2110 std::vector<char> result; | |
2111 bool found = false; | |
2112 bool ok = transaction_->Get(LevelDBSlice(primary_leveldb_key), result, found); | |
2113 if (!ok) { | |
2114 INTERNAL_READ_ERROR(kLoadCurrentRow); | |
2115 return false; | |
2116 } | |
2117 if (!found) { | |
2118 transaction_->Remove(iterator_->Key()); | |
2119 return false; | |
2120 } | |
2121 | |
2122 int64_t object_store_data_version; | |
2123 const char* t = | |
2124 DecodeVarInt(&*result.begin(), &*result.end(), object_store_data_version); | |
2125 if (!t) { | |
2126 INTERNAL_READ_ERROR(kLoadCurrentRow); | |
2127 return false; | |
2128 } | |
2129 | |
2130 if (object_store_data_version != index_data_version) { | |
2131 transaction_->Remove(iterator_->Key()); | |
2132 return false; | |
2133 } | |
2134 | |
2135 return true; | |
2136 } | |
2137 | |
2138 class IndexCursorImpl : public IndexedDBBackingStore::Cursor { | |
2139 public: | |
2140 IndexCursorImpl( | |
2141 LevelDBTransaction* transaction, | |
2142 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) | |
2143 : IndexedDBBackingStore::Cursor(transaction, cursor_options) {} | |
2144 | |
2145 virtual Cursor* Clone() OVERRIDE { return new IndexCursorImpl(this); } | |
2146 | |
2147 // IndexedDBBackingStore::Cursor | |
2148 virtual std::vector<char>* Value() OVERRIDE { return ¤t_value_; } | |
2149 virtual const IndexedDBKey& primary_key() const OVERRIDE { | |
2150 return *primary_key_; | |
2151 } | |
2152 virtual const IndexedDBBackingStore::RecordIdentifier& | |
2153 RecordIdentifier() const { | |
2154 NOTREACHED(); | |
2155 return record_identifier_; | |
2156 } | |
2157 virtual bool LoadCurrentRow() OVERRIDE; | |
2158 | |
2159 protected: | |
2160 virtual std::vector<char> EncodeKey(const IndexedDBKey& key) OVERRIDE { | |
2161 return IndexDataKey::Encode(cursor_options_.database_id, | |
2162 cursor_options_.object_store_id, | |
2163 cursor_options_.index_id, | |
2164 key); | |
2165 } | |
2166 | |
2167 private: | |
2168 explicit IndexCursorImpl(const IndexCursorImpl* other) | |
2169 : IndexedDBBackingStore::Cursor(other), | |
2170 primary_key_(other->primary_key_.get()), | |
2171 current_value_(other->current_value_), | |
2172 primary_leveldb_key_(other->primary_leveldb_key_) {} | |
2173 | |
2174 scoped_ptr<IndexedDBKey> primary_key_; | |
2175 std::vector<char> current_value_; | |
2176 std::vector<char> primary_leveldb_key_; | |
2177 }; | |
2178 | |
2179 bool IndexCursorImpl::LoadCurrentRow() { | |
2180 const char* key_position = iterator_->Key().begin(); | |
2181 const char* key_limit = iterator_->Key().end(); | |
2182 | |
2183 IndexDataKey index_data_key; | |
2184 key_position = IndexDataKey::Decode(key_position, key_limit, &index_data_key); | |
2185 | |
2186 current_key_ = index_data_key.user_key(); | |
2187 DCHECK(current_key_); | |
2188 | |
2189 const char* value_position = iterator_->Value().begin(); | |
2190 const char* value_limit = iterator_->Value().end(); | |
2191 | |
2192 int64_t index_data_version; | |
2193 value_position = | |
2194 DecodeVarInt(value_position, value_limit, index_data_version); | |
2195 if (!value_position) { | |
2196 INTERNAL_READ_ERROR(kLoadCurrentRow); | |
2197 return false; | |
2198 } | |
2199 value_position = DecodeIDBKey(value_position, value_limit, &primary_key_); | |
2200 if (!value_position) { | |
2201 INTERNAL_READ_ERROR(kLoadCurrentRow); | |
2202 return false; | |
2203 } | |
2204 | |
2205 primary_leveldb_key_ = | |
2206 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(), | |
2207 index_data_key.ObjectStoreId(), | |
2208 *primary_key_); | |
2209 | |
2210 std::vector<char> result; | |
2211 bool found = false; | |
2212 bool ok = | |
2213 transaction_->Get(LevelDBSlice(primary_leveldb_key_), result, found); | |
2214 if (!ok) { | |
2215 INTERNAL_READ_ERROR(kLoadCurrentRow); | |
2216 return false; | |
2217 } | |
2218 if (!found) { | |
2219 transaction_->Remove(iterator_->Key()); | |
2220 return false; | |
2221 } | |
2222 | |
2223 int64_t object_store_data_version; | |
2224 value_position = | |
2225 DecodeVarInt(&*result.begin(), &*result.end(), object_store_data_version); | |
2226 if (!value_position) { | |
2227 INTERNAL_READ_ERROR(kLoadCurrentRow); | |
2228 return false; | |
2229 } | |
2230 | |
2231 if (object_store_data_version != index_data_version) { | |
2232 transaction_->Remove(iterator_->Key()); | |
2233 return false; | |
2234 } | |
2235 | |
2236 // TODO: Make value_position an iterator. | |
2237 std::vector<char> value(std::vector<char>::const_iterator(value_position), | |
2238 std::vector<char>::const_iterator(result.end())); | |
2239 current_value_.swap(value); | |
2240 return true; | |
2241 } | |
2242 | |
2243 bool ObjectStoreCursorOptions( | |
2244 LevelDBTransaction* transaction, | |
2245 int64_t database_id, | |
2246 int64_t object_store_id, | |
2247 const IndexedDBKeyRange& range, | |
2248 IndexedDB::CursorDirection direction, | |
2249 IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) { | |
2250 cursor_options.database_id = database_id; | |
2251 cursor_options.object_store_id = object_store_id; | |
2252 | |
2253 bool lower_bound = range.lower().IsValid(); | |
2254 bool upper_bound = range.upper().IsValid(); | |
2255 cursor_options.forward = (direction == IndexedDB::CursorNextNoDuplicate || | |
2256 direction == IndexedDB::CursorNext); | |
2257 cursor_options.unique = (direction == IndexedDB::CursorNextNoDuplicate || | |
2258 direction == IndexedDB::CursorPrevNoDuplicate); | |
2259 | |
2260 if (!lower_bound) { | |
2261 cursor_options.low_key = | |
2262 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey()); | |
2263 cursor_options.low_open = true; // Not included. | |
2264 } else { | |
2265 cursor_options.low_key = | |
2266 ObjectStoreDataKey::Encode(database_id, object_store_id, range.lower()); | |
2267 cursor_options.low_open = range.lowerOpen(); | |
2268 } | |
2269 | |
2270 if (!upper_bound) { | |
2271 cursor_options.high_key = | |
2272 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey()); | |
2273 | |
2274 if (cursor_options.forward) { | |
2275 cursor_options.high_open = true; // Not included. | |
2276 } else { | |
2277 // We need a key that exists. | |
2278 if (!FindGreatestKeyLessThanOrEqual( | |
2279 transaction, cursor_options.high_key, cursor_options.high_key)) | |
2280 return false; | |
2281 cursor_options.high_open = false; | |
2282 } | |
2283 } else { | |
2284 cursor_options.high_key = | |
2285 ObjectStoreDataKey::Encode(database_id, object_store_id, range.upper()); | |
2286 cursor_options.high_open = range.upperOpen(); | |
2287 | |
2288 if (!cursor_options.forward) { | |
2289 // For reverse cursors, we need a key that exists. | |
2290 std::vector<char> found_high_key; | |
2291 if (!FindGreatestKeyLessThanOrEqual( | |
2292 transaction, cursor_options.high_key, found_high_key)) | |
2293 return false; | |
2294 | |
2295 // If the target key should not be included, but we end up with a smaller | |
2296 // key, we should include that. | |
2297 if (cursor_options.high_open && | |
2298 CompareIndexKeys(LevelDBSlice(found_high_key), | |
2299 LevelDBSlice(cursor_options.high_key)) < | |
2300 0) | |
2301 cursor_options.high_open = false; | |
2302 | |
2303 cursor_options.high_key = found_high_key; | |
2304 } | |
2305 } | |
2306 | |
2307 return true; | |
2308 } | |
2309 | |
2310 bool IndexCursorOptions( | |
2311 LevelDBTransaction* transaction, | |
2312 int64_t database_id, | |
2313 int64_t object_store_id, | |
2314 int64_t index_id, | |
2315 const IndexedDBKeyRange& range, | |
2316 IndexedDB::CursorDirection direction, | |
2317 IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) { | |
2318 DCHECK(transaction); | |
2319 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id)) | |
2320 return false; | |
2321 | |
2322 cursor_options.database_id = database_id; | |
2323 cursor_options.object_store_id = object_store_id; | |
2324 cursor_options.index_id = index_id; | |
2325 | |
2326 bool lower_bound = range.lower().IsValid(); | |
2327 bool upper_bound = range.upper().IsValid(); | |
2328 cursor_options.forward = (direction == IndexedDB::CursorNextNoDuplicate || | |
2329 direction == IndexedDB::CursorNext); | |
2330 cursor_options.unique = (direction == IndexedDB::CursorNextNoDuplicate || | |
2331 direction == IndexedDB::CursorPrevNoDuplicate); | |
2332 | |
2333 if (!lower_bound) { | |
2334 cursor_options.low_key = | |
2335 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id); | |
2336 cursor_options.low_open = false; // Included. | |
2337 } else { | |
2338 cursor_options.low_key = IndexDataKey::Encode( | |
2339 database_id, object_store_id, index_id, range.lower()); | |
2340 cursor_options.low_open = range.lowerOpen(); | |
2341 } | |
2342 | |
2343 if (!upper_bound) { | |
2344 cursor_options.high_key = | |
2345 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id); | |
2346 cursor_options.high_open = false; // Included. | |
2347 | |
2348 if (!cursor_options.forward) { // We need a key that exists. | |
2349 if (!FindGreatestKeyLessThanOrEqual( | |
2350 transaction, cursor_options.high_key, cursor_options.high_key)) | |
2351 return false; | |
2352 cursor_options.high_open = false; | |
2353 } | |
2354 } else { | |
2355 cursor_options.high_key = IndexDataKey::Encode( | |
2356 database_id, object_store_id, index_id, range.upper()); | |
2357 cursor_options.high_open = range.upperOpen(); | |
2358 | |
2359 std::vector<char> found_high_key; | |
2360 if (!FindGreatestKeyLessThanOrEqual( | |
2361 transaction, | |
2362 cursor_options.high_key, | |
2363 found_high_key)) // Seek to the *last* key in the set of non-unique | |
2364 // keys. | |
2365 return false; | |
2366 | |
2367 // If the target key should not be included, but we end up with a smaller | |
2368 // key, we should include that. | |
2369 if (cursor_options.high_open && | |
2370 CompareIndexKeys(LevelDBSlice(found_high_key), | |
2371 LevelDBSlice(cursor_options.high_key)) < | |
2372 0) | |
2373 cursor_options.high_open = false; | |
2374 | |
2375 cursor_options.high_key = found_high_key; | |
2376 } | |
2377 | |
2378 return true; | |
2379 } | |
2380 | |
2381 scoped_ptr<IndexedDBBackingStore::Cursor> | |
2382 IndexedDBBackingStore::OpenObjectStoreCursor( | |
2383 IndexedDBBackingStore::Transaction* transaction, | |
2384 int64_t database_id, | |
2385 int64_t object_store_id, | |
2386 const IndexedDBKeyRange& range, | |
2387 IndexedDB::CursorDirection direction) { | |
2388 IDB_TRACE("IndexedDBBackingStore::open_object_store_cursor"); | |
2389 LevelDBTransaction* leveldb_transaction = | |
2390 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
2391 IndexedDBBackingStore::Cursor::CursorOptions cursor_options; | |
2392 if (!ObjectStoreCursorOptions(leveldb_transaction, | |
2393 database_id, | |
2394 object_store_id, | |
2395 range, | |
2396 direction, | |
2397 cursor_options)) | |
2398 return scoped_ptr<IndexedDBBackingStore::Cursor>(); | |
2399 scoped_ptr<ObjectStoreCursorImpl> cursor( | |
2400 new ObjectStoreCursorImpl(leveldb_transaction, cursor_options)); | |
2401 if (!cursor->FirstSeek()) | |
2402 return scoped_ptr<IndexedDBBackingStore::Cursor>(); | |
2403 | |
2404 return cursor.PassAs<IndexedDBBackingStore::Cursor>(); | |
2405 } | |
2406 | |
2407 scoped_ptr<IndexedDBBackingStore::Cursor> | |
2408 IndexedDBBackingStore::OpenObjectStoreKeyCursor( | |
2409 IndexedDBBackingStore::Transaction* transaction, | |
2410 int64_t database_id, | |
2411 int64_t object_store_id, | |
2412 const IndexedDBKeyRange& range, | |
2413 IndexedDB::CursorDirection direction) { | |
2414 IDB_TRACE("IndexedDBBackingStore::open_object_store_key_cursor"); | |
2415 LevelDBTransaction* leveldb_transaction = | |
2416 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
2417 IndexedDBBackingStore::Cursor::CursorOptions cursor_options; | |
2418 if (!ObjectStoreCursorOptions(leveldb_transaction, | |
2419 database_id, | |
2420 object_store_id, | |
2421 range, | |
2422 direction, | |
2423 cursor_options)) | |
2424 return scoped_ptr<IndexedDBBackingStore::Cursor>(); | |
2425 scoped_ptr<ObjectStoreKeyCursorImpl> cursor( | |
2426 new ObjectStoreKeyCursorImpl(leveldb_transaction, cursor_options)); | |
2427 if (!cursor->FirstSeek()) | |
2428 return scoped_ptr<IndexedDBBackingStore::Cursor>(); | |
2429 | |
2430 return cursor.PassAs<IndexedDBBackingStore::Cursor>(); | |
2431 } | |
2432 | |
2433 scoped_ptr<IndexedDBBackingStore::Cursor> | |
2434 IndexedDBBackingStore::OpenIndexKeyCursor( | |
2435 IndexedDBBackingStore::Transaction* transaction, | |
2436 int64_t database_id, | |
2437 int64_t object_store_id, | |
2438 int64_t index_id, | |
2439 const IndexedDBKeyRange& range, | |
2440 IndexedDB::CursorDirection direction) { | |
2441 IDB_TRACE("IndexedDBBackingStore::open_index_key_cursor"); | |
2442 LevelDBTransaction* leveldb_transaction = | |
2443 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
2444 IndexedDBBackingStore::Cursor::CursorOptions cursor_options; | |
2445 if (!IndexCursorOptions(leveldb_transaction, | |
2446 database_id, | |
2447 object_store_id, | |
2448 index_id, | |
2449 range, | |
2450 direction, | |
2451 cursor_options)) | |
2452 return scoped_ptr<IndexedDBBackingStore::Cursor>(); | |
2453 scoped_ptr<IndexKeyCursorImpl> cursor( | |
2454 new IndexKeyCursorImpl(leveldb_transaction, cursor_options)); | |
2455 if (!cursor->FirstSeek()) | |
2456 return scoped_ptr<IndexedDBBackingStore::Cursor>(); | |
2457 | |
2458 return cursor.PassAs<IndexedDBBackingStore::Cursor>(); | |
2459 } | |
2460 | |
2461 scoped_ptr<IndexedDBBackingStore::Cursor> | |
2462 IndexedDBBackingStore::OpenIndexCursor( | |
2463 IndexedDBBackingStore::Transaction* transaction, | |
2464 int64_t database_id, | |
2465 int64_t object_store_id, | |
2466 int64_t index_id, | |
2467 const IndexedDBKeyRange& range, | |
2468 IndexedDB::CursorDirection direction) { | |
2469 IDB_TRACE("IndexedDBBackingStore::open_index_cursor"); | |
2470 LevelDBTransaction* leveldb_transaction = | |
2471 IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); | |
2472 IndexedDBBackingStore::Cursor::CursorOptions cursor_options; | |
2473 if (!IndexCursorOptions(leveldb_transaction, | |
2474 database_id, | |
2475 object_store_id, | |
2476 index_id, | |
2477 range, | |
2478 direction, | |
2479 cursor_options)) | |
2480 return scoped_ptr<IndexedDBBackingStore::Cursor>(); | |
2481 scoped_ptr<IndexCursorImpl> cursor( | |
2482 new IndexCursorImpl(leveldb_transaction, cursor_options)); | |
2483 if (!cursor->FirstSeek()) | |
2484 return scoped_ptr<IndexedDBBackingStore::Cursor>(); | |
2485 | |
2486 return cursor.PassAs<IndexedDBBackingStore::Cursor>(); | |
2487 } | |
2488 | |
2489 IndexedDBBackingStore::Transaction::Transaction( | |
2490 IndexedDBBackingStore* backing_store) | |
2491 : backing_store_(backing_store) {} | |
2492 | |
2493 IndexedDBBackingStore::Transaction::~Transaction() {} | |
2494 | |
2495 void IndexedDBBackingStore::Transaction::begin() { | |
2496 IDB_TRACE("IndexedDBBackingStore::Transaction::begin"); | |
2497 DCHECK(!transaction_); | |
2498 transaction_ = LevelDBTransaction::Create(backing_store_->db_.get()); | |
2499 } | |
2500 | |
2501 bool IndexedDBBackingStore::Transaction::Commit() { | |
2502 IDB_TRACE("IndexedDBBackingStore::Transaction::commit"); | |
2503 DCHECK(transaction_); | |
2504 bool result = transaction_->Commit(); | |
2505 transaction_ = NULL; | |
2506 if (!result) | |
2507 INTERNAL_WRITE_ERROR(kTransactionCommit); | |
2508 return result; | |
2509 } | |
2510 | |
2511 void IndexedDBBackingStore::Transaction::Rollback() { | |
2512 IDB_TRACE("IndexedDBBackingStore::Transaction::rollback"); | |
2513 DCHECK(transaction_); | |
2514 transaction_->Rollback(); | |
2515 transaction_ = NULL; | |
2516 } | |
2517 | |
2518 } // namespace content | |
OLD | NEW |